This method is no-op for other non-Glia triggered results.
+ * @deprecated This method is no longer required, as all the required permissions are now managed internally.
*/
+ @Deprecated(forRemoval = true)
public static void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
Logger.d(TAG, "onRequestPermissionsResult");
Dependencies.glia().onRequestPermissionsResult(requestCode, permissions, grantResults);
@@ -185,7 +133,9 @@ public static void onRequestPermissionsResult(int requestCode, String[] permissi
* Your activity in turn must call this method to pass the results of the request to Glia SDK.
*
*
This method is no-op for other non-Glia triggered results.
+ * @deprecated This method is no longer required, as required activity results are now managed internally.
*/
+ @Deprecated(forRemoval = true)
public static void onActivityResult(int requestCode, int resultCode, Intent data) {
Logger.d(TAG, "onActivityResult");
Dependencies.getRepositoryFactory().getEngagementRepository().onActivityResult(requestCode, resultCode, data);
@@ -210,53 +160,6 @@ public static void endEngagement() {
Dependencies.getUseCaseFactory().getEndEngagementUseCase().invoke(true);
}
- /**
- * Updates the visitor's information
- *
- * Updates the visitor's information stored on the server. This information will also be displayed to the operator.
- *
- * @deprecated since 1.9.0 use @see {@link com.glia.androidsdk.Glia#updateVisitorInfo(VisitorInfoUpdateRequest, Consumer)}
- */
- @Deprecated
- public static void updateVisitorInfo(VisitorInfoUpdate visitorInfoUpdate, Consumer exceptionConsumer) {
- Logger.logDeprecatedMethodUse(TAG, "updateVisitorInfo(VisitorInfoUpdate, Consumer)");
- Dependencies.glia().updateVisitorInfo(new VisitorInfoUpdateRequest.Builder()
- .setName(visitorInfoUpdate.getName())
- .setEmail(visitorInfoUpdate.getEmail())
- .setPhone(visitorInfoUpdate.getPhone())
- .setNote(visitorInfoUpdate.getNote())
- .setCustomAttributes(visitorInfoUpdate.getCustomAttributes())
- .setCustomAttrsUpdateMethod(visitorInfoUpdate.getCustomAttrsUpdateMethod())
- .setNoteUpdateMethod(visitorInfoUpdate.getNoteUpdateMethod())
- .build(), e -> {
- if (e != null) {
- exceptionConsumer.accept(new GliaWidgetException(e.debugMessage, e.cause));
- } else {
- exceptionConsumer.accept(null);
- }
- });
- }
-
- /**
- * Fetches the visitor's information
- *
- * If a visitor is authenticated, the response will include the attributes and tokens fetched from the authentication provider.
- *
- * @deprecated since 1.9.0 use @see {@link com.glia.androidsdk.Glia#getVisitorInfo(RequestCallback)}
- */
- @Deprecated
- public static void getVisitorInfo(Consumer visitorCallback, Consumer exceptionConsumer) {
- Logger.logDeprecatedMethodUse(TAG, "getVisitorInfo(Consumer, Consumer)");
- Dependencies.glia().getVisitorInfo((visitorInfo, e) -> {
- if (visitorInfo != null) {
- visitorCallback.accept(new GliaVisitorInfo(visitorInfo));
- }
- if (e != null) {
- exceptionConsumer.accept(new GliaWidgetException(e.debugMessage, e.cause));
- }
- });
- }
-
/**
* @return current instance of {@link CustomCardAdapter}
*/
diff --git a/widgetssdk/src/main/java/com/glia/widgets/GliaWidgetsConfig.kt b/widgetssdk/src/main/java/com/glia/widgets/GliaWidgetsConfig.kt
index ed7eb8bc2..6c2b7229e 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/GliaWidgetsConfig.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/GliaWidgetsConfig.kt
@@ -43,9 +43,6 @@ class GliaWidgetsConfig private constructor(builder: Builder) {
@JvmField
val enableBubbleInsideApp: Boolean?
- @JvmField
- val uiTheme: UiTheme?
-
@JvmField
val manualLocaleOverride: String?
@@ -58,10 +55,9 @@ class GliaWidgetsConfig private constructor(builder: Builder) {
requestCode = builder.requestCode
uiJsonRemoteConfig = builder.uiJsonRemoteConfig
companyName = builder.companyName
- screenSharingMode = builder.screenSharingMode ?: DEFAULT_SCREEN_SHARING_MODE
+ screenSharingMode = builder.screenSharingMode
enableBubbleOutsideApp = builder.enableBubbleOutsideApp
enableBubbleInsideApp = builder.enableBubbleInsideApp
- uiTheme = builder.uiTheme
manualLocaleOverride = builder.manualLocaleOverride
}
@@ -134,8 +130,6 @@ class GliaWidgetsConfig private constructor(builder: Builder) {
private set
var enableBubbleInsideApp: Boolean? = null
private set
- var uiTheme: UiTheme? = null
- private set
var manualLocaleOverride: String? = null
private set
@@ -244,19 +238,6 @@ class GliaWidgetsConfig private constructor(builder: Builder) {
return this
}
- /**
- * @param uiTheme - uiTheme resource for UI configuration
- * @return Builder instance
- */
- @Deprecated(
- "While UiTheme can still be used for UI customization, we strongly encourage adopting remote configurations(GliaWidgetsConfig.Builder.setUiJsonRemoteConfig). " +
- "The remote configurations approach is more versatile and better suited for future development."
- )
- fun setUiTheme(uiTheme: UiTheme?): Builder {
- this.uiTheme = uiTheme
- return this
- }
-
/**
* @param manualLocaleOverride - manual locale override if you wish not to use the default locale
* @return Builder instance
@@ -275,8 +256,4 @@ class GliaWidgetsConfig private constructor(builder: Builder) {
return GliaWidgetsConfig(this)
}
}
-
- internal companion object {
- val DEFAULT_SCREEN_SHARING_MODE = ScreenSharing.Mode.APP_BOUNDED
- }
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/UiTheme.kt b/widgetssdk/src/main/java/com/glia/widgets/UiTheme.kt
index a7d8525b0..60f97a599 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/UiTheme.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/UiTheme.kt
@@ -29,11 +29,7 @@ import kotlinx.parcelize.Parcelize
"While this class can still be used for UI customization, we strongly encourage adopting remote configurations(GliaWidgetsConfig.Builder.setUiJsonRemoteConfig). " +
"The remote configurations approach is more versatile and better suited for future development."
)
-data class UiTheme(
- /**
- * Text to be shown on the top of the app bar in the chat
- */
- val appBarTitle: String? = null,
+internal data class UiTheme(
/**
* Primary color for your brand. Used for example to set the color of the appbar
@@ -372,7 +368,6 @@ data class UiTheme(
) : Parcelable, Mergeable {
private constructor(builder: UiThemeBuilder) : this(
- appBarTitle = builder.appBarTitle,
brandPrimaryColor = builder.brandPrimaryColor,
baseLightColor = builder.baseLightColor,
baseDarkColor = builder.baseDarkColor,
@@ -439,7 +434,6 @@ data class UiTheme(
)
override fun merge(other: UiTheme): UiTheme = UiTheme(
- appBarTitle = appBarTitle merge other.appBarTitle,
brandPrimaryColor = brandPrimaryColor merge other.brandPrimaryColor,
baseLightColor = baseLightColor merge other.baseLightColor,
baseDarkColor = baseDarkColor merge other.baseDarkColor,
@@ -506,14 +500,14 @@ data class UiTheme(
private fun toColorPallet(context: Context): ColorPallet = context.run {
ColorPallet(
- baseDarkColorTheme = ColorTheme(context.getColor(baseDarkColor ?: R.color.glia_base_dark_color)),
- baseLightColorTheme = ColorTheme(context.getColor(baseLightColor ?: R.color.glia_base_light_color)),
- baseNeutralColorTheme = ColorTheme(context.getColor(R.color.glia_system_agent_bubble_color)),
- baseNormalColorTheme = ColorTheme(context.getColor(baseNormalColor ?: R.color.glia_base_normal_color)),
- baseShadeColorTheme = ColorTheme(context.getColor(baseShadeColor ?: R.color.glia_base_shade_color)),
- primaryColorTheme = ColorTheme(context.getColor(brandPrimaryColor ?: R.color.glia_brand_primary_color)),
+ darkColorTheme = ColorTheme(context.getColor(baseDarkColor ?: R.color.glia_dark_color)),
+ lightColorTheme = ColorTheme(context.getColor(baseLightColor ?: R.color.glia_light_color)),
+ neutralColorTheme = ColorTheme(context.getColor(R.color.glia_neutral_color)),
+ normalColorTheme = ColorTheme(context.getColor(baseNormalColor ?: R.color.glia_normal_color)),
+ shadeColorTheme = ColorTheme(context.getColor(baseShadeColor ?: R.color.glia_shade_color)),
+ primaryColorTheme = ColorTheme(context.getColor(brandPrimaryColor ?: R.color.glia_primary_color)),
secondaryColorTheme = null,
- systemNegativeColorTheme = ColorTheme(context.getColor(systemNegativeColor ?: R.color.glia_system_negative_color))
+ negativeColorTheme = ColorTheme(context.getColor(systemNegativeColor ?: R.color.glia_negative_color))
)
}
@@ -544,11 +538,6 @@ data class UiTheme(
"The remote configurations approach is more versatile and better suited for future development."
)
class UiThemeBuilder {
- /**
- * Text to be shown on the top of the app bar in the chat
- */
- var appBarTitle: String? = null
- private set
/**
* Primary color for your brand. Used for example to set the color of the appbar
@@ -932,10 +921,6 @@ data class UiTheme(
var gvaQuickReplyTextColor: Int? = null
private set
- fun setAppBarTitle(appBarTitle: String?) {
- this.appBarTitle = appBarTitle
- }
-
fun setFontRes(@FontRes fontRes: Int?) {
this.fontRes = fontRes?.takeIf { it != 0 }
}
@@ -1185,7 +1170,6 @@ data class UiTheme(
}
fun setTheme(theme: UiTheme) {
- appBarTitle = theme.appBarTitle
brandPrimaryColor = theme.brandPrimaryColor
baseLightColor = theme.baseLightColor
baseDarkColor = theme.baseDarkColor
diff --git a/widgetssdk/src/main/java/com/glia/widgets/call/CallActivity.java b/widgetssdk/src/main/java/com/glia/widgets/call/CallActivity.java
deleted file mode 100644
index 974beb585..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/call/CallActivity.java
+++ /dev/null
@@ -1,197 +0,0 @@
-package com.glia.widgets.call;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-import androidx.annotation.Nullable;
-
-import com.glia.widgets.GliaWidgets;
-import com.glia.widgets.R;
-import com.glia.widgets.base.FadeTransitionActivity;
-import com.glia.widgets.chat.ChatActivity;
-import com.glia.widgets.core.configuration.EngagementConfiguration;
-import com.glia.widgets.di.Dependencies;
-import com.glia.widgets.helper.Logger;
-import com.glia.widgets.helper.Utils;
-import com.glia.widgets.locale.LocaleString;
-import com.glia.widgets.webbrowser.WebBrowserActivity;
-
-import java.util.ArrayList;
-import java.util.Objects;
-
-/**
- * This activity is used for engagements that include audio and/or video calls.
- *
- * Main features:
- * - Requests required permissions and enqueues for audio and/or video engagements if no ongoing engagements are found.
- * - Provides video feeds from operator and visitor cameras.
- * - Provides controls for managing ongoing engagements, including video and audio.
- * - Allows switching between chat and call activities.
- *
- * Before this activity is launched, make sure that Glia Widgets SDK is set up correctly.
- *
- * Data that can be passed together with the Activity intent:
- * - {@link GliaWidgets#QUEUE_IDS}: IDs list of the queues you would like to use for your engagements.
- * For a full list of optional parameters, see the constants defined in {@link GliaWidgets}.
- *
- */
-public final class CallActivity extends FadeTransitionActivity {
- private static final String TAG = CallActivity.class.getSimpleName();
-
- private CallConfiguration callConfiguration;
-
- private CallView callView;
- private CallView.OnBackClickedListener onBackClickedListener = this::finish;
- private CallView.OnNavigateToChatListener onNavigateToChatListener = () -> {
- navigateToChat();
- finish();
- };
- private final CallView.OnNavigateToWebBrowserListener onNavigateToWebBrowserListener = this::navigateToWebBrowser;
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- Logger.i(TAG, "Create Call screen");
- setContentView(R.layout.call_activity);
- callView = findViewById(R.id.call_view);
-
- // Legacy company name support
- Dependencies.getSdkConfigurationManager().setLegacyCompanyName(getIntent().getStringExtra(GliaWidgets.COMPANY_NAME));
-
- callConfiguration = CallActivityIntentHelper.readConfiguration(this);
- if (this.getIntent().hasExtra(GliaWidgets.USE_OVERLAY)) {
- // Integrator has passed a deprecated GliaWidgets.USE_OVERLAY parameter with Intent
- // Override bubble configuration with USE_OVERLAY value
- boolean useOverlay = this.getIntent().getBooleanExtra(GliaWidgets.USE_OVERLAY, true);
- Dependencies.getSdkConfigurationManager().setLegacyUseOverlay(useOverlay);
- }
-
- if (!callView.shouldShowMediaEngagementView(callConfiguration.isUpgradeToCall)) {
- finishAndRemoveTask();
- return;
- }
-
- callView.setOnTitleUpdatedListener(this::setTitle);
- callView.setEngagementConfiguration(callConfiguration.engagementConfiguration);
- callView.setUiTheme(callConfiguration.engagementConfiguration.getRunTimeTheme());
- callView.setOnBackClickedListener(onBackClickedListener);
-
- // In case the engagement ends, Activity is removed from the device's Recents menu
- // to avoid app users to accidentally start queueing for another call when they resume
- // the app from the Recents menu and the app's backstack was empty.
- callView.setOnEndListener(this::finishAndRemoveTask);
-
- callView.setOnMinimizeListener(this::finish);
- callView.setOnNavigateToChatListener(onNavigateToChatListener);
- callView.setOnNavigateToWebBrowserListener(onNavigateToWebBrowserListener);
-
- if (savedInstanceState == null) {
- startCall();
- }
- }
-
- @Override
- protected void onResume() {
- callView.onResume();
- super.onResume();
- }
-
- @Override
- protected void onPause() {
- callView.onPause();
- super.onPause();
- }
-
- @Override
- protected void onDestroy() {
- Logger.i(TAG, "Destroy Call screen");
- onBackClickedListener = null;
- onNavigateToChatListener = null;
- callView.onDestroy();
- super.onDestroy();
- }
-
- @Override
- public void onUserInteraction() {
- super.onUserInteraction();
- callView.onUserInteraction();
- }
-
- private void startCall() {
- EngagementConfiguration engagementConfiguration = Objects.requireNonNull(callConfiguration.engagementConfiguration);
- callView.startCall(
- Objects.requireNonNull(engagementConfiguration.getCompanyName()),
- engagementConfiguration.getQueueIds(),
- engagementConfiguration.getContextAssetId(),
- Objects.requireNonNull(engagementConfiguration.getScreenSharingMode()),
- callConfiguration.isUpgradeToCall,
- callConfiguration.mediaType
- );
- }
-
- private void navigateToChat() {
- Logger.d(TAG, "navigateToChat");
- EngagementConfiguration sdkConfiguration = Objects.requireNonNull(callConfiguration.engagementConfiguration);
- ArrayList queueIds;
- if (sdkConfiguration.getQueueIds() != null) {
- queueIds = new ArrayList<>(sdkConfiguration.getQueueIds());
- } else {
- queueIds = null;
- }
- Intent newIntent = new Intent(getApplicationContext(), ChatActivity.class)
- .putExtra(GliaWidgets.QUEUE_IDS, queueIds)
- .putExtra(GliaWidgets.CONTEXT_ASSET_ID, sdkConfiguration.getContextAssetId())
- .putExtra(GliaWidgets.UI_THEME, sdkConfiguration.getRunTimeTheme())
- .putExtra(GliaWidgets.SCREEN_SHARING_MODE, sdkConfiguration.getScreenSharingMode());
- startActivity(newIntent);
- }
-
- private void navigateToWebBrowser(LocaleString title, String url) {
- Intent newIntent = WebBrowserActivity.Companion.intent(this, title, url);
- startActivity(newIntent);
- }
-
- /**
- * Creates and fills out Intent for starting CallActivity
- * @deprecated use {@link #getIntent(Context, CallConfiguration)} since 1.8.2
- * @param applicationContext - application context
- * @param engagementConfiguration - widgets sdk configuration
- * @param mediaType - media type that should be started (in case media engagement not ongoing)
- * @return Intent for starting CallActivity
- */
- @Deprecated
- public static Intent getIntent(
- Context applicationContext,
- EngagementConfiguration engagementConfiguration,
- String mediaType
- ) {
- Logger.logDeprecatedMethodUse(TAG, "getIntent(Context, GliaSdkConfiguration, String)");
- return getIntent(applicationContext,
- new CallConfiguration.Builder()
- .setEngagementConfiguration(engagementConfiguration)
- .setMediaType(Utils.toMediaType(mediaType))
- .build()
- );
- }
-
- /**
- * Creates and fills out Intent for starting CallActivity
- * @param context - Context object
- * @param callConfiguration - CallActivity configuration
- * @return - Intent for Starting CallActivity
- */
- public static Intent getIntent(
- Context context,
- CallConfiguration callConfiguration
- ) {
- return CallActivityIntentHelper.createIntent(context, callConfiguration);
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/call/CallActivity.kt b/widgetssdk/src/main/java/com/glia/widgets/call/CallActivity.kt
new file mode 100644
index 000000000..7148d187d
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/call/CallActivity.kt
@@ -0,0 +1,107 @@
+package com.glia.widgets.call
+
+import android.os.Bundle
+import com.glia.androidsdk.Engagement.MediaType
+import com.glia.widgets.R
+import com.glia.widgets.base.FadeTransitionActivity
+import com.glia.widgets.call.CallView.OnNavigateToChatListener
+import com.glia.widgets.call.CallView.OnNavigateToWebBrowserListener
+import com.glia.widgets.chat.Intention
+import com.glia.widgets.di.Dependencies
+import com.glia.widgets.helper.ExtraKeys
+import com.glia.widgets.helper.Logger
+import com.glia.widgets.helper.TAG
+import com.glia.widgets.helper.getEnumExtra
+import com.glia.widgets.launcher.ActivityLauncher
+import com.glia.widgets.locale.LocaleString
+import kotlin.properties.Delegates
+
+/**
+ * This activity is used for engagements that include audio and/or video calls.
+ *
+ *
+ * Main features:
+ * - Requests required permissions and enqueues for audio and/or video engagements if no ongoing engagements are found.
+ * - Provides video feeds from operator and visitor cameras.
+ * - Provides controls for managing ongoing engagements, including video and audio.
+ * - Allows switching between chat and call activities.
+ *
+ *
+ * Before this activity is launched, make sure that Glia Widgets SDK is set up correctly.
+ */
+internal class CallActivity : FadeTransitionActivity() {
+ private val activityLauncher: ActivityLauncher by lazy { Dependencies.activityLauncher }
+ private var callView: CallView by Delegates.notNull()
+
+ private var onBackClickedListener: CallView.OnBackClickedListener? = CallView.OnBackClickedListener { this.finish() }
+ private var onNavigateToChatListener: OnNavigateToChatListener? = OnNavigateToChatListener {
+ navigateToChat()
+ finish()
+ }
+ private val onNavigateToWebBrowserListener = OnNavigateToWebBrowserListener(this::navigateToWebBrowser)
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ Logger.i(TAG, "Create Call screen")
+ setContentView(R.layout.call_activity)
+ callView = findViewById(R.id.call_view)
+
+ val isUpgradeToCall = intent.getBooleanExtra(ExtraKeys.IS_UPGRADE_TO_CALL, false)
+
+ if (!callView.shouldShowMediaEngagementView(isUpgradeToCall)) {
+ finishAndRemoveTask()
+ return
+ }
+
+ callView.setOnTitleUpdatedListener(this::setTitle)
+ onBackClickedListener?.also(callView::setOnBackClickedListener)
+
+ // In case the engagement ends, Activity is removed from the device's Recent menu
+ // to avoid app users to accidentally start queueing for another call when they resume
+ // the app from the Recent menu and the app's backstack was empty.
+ callView.setOnEndListener { this.finishAndRemoveTask() }
+
+ callView.setOnMinimizeListener { this.finish() }
+ onNavigateToChatListener?.also(callView::setOnNavigateToChatListener)
+ callView.setOnNavigateToWebBrowserListener(onNavigateToWebBrowserListener)
+
+ if (savedInstanceState == null) {
+
+ startCall(intent.getEnumExtra(ExtraKeys.MEDIA_TYPE), isUpgradeToCall)
+ }
+ }
+
+ override fun onResume() {
+ callView.onResume()
+ super.onResume()
+ }
+
+ override fun onPause() {
+ callView.onPause()
+ super.onPause()
+ }
+
+ override fun onDestroy() {
+ Logger.i(TAG, "Destroy Call screen")
+ onBackClickedListener = null
+ onNavigateToChatListener = null
+ callView.onDestroy()
+ super.onDestroy()
+ }
+
+ override fun onUserInteraction() {
+ super.onUserInteraction()
+ callView.onUserInteraction()
+ }
+
+ private fun startCall(mediaType: MediaType?, isUpgradeToCall: Boolean) {
+ callView.startCall(isUpgradeToCall, mediaType)
+ }
+
+ private fun navigateToChat() {
+ Logger.d(TAG, "navigateToChat")
+ activityLauncher.launchChat(this, Intention.RETURN_TO_CHAT)
+ }
+
+ private fun navigateToWebBrowser(title: LocaleString, url: String) = activityLauncher.launchWebBrowser(this, title, url)
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/call/CallActivityIntentHelper.kt b/widgetssdk/src/main/java/com/glia/widgets/call/CallActivityIntentHelper.kt
deleted file mode 100644
index 1125d8e81..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/call/CallActivityIntentHelper.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.glia.widgets.call
-
-import android.content.Context
-import android.content.Intent
-import androidx.appcompat.app.AppCompatActivity
-import com.glia.androidsdk.Engagement
-import com.glia.widgets.GliaWidgets
-import com.glia.widgets.core.configuration.EngagementConfiguration
-import java.util.ArrayList
-
-internal object CallActivityIntentHelper {
-
- @JvmStatic
- fun createIntent(context: Context, callConfiguration: CallConfiguration): Intent {
- val sdkConfiguration = callConfiguration.engagementConfiguration ?: throw NullPointerException("WidgetsSdk Configuration can't be null")
-
- return Intent(context, CallActivity::class.java)
- .putExtra(GliaWidgets.QUEUE_IDS, sdkConfiguration.queueIds?.let { ArrayList(it) })
- .putExtra(GliaWidgets.CONTEXT_ASSET_ID, sdkConfiguration.contextAssetId)
- .putExtra(GliaWidgets.UI_THEME, sdkConfiguration.runTimeTheme)
- .putExtra(GliaWidgets.SCREEN_SHARING_MODE, sdkConfiguration.screenSharingMode)
- .putExtra(GliaWidgets.MEDIA_TYPE, callConfiguration.mediaType)
- .putExtra(GliaWidgets.IS_UPGRADE_TO_CALL, callConfiguration.isUpgradeToCall)
- }
-
- @JvmStatic
- fun readConfiguration(activity: AppCompatActivity): CallConfiguration {
- val intent = activity.intent
- val engagementConfiguration = EngagementConfiguration.Builder().intent(intent).build()
- val mediaType = intent.getSerializableExtra(GliaWidgets.MEDIA_TYPE) as Engagement.MediaType?
- val isUpgradeToCall = intent.getBooleanExtra(GliaWidgets.IS_UPGRADE_TO_CALL, false)
- return CallConfiguration.Builder()
- .setEngagementConfiguration(engagementConfiguration)
- .setMediaType(mediaType)
- .setIsUpgradeToCall(isUpgradeToCall)
- .build()
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/call/CallConfiguration.kt b/widgetssdk/src/main/java/com/glia/widgets/call/CallConfiguration.kt
deleted file mode 100644
index df8052301..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/call/CallConfiguration.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.glia.widgets.call
-
-import com.glia.androidsdk.Engagement
-import com.glia.widgets.core.configuration.EngagementConfiguration
-import com.glia.widgets.helper.Utils
-
-internal class CallConfiguration private constructor(builder: Builder) {
- @JvmField
- val engagementConfiguration: EngagementConfiguration?
- @JvmField
- val mediaType: Engagement.MediaType?
- @JvmField
- val isUpgradeToCall: Boolean
-
- init {
- engagementConfiguration = builder.engagementConfiguration
- mediaType = builder.mediaType
- isUpgradeToCall = builder.isUpgradeToCall
- }
-
- class Builder {
- var engagementConfiguration: EngagementConfiguration? = null
- private set
- var mediaType: Engagement.MediaType? = null
- private set
- var isUpgradeToCall = false
- private set
-
- fun setEngagementConfiguration(engagementConfiguration: EngagementConfiguration?) = apply {
- this.engagementConfiguration = engagementConfiguration
- }
-
- fun setMediaType(mediaType: Engagement.MediaType?) = apply {
- this.mediaType = mediaType
- }
-
- fun setMediaType(mediaType: String) = apply {
- this.mediaType = Utils.toMediaType(mediaType)
- }
-
- fun setIsUpgradeToCall(isUpgradeToCall: Boolean) = apply {
- this.isUpgradeToCall = isUpgradeToCall
- }
-
-
- fun build(): CallConfiguration {
- return CallConfiguration(this)
- }
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/call/CallContract.kt b/widgetssdk/src/main/java/com/glia/widgets/call/CallContract.kt
index c75420bee..fcfa070a5 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/call/CallContract.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/call/CallContract.kt
@@ -1,12 +1,11 @@
package com.glia.widgets.call
import com.glia.androidsdk.Engagement
-import com.glia.androidsdk.screensharing.ScreenSharing
-import com.glia.widgets.locale.LocaleString
import com.glia.widgets.base.BaseController
import com.glia.widgets.base.BaseView
import com.glia.widgets.core.dialog.model.ConfirmationDialogLinks
import com.glia.widgets.core.dialog.model.Link
+import com.glia.widgets.locale.LocaleString
internal interface CallContract {
interface Controller : BaseController {
@@ -21,14 +20,7 @@ internal interface CallContract {
fun muteButtonClicked()
fun videoButtonClicked()
fun flipVideoButtonClicked()
- fun startCall(
- companyName: String,
- queueIds: List?,
- visitorContextAssetId: String?,
- mediaType: Engagement.MediaType?,
- screenSharingMode: ScreenSharing.Mode,
- upgradeToCall: Boolean
- )
+ fun startCall(mediaType: Engagement.MediaType?, upgradeToCall: Boolean)
fun onResume()
fun onPause()
diff --git a/widgetssdk/src/main/java/com/glia/widgets/call/CallController.kt b/widgetssdk/src/main/java/com/glia/widgets/call/CallController.kt
index 90aa92b59..c16c26330 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/call/CallController.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/call/CallController.kt
@@ -8,7 +8,6 @@ import com.glia.androidsdk.comms.MediaDirection
import com.glia.androidsdk.comms.MediaState
import com.glia.androidsdk.comms.MediaUpgradeOffer
import com.glia.androidsdk.comms.Video
-import com.glia.androidsdk.screensharing.ScreenSharing
import com.glia.widgets.Constants
import com.glia.widgets.call.CallStatus.EngagementOngoingAudioCallStarted
import com.glia.widgets.call.CallStatus.EngagementOngoingVideoCallStarted
@@ -16,7 +15,6 @@ import com.glia.widgets.call.domain.HandleCallPermissionsUseCase
import com.glia.widgets.chat.domain.DecideOnQueueingUseCase
import com.glia.widgets.chat.domain.UpdateFromCallScreenUseCase
import com.glia.widgets.core.audio.domain.TurnSpeakerphoneUseCase
-import com.glia.widgets.core.configuration.GliaSdkConfigurationManager
import com.glia.widgets.core.dialog.DialogContract
import com.glia.widgets.core.dialog.domain.ConfirmationDialogLinksUseCase
import com.glia.widgets.core.dialog.domain.IsShowOverlayPermissionRequestDialogUseCase
@@ -37,7 +35,7 @@ import com.glia.widgets.engagement.domain.EnqueueForEngagementUseCase
import com.glia.widgets.engagement.domain.FlipCameraButtonStateUseCase
import com.glia.widgets.engagement.domain.FlipVisitorCameraUseCase
import com.glia.widgets.engagement.domain.IsCurrentEngagementCallVisualizerUseCase
-import com.glia.widgets.engagement.domain.IsQueueingOrEngagementUseCase
+import com.glia.widgets.engagement.domain.IsQueueingOrLiveEngagementUseCase
import com.glia.widgets.engagement.domain.OperatorMediaUseCase
import com.glia.widgets.engagement.domain.ScreenSharingUseCase
import com.glia.widgets.engagement.domain.ToggleVisitorAudioMediaStateUseCase
@@ -64,10 +62,8 @@ import java.util.concurrent.TimeUnit
private const val MAX_IDLE_TIME = 3200
private const val INACTIVITY_TIMER_TICKER_VALUE = 400
private const val INACTIVITY_TIMER_DELAY_VALUE = 0
-private const val TAG = "CallController"
internal class CallController(
- private val sdkConfigurationManager: GliaSdkConfigurationManager,
private val callTimer: TimeCounter,
private val inactivityTimeCounter: TimeCounter,
private val connectingTimerCounter: TimeCounter,
@@ -92,7 +88,7 @@ internal class CallController(
private val toggleVisitorVideoMediaStateUseCase: ToggleVisitorVideoMediaStateUseCase,
private val flipVisitorCameraUseCase: FlipVisitorCameraUseCase,
private val flipCameraButtonStateUseCase: FlipCameraButtonStateUseCase,
- private val isQueueingOrEngagementUseCase: IsQueueingOrEngagementUseCase,
+ private val isQueueingOrLiveEngagementUseCase: IsQueueingOrLiveEngagementUseCase,
private val enqueueForEngagementUseCase: EnqueueForEngagementUseCase,
private val decideOnQueueingUseCase: DecideOnQueueingUseCase,
private val screenSharingUseCase: ScreenSharingUseCase,
@@ -184,45 +180,21 @@ internal class CallController(
emitViewState(callState.engagementStarted())
}
- override fun startCall(
- companyName: String,
- queueIds: List?,
- visitorContextAssetId: String?,
- mediaType: Engagement.MediaType?,
- screenSharingMode: ScreenSharing.Mode,
- upgradeToCall: Boolean
- ) {
+ override fun startCall(mediaType: Engagement.MediaType?, upgradeToCall: Boolean) {
if (upgradeToCall || mediaType == null) {
- initCall(
- companyName,
- queueIds,
- visitorContextAssetId,
- mediaType,
- screenSharingMode)
+ initCall(mediaType)
return
}
handleCallPermissionsUseCase.invoke(mediaType) { isPermissionsGranted: Boolean ->
if (isPermissionsGranted) {
- initCall(
- companyName,
- queueIds,
- visitorContextAssetId,
- mediaType,
- screenSharingMode)
+ initCall(mediaType)
} else {
view?.showMissingPermissionsDialog()
}
}
}
- private fun initCall(
- companyName: String,
- queueIds: List?,
- visitorContextAssetId: String?,
- mediaType: Engagement.MediaType?,
- screenSharingMode: ScreenSharing.Mode
- ) {
- sdkConfigurationManager.screenSharingMode = screenSharingMode
+ private fun initCall(mediaType: Engagement.MediaType?) {
if (isShowOverlayPermissionRequestDialogUseCase()) {
dialogController.showOverlayPermissionsDialog()
} else {
@@ -232,7 +204,7 @@ internal class CallController(
if (callState.integratorCallStarted || dialogController.isShowingUnexpectedErrorDialog) {
return
}
- emitViewState(callState.initCall(companyName, queueIds, visitorContextAssetId, mediaType))
+ emitViewState(callState.initCall(mediaType))
createNewTimerStatusCallback()
initMessagesNotSeenCallback()
tryToQueueForEngagement()
@@ -247,7 +219,7 @@ internal class CallController(
}
private fun tryToQueueForEngagement() {
- if (!isQueueingOrEngagementUseCase()) {
+ if (!isQueueingOrLiveEngagementUseCase()) {
confirmationDialogUseCase { shouldShow: Boolean ->
if (shouldShow) {
dialogController.showEngagementConfirmationDialog()
@@ -259,7 +231,7 @@ internal class CallController(
}
override fun onLiveObservationDialogRequested() {
- if (isQueueingOrEngagementUseCase()) return
+ if (isQueueingOrLiveEngagementUseCase()) return
view?.showEngagementConfirmationDialog()
}
@@ -288,7 +260,7 @@ internal class CallController(
}
private fun enqueueForEngagement() {
- enqueueForEngagementUseCase(callState.queueIds, callState.requestedMediaType, callState.visitorContextAssetId)
+ enqueueForEngagementUseCase(callState.requestedMediaType ?: return)
}
override fun onDestroy(retained: Boolean) {
@@ -431,7 +403,7 @@ internal class CallController(
}
private fun onNewOperatorMediaState(operatorMediaState: MediaState) {
- if (!isQueueingOrEngagementUseCase.hasOngoingEngagement) return
+ if (!isQueueingOrLiveEngagementUseCase.hasOngoingLiveEngagement) return
d(TAG, "newOperatorMediaState: $operatorMediaState, timer task running: ${callTimer.isRunning}")
if (operatorMediaState.video != null) {
onOperatorMediaStateVideo(operatorMediaState)
diff --git a/widgetssdk/src/main/java/com/glia/widgets/call/CallState.java b/widgetssdk/src/main/java/com/glia/widgets/call/CallState.java
index e52d4143e..cf74450bd 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/call/CallState.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/call/CallState.java
@@ -10,7 +10,6 @@
import com.glia.widgets.helper.Logger;
import com.glia.widgets.view.floatingvisitorvideoview.FloatingVisitorVideoContract.FlipButtonState;
-import java.util.List;
import java.util.Objects;
class CallState {
@@ -23,7 +22,6 @@ class CallState {
public final boolean landscapeLayoutControlsVisible;
public final boolean isMuted;
public final boolean hasVideo;
- public final String companyName;
@Nullable
public final Engagement.MediaType requestedMediaType;
public final boolean isSpeakerOn;
@@ -31,8 +29,6 @@ class CallState {
// Need this to not update all views when only time is changed.
public final boolean isOnlyTimeChanged;
public final boolean isCallVisualizer;
- public final List queueIds;
- public final String visitorContextAssetId;
public final boolean isSharingScreen;
public final FlipButtonState flipButtonState;
@@ -45,14 +41,11 @@ private CallState(Builder builder) {
this.landscapeLayoutControlsVisible = builder.landscapeLayoutControlsVisible;
this.isMuted = builder.isMuted;
this.hasVideo = builder.hasVideo;
- this.companyName = builder.companyName;
this.requestedMediaType = builder.requestedMediaType;
this.isSpeakerOn = builder.isSpeakerOn;
this.isOnHold = builder.isOnHold;
this.isOnlyTimeChanged = builder.isOnlyTimeChanged;
this.isCallVisualizer = builder.isCallVisualizer;
- this.queueIds = builder.queueIds;
- this.visitorContextAssetId = builder.visitorContextAssetId;
this.isSharingScreen = builder.isSharingScreen;
this.flipButtonState = builder.flipButtonState;
}
@@ -83,7 +76,6 @@ public boolean equals(Object o) {
isMuted == callState.isMuted &&
hasVideo == callState.hasVideo &&
Objects.equals(callStatus, callState.callStatus) &&
- Objects.equals(companyName, callState.companyName) &&
Objects.equals(requestedMediaType, callState.requestedMediaType) &&
isSpeakerOn == callState.isSpeakerOn &&
isOnHold == callState.isOnHold &&
@@ -97,7 +89,7 @@ public boolean equals(Object o) {
public int hashCode() {
return Objects.hash(integratorCallStarted, isVisible, messagesNotSeen,
callStatus, landscapeLayoutControlsVisible, isMuted, hasVideo,
- companyName, requestedMediaType, isSpeakerOn, isOnHold, isOnlyTimeChanged,
+ requestedMediaType, isSpeakerOn, isOnHold, isOnlyTimeChanged,
isCallVisualizer, isSharingScreen, flipButtonState);
}
@@ -163,7 +155,6 @@ public String toString() {
", landscapeLayoutControlsVisible=" + landscapeLayoutControlsVisible +
", isMuted=" + isMuted +
", hasVideo=" + hasVideo +
- ", companyName: " + companyName +
", requestedMediaType: " + requestedMediaType +
", isSpeakerOn: " + isSpeakerOn +
", isOnHold: " + isOnHold +
@@ -481,15 +472,12 @@ private boolean isMuted(MediaState visitorMediaState) {
visitorMediaState.getAudio().getStatus() != Media.Status.PLAYING;
}
- public CallState initCall(String companyName, List queueIds, String visitorContextAssetId, @Nullable Engagement.MediaType requestedMediaType) {
+ public CallState initCall(@Nullable Engagement.MediaType requestedMediaType) {
return new Builder()
.copyFrom(this)
.setIntegratorCallStarted(true)
.setVisible(true)
.setIsOnHold(false)
- .setCompanyName(companyName)
- .setQueueIds(queueIds)
- .setVisitorContextAssetId(visitorContextAssetId)
.setRequestedMediaType(requestedMediaType)
.createCallState();
}
@@ -506,15 +494,12 @@ public static class Builder {
private boolean landscapeLayoutControlsVisible;
private boolean isMuted;
private boolean hasVideo;
- private String companyName;
private Engagement.MediaType requestedMediaType;
private boolean isSpeakerOn;
private boolean isOnHold;
//Maybe helpful when converting to Kotlin, as an android studio makes fields nullable.
private boolean isOnlyTimeChanged = false;
private boolean isCallVisualizer;
- private List queueIds;
- private String visitorContextAssetId;
private boolean isSharingScreen;
private FlipButtonState flipButtonState = FlipButtonState.HIDE;
@@ -553,11 +538,6 @@ public Builder setHasVideo(boolean hasVideo) {
return this;
}
- public Builder setCompanyName(String companyName) {
- this.companyName = companyName;
- return this;
- }
-
public Builder setRequestedMediaType(Engagement.MediaType requestedMediaType) {
this.requestedMediaType = requestedMediaType;
return this;
@@ -583,16 +563,6 @@ public Builder setIsCallVisualizer(boolean isCallVisualizer) {
return this;
}
- public Builder setQueueIds(List queueIds) {
- this.queueIds = queueIds;
- return this;
- }
-
- public Builder setVisitorContextAssetId(String visitorContextAssetId) {
- this.visitorContextAssetId = visitorContextAssetId;
- return this;
- }
-
public Builder setIsSharingScreen(Boolean isSharingScreen) {
this.isSharingScreen = isSharingScreen;
return this;
@@ -611,15 +581,12 @@ Builder copyFrom(CallState callState) {
landscapeLayoutControlsVisible = callState.landscapeLayoutControlsVisible;
isMuted = callState.isMuted;
hasVideo = callState.hasVideo;
- companyName = callState.companyName;
requestedMediaType = callState.requestedMediaType;
isSpeakerOn = callState.isSpeakerOn;
isOnHold = callState.isOnHold;
//as we are updating this field only when only time is changed, so needs to make it false every time.
isOnlyTimeChanged = false;
isCallVisualizer = callState.isCallVisualizer;
- queueIds = callState.queueIds;
- visitorContextAssetId = callState.visitorContextAssetId;
isSharingScreen = callState.isSharingScreen;
flipButtonState = callState.flipButtonState;
return this;
diff --git a/widgetssdk/src/main/java/com/glia/widgets/call/CallView.kt b/widgetssdk/src/main/java/com/glia/widgets/call/CallView.kt
index 5104f7318..19ec68d1d 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/call/CallView.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/call/CallView.kt
@@ -22,13 +22,11 @@ import androidx.transition.TransitionSet
import com.glia.androidsdk.Engagement
import com.glia.androidsdk.comms.MediaState
import com.glia.androidsdk.comms.VideoView
-import com.glia.androidsdk.screensharing.ScreenSharing
import com.glia.widgets.Constants
import com.glia.widgets.R
import com.glia.widgets.UiTheme
import com.glia.widgets.UiTheme.UiThemeBuilder
import com.glia.widgets.call.CallState.ViewState
-import com.glia.widgets.core.configuration.EngagementConfiguration
import com.glia.widgets.core.dialog.DialogContract
import com.glia.widgets.core.dialog.model.DialogState
import com.glia.widgets.databinding.CallButtonsLayoutBinding
@@ -41,7 +39,6 @@ import com.glia.widgets.helper.Utils
import com.glia.widgets.helper.getColorCompat
import com.glia.widgets.helper.getColorStateListCompat
import com.glia.widgets.helper.getFontCompat
-import com.glia.widgets.helper.getFullHybridTheme
import com.glia.widgets.helper.hideKeyboard
import com.glia.widgets.helper.insetsController
import com.glia.widgets.helper.requireActivity
@@ -50,6 +47,7 @@ import com.glia.widgets.helper.setLocaleContentDescription
import com.glia.widgets.helper.setLocaleHint
import com.glia.widgets.helper.setLocaleText
import com.glia.widgets.helper.showToast
+import com.glia.widgets.launcher.ActivityLauncher
import com.glia.widgets.locale.LocaleString
import com.glia.widgets.locale.StringKey
import com.glia.widgets.locale.StringKeyPair
@@ -86,6 +84,7 @@ internal class CallView(
defStyleAttr,
defStyleRes
), CallContract.View, DialogDelegate by DialogDelegateImpl() {
+ private val activityLauncher: ActivityLauncher by lazy { Dependencies.activityLauncher }
private val callTheme: CallTheme? by lazy {
Dependencies.gliaThemeManager.theme?.callTheme
@@ -174,22 +173,8 @@ internal class CallView(
}
}
- fun startCall(
- companyName: String,
- queueIds: List?,
- visitorContextAssetId: String?,
- screenSharingMode: ScreenSharing.Mode,
- isUpgradeToCall: Boolean,
- mediaType: Engagement.MediaType?
- ) {
- callController?.startCall(
- companyName,
- queueIds,
- visitorContextAssetId,
- mediaType,
- screenSharingMode,
- isUpgradeToCall
- )
+ fun startCall(isUpgradeToCall: Boolean, mediaType: Engagement.MediaType?) {
+ callController?.startCall(mediaType, isUpgradeToCall)
}
fun onDestroy() {
@@ -495,8 +480,8 @@ internal class CallView(
private fun setAppBarTheme() {
val builder = UiThemeBuilder()
builder.setTheme(theme)
- builder.setSystemNegativeColor(R.color.glia_system_negative_color)
- builder.setBaseLightColor(R.color.glia_base_light_color)
+ builder.setSystemNegativeColor(R.color.glia_negative_color)
+ builder.setBaseLightColor(R.color.glia_light_color)
builder.setBrandPrimaryColor(R.color.glia_call_view_background_color)
builder.setGliaChatHeaderTitleTintColor(android.R.color.white)
builder.setGliaChatHeaderHomeButtonTintColor(android.R.color.white)
@@ -521,12 +506,6 @@ internal class CallView(
private fun setDefaultTheme(typedArray: TypedArray) {
theme = Utils.getThemeFromTypedArray(typedArray, this.context)
- .getFullHybridTheme(Dependencies.sdkConfigurationManager.uiTheme)
- }
-
- fun setUiTheme(uiTheme: UiTheme?) {
- theme = theme.getFullHybridTheme(uiTheme ?: return)
- setupViewAppearance()
}
fun setOnBackClickedListener(onBackClicked: OnBackClickedListener) {
@@ -617,8 +596,7 @@ internal class CallView(
positiveButtonClickListener = {
resetDialogStateAndDismiss()
callController?.overlayPermissionsDialogDismissed()
- val overlayIntent = Dependencies.intentConfigurationHelper.createForOverlayPermissionScreen(context)
- this.context.startActivity(overlayIntent)
+ activityLauncher.launchOverlayPermission(context)
},
negativeButtonClickListener = {
resetDialogStateAndDismiss()
@@ -671,11 +649,6 @@ internal class CallView(
callController?.onUserInteraction()
}
- fun setEngagementConfiguration(engagementConfiguration: EngagementConfiguration?) {
- serviceChatHeadController?.setBuildTimeTheme(theme)
- serviceChatHeadController?.setEngagementConfiguration(engagementConfiguration)
- }
-
override fun showToast(message: String) {
post { context.showToast(message, Toast.LENGTH_SHORT) }
}
@@ -704,12 +677,6 @@ internal class CallView(
fun onTitleUpdated(title: LocaleString?)
}
- @Deprecated("", ReplaceWith("shouldShowMediaEngagementView(isUpgradeToCall)"))
- fun shouldShowMediaEngagementView() {
- Logger.logDeprecatedMethodUse(TAG, "shouldShowMediaEngagementView()")
- shouldShowMediaEngagementView(false)
- }
-
fun shouldShowMediaEngagementView(isUpgradeToCall: Boolean) =
callController?.shouldShowMediaEngagementView(isUpgradeToCall) ?: false
diff --git a/widgetssdk/src/main/java/com/glia/widgets/callvisualizer/CallVisualizerActivityWatcher.kt b/widgetssdk/src/main/java/com/glia/widgets/callvisualizer/CallVisualizerActivityWatcher.kt
index 678bb4420..a0c86e1c3 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/callvisualizer/CallVisualizerActivityWatcher.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/callvisualizer/CallVisualizerActivityWatcher.kt
@@ -2,7 +2,6 @@ package com.glia.widgets.callvisualizer
import android.app.Activity
import com.glia.widgets.R
-import com.glia.widgets.locale.LocaleString
import com.glia.widgets.base.BaseSingleActivityWatcher
import com.glia.widgets.callvisualizer.controller.CallVisualizerContract
import com.glia.widgets.core.dialog.model.ConfirmationDialogLinks
@@ -11,7 +10,9 @@ import com.glia.widgets.helper.GliaActivityManager
import com.glia.widgets.helper.Logger
import com.glia.widgets.helper.OneTimeEvent
import com.glia.widgets.helper.TAG
+import com.glia.widgets.launcher.ActivityLauncher
import com.glia.widgets.locale.LocaleProvider
+import com.glia.widgets.locale.LocaleString
import com.glia.widgets.view.Dialogs
import com.glia.widgets.view.snackbar.SnackBarDelegate
import com.glia.widgets.view.snackbar.SnackBarDelegateFactory
@@ -25,7 +26,8 @@ internal class CallVisualizerActivityWatcher(
private val controller: CallVisualizerContract.Controller,
gliaActivityManager: GliaActivityManager,
private val localeProvider: LocaleProvider,
- private val themeManager: UnifiedThemeManager
+ private val themeManager: UnifiedThemeManager,
+ private val activityLauncher: ActivityLauncher
) : BaseSingleActivityWatcher(gliaActivityManager) {
init {
@@ -89,8 +91,7 @@ internal class CallVisualizerActivityWatcher(
}
private fun openWebBrowser(activity: Activity, title: LocaleString, url: String) {
- val intent = WebBrowserActivity.intent(activity, title, url)
- activity.startActivity(intent)
+ activityLauncher.launchWebBrowser(activity, title, url)
}
private fun showSnackBar(activity: Activity) {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/callvisualizer/EndScreenSharingView.kt b/widgetssdk/src/main/java/com/glia/widgets/callvisualizer/EndScreenSharingView.kt
index 46b423730..43bde7f0c 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/callvisualizer/EndScreenSharingView.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/callvisualizer/EndScreenSharingView.kt
@@ -9,15 +9,12 @@ import com.glia.widgets.R
import com.glia.widgets.UiTheme
import com.glia.widgets.databinding.EndScreenSharingViewBinding
import com.glia.widgets.di.Dependencies
-import com.glia.widgets.helper.Logger
import com.glia.widgets.helper.SimpleWindowInsetsAndAnimationHandler
-import com.glia.widgets.helper.TAG
import com.glia.widgets.helper.Utils
import com.glia.widgets.helper.applyButtonTheme
import com.glia.widgets.helper.applyTextTheme
import com.glia.widgets.helper.getColorCompat
import com.glia.widgets.helper.getFontCompat
-import com.glia.widgets.helper.getFullHybridTheme
import com.glia.widgets.helper.layoutInflater
import com.glia.widgets.helper.setLocaleText
import com.glia.widgets.locale.LocaleString
@@ -70,12 +67,7 @@ internal class EndScreenSharingView(
private fun applyDefaultTheme(attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) {
context.withStyledAttributes(attrs, R.styleable.GliaView, defStyleAttr, defStyleRes) {
- var theme = Utils.getThemeFromTypedArray(this, context)
- theme.iconEndScreenShare?.let {
- binding.endSharingButton.icon = ResourcesCompat.getDrawable(context.resources, it, null)
- }
- theme = theme.getFullHybridTheme(Dependencies.sdkConfigurationManager.uiTheme)
- applyRuntimeTheme(theme)
+ applyRuntimeTheme(Utils.getThemeFromTypedArray(this, context))
}
}
@@ -109,10 +101,9 @@ internal class EndScreenSharingView(
binding.root.applyLayerTheme(theme.background)
}
- private fun applyRuntimeTheme(theme: UiTheme?) {
- if (theme == null) {
- Logger.d(TAG, "UiTheme is null!")
- return
+ private fun applyRuntimeTheme(theme: UiTheme) {
+ theme.iconEndScreenShare?.let {
+ binding.endSharingButton.icon = ResourcesCompat.getDrawable(context.resources, it, null)
}
val systemNegativeColor = theme.systemNegativeColor?.let(::getColorCompat)
diff --git a/widgetssdk/src/main/java/com/glia/widgets/callvisualizer/controller/VisitorCodeController.kt b/widgetssdk/src/main/java/com/glia/widgets/callvisualizer/controller/VisitorCodeController.kt
index 12703fd0a..2db9d6a4e 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/callvisualizer/controller/VisitorCodeController.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/callvisualizer/controller/VisitorCodeController.kt
@@ -5,7 +5,7 @@ import com.glia.widgets.callvisualizer.VisitorCodeContract
import com.glia.widgets.core.callvisualizer.domain.VisitorCodeRepository
import com.glia.widgets.engagement.State
import com.glia.widgets.engagement.domain.EngagementStateUseCase
-import com.glia.widgets.engagement.domain.IsQueueingOrEngagementUseCase
+import com.glia.widgets.engagement.domain.IsQueueingOrLiveEngagementUseCase
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.schedulers.Schedulers
@@ -14,7 +14,7 @@ internal class VisitorCodeController(
private val callVisualizerController: CallVisualizerContract.Controller,
private val visitorCodeRepository: VisitorCodeRepository,
private val engagementStateUseCase: EngagementStateUseCase,
- private val isQueueingOrEngagementUseCase: IsQueueingOrEngagementUseCase
+ private val isQueueingOrLiveEngagementUseCase: IsQueueingOrLiveEngagementUseCase
) : VisitorCodeContract.Controller {
private var disposable: Disposable? = null
@@ -63,7 +63,7 @@ internal class VisitorCodeController(
}
private fun autoCloseOnEngagement() {
- if (isQueueingOrEngagementUseCase.hasOngoingEngagement) {
+ if (isQueueingOrLiveEngagementUseCase.hasOngoingLiveEngagement) {
// Normally, there is no need for visitor code if there is already ongoing engagement.
// But it also doesn't seem right to just close it if right away when it was open
return
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/AttachmentPopup.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/AttachmentPopup.kt
index 25929f43f..941f3762c 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/AttachmentPopup.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/AttachmentPopup.kt
@@ -12,18 +12,19 @@ import com.glia.widgets.databinding.ChatAttachmentPopupBinding
import com.glia.widgets.helper.getColorCompat
import com.glia.widgets.helper.setLocaleText
import com.glia.widgets.helper.setTintCompat
+import com.glia.widgets.helper.wrapWithMaterialThemeOverlay
import com.glia.widgets.view.unifiedui.applyImageColorTheme
import com.glia.widgets.view.unifiedui.applyTextTheme
import com.glia.widgets.view.unifiedui.theme.chat.AttachmentsPopupTheme
internal class AttachmentPopup(
- anchor: View,
+ context: Context,
private val theme: AttachmentsPopupTheme?,
private val popupWindowFunc: (LinearLayout) -> PopupWindow = ::popupWindow
) {
- private val margin by lazy { anchor.context.resources.getDimensionPixelSize(R.dimen.glia_chat_attachment_menu_margin) }
- private val binding: ChatAttachmentPopupBinding by lazy { bindLayout(anchor.context) }
+ private val margin by lazy { context.resources.getDimensionPixelSize(R.dimen.glia_chat_attachment_menu_margin) }
+ private val binding: ChatAttachmentPopupBinding by lazy { bindLayout(context.wrapWithMaterialThemeOverlay()) }
private val popupWindow: PopupWindow by lazy { createPopupMenu() }
private fun createPopupMenu(): PopupWindow {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/ChatActivity.java b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatActivity.java
deleted file mode 100644
index d6b71d877..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/ChatActivity.java
+++ /dev/null
@@ -1,238 +0,0 @@
-package com.glia.widgets.chat;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Parcelable;
-
-import androidx.activity.EdgeToEdge;
-import androidx.activity.OnBackPressedCallback;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.glia.widgets.GliaWidgets;
-import com.glia.widgets.R;
-import com.glia.widgets.UiTheme;
-import com.glia.widgets.base.FadeTransitionActivity;
-import com.glia.widgets.call.CallActivity;
-import com.glia.widgets.call.CallConfiguration;
-import com.glia.widgets.core.configuration.EngagementConfiguration;
-import com.glia.widgets.di.Dependencies;
-import com.glia.widgets.helper.Logger;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * This activity is used to handle chat engagements.
- *
- * Main features:
- * - Shows chat history to authenticated visitors before enqueuing for new engagements.
- * - Requests required permissions and enqueues for new chat engagements if no ongoing engagements are found.
- * - Provides controls for managing ongoing engagements.
- * - Enables message exchange between the visitor and the operator during ongoing engagements.
- * - Allows the operator to upgrade engagements.
- *
- * Before this activity is launched, make sure that Glia Widgets SDK is set up correctly.
- *
- * Data that can be passed together with the Activity intent:
- * - {@link GliaWidgets#QUEUE_IDS}: IDs list of the queues you would like to use for your engagements.
- * For a full list of optional parameters, see the constants defined in {@link GliaWidgets}.
- *
- * Code example:
- *
- * Intent intent = new Intent(requireContext(), ChatActivity.class);
- * intent.putExtra(GliaWidgets.QUEUE_IDS, new ArrayList<>(List.of("CHAT_QUEUE_ID")));
- * startActivity(intent);
- *
- */
-public final class ChatActivity extends FadeTransitionActivity {
- private static final String TAG = ChatActivity.class.getSimpleName();
- private ChatView chatView;
-
- private EngagementConfiguration engagementConfiguration;
-
- /**
- * Creates and fills out Intent for starting ChatActivity
- *
- * @param context - Android Context object
- * @param contextId - Glia visitor context asset ID
- * @param queueId - Queue ID or `null` to use the default queues
- * @return - Intent for Starting ChatActivity
- * @deprecated use {@link #getIntent(Context, String, List)} instead.
- */
- @Deprecated
- public static Intent getIntent(
- @NonNull Context context,
- @Nullable String contextId,
- @Nullable String queueId
- ) {
- return new Intent(context, ChatActivity.class)
- .putExtra(GliaWidgets.CONTEXT_ASSET_ID, contextId)
- .putExtra(GliaWidgets.QUEUE_ID, queueId);
- }
-
- /**
- * Creates and fills out Intent for starting ChatActivity
- *
- * @param context - Android Context object
- * @param contextId - Glia visitor context asset ID
- * @param queueIds - Queue IDs of the queues you would like to use for your engagements
- * @return - Intent for Starting ChatActivity
- */
- public static Intent getIntent(
- @NonNull Context context,
- @Nullable String contextId,
- @NonNull List queueIds
- ) {
- return new Intent(context, ChatActivity.class)
- .putExtra(GliaWidgets.CONTEXT_ASSET_ID, contextId)
- .putStringArrayListExtra(GliaWidgets.QUEUE_IDS, new ArrayList<>(queueIds));
- }
-
- /**
- * Creates and fills out Intent for starting ChatActivity
- *
- * @param context - Android Context object
- * @param contextId - Glia visitor context asset ID
- * @param queueId - Queue ID or `null` to use the default queues
- * @param chatType - Type of chat screen
- * @return - Intent for Starting ChatActivity
- * @deprecated use {@link #getIntent(Context, String, List, ChatType)} instead.
- */
- @Deprecated
- public static Intent getIntent(
- @NonNull Context context,
- @Nullable String contextId,
- @Nullable String queueId,
- @Nullable ChatType chatType
- ) {
- return new Intent(context, ChatActivity.class)
- .putExtra(GliaWidgets.CONTEXT_ASSET_ID, contextId)
- .putExtra(GliaWidgets.QUEUE_ID, queueId)
- .putExtra(GliaWidgets.COMPANY_NAME, "Legacy company")
- .putExtra(GliaWidgets.CHAT_TYPE, (Parcelable) chatType);
- }
-
- /**
- * Creates and fills out Intent for starting ChatActivity
- *
- * @param context - Android Context object
- * @param contextId - Glia visitor context asset ID
- * @param queueIds - Queue IDs of the queues you would like to use for your engagements
- * @param chatType - Type of chat screen
- * @return - Intent for Starting ChatActivity
- */
- public static Intent getIntent(
- @NonNull Context context,
- @Nullable String contextId,
- @NonNull List queueIds,
- @Nullable ChatType chatType
- ) {
- return new Intent(context, ChatActivity.class)
- .putExtra(GliaWidgets.CONTEXT_ASSET_ID, contextId)
- .putExtra(GliaWidgets.QUEUE_IDS, new ArrayList<>(queueIds))
- .putExtra(GliaWidgets.COMPANY_NAME, "Legacy company")
- .putExtra(GliaWidgets.CHAT_TYPE, (Parcelable) chatType);
- }
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- EdgeToEdge.enable(this);
- super.onCreate(savedInstanceState);
- Logger.i(TAG, "Create Chat screen");
- setContentView(R.layout.chat_activity);
- chatView = findViewById(R.id.chat_view);
-
- getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
- @Override
- public void handleOnBackPressed() {
- chatView.onBackPressed();
- }
- });
-
- // Legacy company name support
- Dependencies.getSdkConfigurationManager().setLegacyCompanyName(getIntent().getStringExtra(GliaWidgets.COMPANY_NAME));
-
- chatView.setOnTitleUpdatedListener(this::setTitle);
- engagementConfiguration = createGliaSdkConfigurationFromIntent(getIntent());
- if (getIntent().hasExtra(GliaWidgets.USE_OVERLAY)) {
- // Integrator has passed a deprecated GliaWidgets.USE_OVERLAY parameter with Intent
- // Override bubble configuration with USE_OVERLAY value
- boolean useOverlay = getIntent().getBooleanExtra(GliaWidgets.USE_OVERLAY, true);
- Dependencies.getSdkConfigurationManager().setLegacyUseOverlay(useOverlay);
- }
-
- if (!chatView.shouldShow()) {
- finishAndRemoveTask();
- return;
- }
-
- chatView.setConfiguration(engagementConfiguration);
- chatView.setUiTheme(engagementConfiguration.getRunTimeTheme());
- chatView.setOnBackClickedListener(this::finish);
- chatView.setOnBackToCallListener(this::backToCallScreen);
-
- // In case the engagement ends, Activity is removed from the device's Recents menu
- // to avoid app users to accidentally start queueing for another call when they resume
- // the app from the Recents menu and the app's backstack was empty.
- chatView.setOnEndListener(this::finishAndRemoveTask);
-
- chatView.setOnMinimizeListener(this::finish);
- chatView.setOnNavigateToCallListener(this::startCallScreen);
- chatView.startChat(
- engagementConfiguration.getCompanyName(),
- engagementConfiguration.getQueueIds(),
- engagementConfiguration.getContextAssetId(),
- engagementConfiguration.getScreenSharingMode(),
- Objects.requireNonNull(engagementConfiguration.getChatType())
- );
- }
-
- @Override
- protected void onResume() {
- chatView.onResume();
- super.onResume();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- chatView.onPause();
- }
-
- @Override
- protected void onDestroy() {
- chatView.onDestroyView();
- Logger.i(TAG, "Destroy Chat screen");
- super.onDestroy();
- }
-
- private EngagementConfiguration createGliaSdkConfigurationFromIntent(Intent intent) {
- return new EngagementConfiguration.Builder()
- .intent(intent)
- .build();
- }
-
- private void startCallScreen(UiTheme theme, String mediaType) {
- startActivity(
- CallActivity.getIntent(
- getApplicationContext(),
- getConfigurationBuilder().setMediaType(mediaType)
- .setIsUpgradeToCall(true)
- .build()
- )
- );
- finish();
- }
-
- private void backToCallScreen() {
- startActivity(CallActivity.getIntent(getApplicationContext(), getConfigurationBuilder().build()));
- finish();
- }
-
- private CallConfiguration.Builder getConfigurationBuilder() {
- return new CallConfiguration.Builder().setEngagementConfiguration(engagementConfiguration);
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/ChatActivity.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatActivity.kt
new file mode 100644
index 000000000..4acb5c23f
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatActivity.kt
@@ -0,0 +1,96 @@
+package com.glia.widgets.chat
+
+import android.os.Bundle
+import androidx.activity.OnBackPressedCallback
+import androidx.activity.enableEdgeToEdge
+import com.glia.widgets.R
+import com.glia.widgets.base.FadeTransitionActivity
+import com.glia.widgets.di.Dependencies
+import com.glia.widgets.helper.ExtraKeys
+import com.glia.widgets.helper.Logger
+import com.glia.widgets.helper.TAG
+import com.glia.widgets.helper.getEnumExtra
+import com.glia.widgets.launcher.ActivityLauncher
+import kotlin.properties.Delegates
+
+/**
+ * This activity is used to handle chat engagements.
+ *
+ *
+ * Main features:
+ * - Shows chat history to authenticated visitors before enqueuing for new engagements.
+ * - Requests required permissions and enqueues for new chat engagements if no ongoing engagements are found.
+ * - Provides controls for managing ongoing engagements.
+ * - Enables message exchange between the visitor and the operator during ongoing engagements.
+ * - Allows the operator to upgrade engagements.
+ *
+ *
+ * Before this activity is launched, make sure that Glia Widgets SDK is set up correctly.
+ */
+internal class ChatActivity : FadeTransitionActivity() {
+ private val activityLauncher: ActivityLauncher by lazy { Dependencies.activityLauncher }
+
+ private var chatView: ChatView by Delegates.notNull()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ this.enableEdgeToEdge()
+ super.onCreate(savedInstanceState)
+ Logger.i(TAG, "Create Chat screen")
+ setContentView(R.layout.chat_activity)
+ chatView = findViewById(R.id.chat_view)
+
+ onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ chatView.onBackPressed()
+ }
+ })
+
+ chatView.setOnTitleUpdatedListener(this::setTitle)
+
+ if (!chatView.shouldShow()) {
+ finishAndRemoveTask()
+ return
+ }
+
+ chatView.setOnBackClickedListener(::finish)
+ chatView.setOnBackToCallListener(::backToCallScreen)
+
+ // In case the engagement ends, Activity is removed from the device's Recents menu
+ // to avoid app users to accidentally start queueing for another call when they resume
+ // the app from the Recents menu and the app's backstack was empty.
+ chatView.setOnEndListener(::finishAndRemoveTask)
+
+ chatView.setOnMinimizeListener(::finish)
+
+ val intention = intent.getEnumExtra(ExtraKeys.OPEN_CHAT_INTENTION)
+
+ check(intention != null) { "Intention must be provided" }
+
+ if (savedInstanceState == null) {
+ chatView.startChat(intention)
+ } else {
+ chatView.restoreChat()
+ }
+ }
+
+ override fun onResume() {
+ chatView.onResume()
+ super.onResume()
+ }
+
+ override fun onPause() {
+ super.onPause()
+ chatView.onPause()
+ }
+
+ override fun onDestroy() {
+ chatView.onDestroyView()
+ Logger.i(TAG, "Destroy Chat screen")
+ super.onDestroy()
+ }
+
+ private fun backToCallScreen() {
+ activityLauncher.launchCall(this, null, false)
+ finish()
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/ChatContract.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatContract.kt
index 379667eb0..6c487a645 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/ChatContract.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatContract.kt
@@ -2,6 +2,7 @@ package com.glia.widgets.chat
import android.net.Uri
import android.widget.Toast
+import com.glia.androidsdk.Engagement
import com.glia.androidsdk.chat.AttachmentFile
import com.glia.androidsdk.chat.SingleChoiceOption
import com.glia.widgets.base.BaseController
@@ -12,8 +13,10 @@ import com.glia.widgets.chat.model.CustomCardChatItem
import com.glia.widgets.chat.model.GvaButton
import com.glia.widgets.chat.model.OperatorMessageItem
import com.glia.widgets.core.dialog.model.ConfirmationDialogLinks
+import com.glia.widgets.core.dialog.model.LeaveDialogAction
import com.glia.widgets.core.dialog.model.Link
import com.glia.widgets.core.fileupload.model.LocalAttachment
+import com.glia.widgets.entrywidget.EntryWidgetContract
import com.glia.widgets.locale.LocaleString
internal interface ChatContract {
@@ -49,7 +52,8 @@ internal interface ChatContract {
fun onLinkClicked(link: Link)
fun getConfirmationDialogLinks(): ConfirmationDialogLinks
fun onEngagementConfirmationDialogRequested()
- fun initChat(companyName: String?, queueIds: List?, visitorContextAssetId: String?, chatType: ChatType)
+ fun initChat(intention: Intention)
+ fun restoreChat()
fun show()
fun onPause()
fun onResume()
@@ -58,13 +62,15 @@ internal interface ChatContract {
fun onImageCaptured(result: Boolean)
fun onContentChosen(uri: Uri)
fun onLocalImageItemClick(attachment: LocalAttachment, view: android.view.View)
+ fun leaveCurrentConversationDialogLeaveClicked(action: LeaveDialogAction)
+ fun leaveCurrentConversationDialogStayClicked()
+ fun onScTopBannerItemClicked(itemType: EntryWidgetContract.ItemType)
}
interface View : BaseView {
fun emitUploadAttachments(attachments: List)
fun emitState(chatState: ChatState)
fun emitItems(items: List)
- fun navigateToCall(mediaType: String)
fun backToCall()
fun minimizeView()
fun smoothScrollToBottom()
@@ -83,5 +89,6 @@ internal interface ChatContract {
fun dispatchImageCapture(uri: Uri)
fun navigateToImagePreview(attachmentFile: AttachmentFile, view: android.view.View)
fun navigateToImagePreview(attachmentFile: LocalAttachment, view: android.view.View)
+ fun launchCall(mediaType: Engagement.MediaType)
}
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/ChatManager.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatManager.kt
index 30f0d2b21..ec842ba03 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/ChatManager.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatManager.kt
@@ -31,7 +31,7 @@ import com.glia.widgets.chat.model.VisitorChatItem
import com.glia.widgets.core.engagement.domain.model.ChatHistoryResponse
import com.glia.widgets.core.engagement.domain.model.ChatMessageInternal
import com.glia.widgets.core.secureconversations.domain.MarkMessagesReadWithDelayUseCase
-import com.glia.widgets.engagement.domain.IsQueueingOrEngagementUseCase
+import com.glia.widgets.engagement.domain.IsQueueingOrLiveEngagementUseCase
import com.glia.widgets.helper.Logger
import com.glia.widgets.helper.TAG
import com.glia.widgets.helper.isValid
@@ -53,7 +53,7 @@ internal class ChatManager(
private val sendUnsentMessagesUseCase: SendUnsentMessagesUseCase,
private val handleCustomCardClickUseCase: HandleCustomCardClickUseCase,
private val isAuthenticatedUseCase: IsAuthenticatedUseCase,
- private val isQueueingOrEngagementUseCase: IsQueueingOrEngagementUseCase,
+ private val isQueueingOrLiveEngagementUseCase: IsQueueingOrLiveEngagementUseCase,
private val compositeDisposable: CompositeDisposable = CompositeDisposable(),
private val state: BehaviorProcessor = BehaviorProcessor.createDefault(State()),
private val quickReplies: BehaviorProcessor> = BehaviorProcessor.create(),
@@ -65,6 +65,7 @@ internal class ChatManager(
onQuickReplyReceived: (List) -> Unit,
onOperatorMessageReceived: (count: Int) -> Unit
): Flowable> {
+ compositeDisposable.clear()
subscribe(onHistoryLoaded, onOperatorMessageReceived, onQuickReplyReceived)
return state
.doOnNext(::updateQuickReplies)
@@ -107,7 +108,7 @@ internal class ChatManager(
@VisibleForTesting
fun loadHistory(onHistoryLoaded: (hasHistory: Boolean) -> Unit): Flowable {
- val historyEvent = if (isAuthenticatedUseCase() || isQueueingOrEngagementUseCase.hasOngoingEngagement) {
+ val historyEvent = if (isAuthenticatedUseCase() || isQueueingOrLiveEngagementUseCase.hasOngoingLiveEngagement) {
loadHistoryUseCase()
.doOnSuccess { historyLoaded.onNext(true) }
.zipWith(state.firstOrError(), ::mapChatHistory)
@@ -203,7 +204,7 @@ internal class ChatManager(
@VisibleForTesting
fun mapAction(action: Action, state: State): State {
return when (action) {
- is Action.QueuingStarted -> mapInQueue(action.companyName, state)
+ Action.QueuingStarted -> mapInQueue(state)
is Action.OperatorConnected -> mapOperatorConnected(action, state)
Action.Transferring -> mapTransferring(state)
is Action.OperatorJoined -> mapOperatorJoined(action, state)
@@ -371,7 +372,7 @@ internal class ChatManager(
fun mapOperatorJoined(action: Action.OperatorJoined, state: State): State = state.apply {
chatItems -= OperatorStatusItem.Transferring
chatItems += action.run {
- OperatorStatusItem.Joined(companyName, operatorFormattedName, operatorImageUrl)
+ OperatorStatusItem.Joined(operatorFormattedName, operatorImageUrl)
}
}
@@ -390,7 +391,7 @@ internal class ChatManager(
@VisibleForTesting
fun mapOperatorConnected(action: Action.OperatorConnected, state: State): State {
- val operatorStatusItem = action.run { OperatorStatusItem.Connected(companyName, operatorFormattedName, operatorImageUrl) }
+ val operatorStatusItem = action.run { OperatorStatusItem.Connected(operatorFormattedName, operatorImageUrl) }
val oldOperatorStatusItem: OperatorStatusItem? = state.operatorStatusItem
state.operatorStatusItem = operatorStatusItem
@@ -420,8 +421,8 @@ internal class ChatManager(
}
@VisibleForTesting
- fun mapInQueue(companyName: String, state: State): State = state.apply {
- OperatorStatusItem.InQueue(companyName).also {
+ fun mapInQueue(state: State): State = state.apply {
+ OperatorStatusItem.InQueue.also {
operatorStatusItem = it
chatItems += it
}
@@ -476,18 +477,18 @@ internal class ChatManager(
}
internal sealed interface Action {
- data class QueuingStarted(val companyName: String) : Action
- data class OperatorConnected(val companyName: String, val operatorFormattedName: String, val operatorImageUrl: String?) : Action
- object Transferring : Action
- data class OperatorJoined(val companyName: String, val operatorFormattedName: String, val operatorImageUrl: String?) : Action
+ data object QueuingStarted : Action
+ data class OperatorConnected(val operatorFormattedName: String, val operatorImageUrl: String?) : Action
+ data object Transferring : Action
+ data class OperatorJoined(val operatorFormattedName: String, val operatorImageUrl: String?) : Action
data class ResponseCardClicked(val responseCard: OperatorMessageItem.ResponseCard) : Action
data class OnMediaUpgradeStarted(val isVideo: Boolean) : Action
data class OnMediaUpgradeTimerUpdated(val formattedValue: String) : Action
- object OnMediaUpgradeToVideo : Action
- object OnMediaUpgradeCanceled : Action
+ data object OnMediaUpgradeToVideo : Action
+ data object OnMediaUpgradeCanceled : Action
data class CustomCardClicked(val customCard: CustomCardChatItem, val attachment: SingleChoiceAttachment) : Action
- object ChatRestored : Action
- object None : Action
+ data object ChatRestored : Action
+ data object None : Action
data class MessagePreviewAdded(val visitorChatItem: VisitorChatItem, val payload: SendMessagePayload) : Action
data class AttachmentPreviewAdded(val attachments: List, val payload: SendMessagePayload?) : Action
data class OnMessageSent(val message: VisitorMessage) : Action
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/ChatType.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatType.kt
deleted file mode 100644
index 169537189..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/ChatType.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.glia.widgets.chat
-
-import android.os.Parcelable
-import kotlinx.parcelize.Parcelize
-
-/**
- * Determines which type of chat engagement to launch.
- *
- * Code example:
- * ```
- * Intent intent = new Intent(requireContext(), ChatActivity.class);
- * intent.putExtra(GliaWidgets.QUEUE_ID, "MESSAGING_QUEUE_ID");
- * intent.putExtra(GliaWidgets.CHAT_TYPE, (Parcelable) ChatType.SECURE_MESSAGING);
- * startActivity(intent);
- * ```
- */
-@Parcelize
-enum class ChatType : Parcelable {
- /**
- * Regular engagements with live chat.
- */
- LIVE_CHAT,
-
- /**
- * Secure conversations.
- *
- * It is a secure alternative to SMS and email, which also allows for asynchronous interactions.
- *
- * @see Secure conversations
- */
- SECURE_MESSAGING;
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/ChatView.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatView.kt
index 711196dbf..c3d335136 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/ChatView.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatView.kt
@@ -1,32 +1,36 @@
package com.glia.widgets.chat
-import android.content.ClipData
import android.content.Context
-import android.content.Intent
import android.content.res.TypedArray
import android.net.Uri
-import android.provider.Settings
import android.text.Editable
import android.text.TextWatcher
import android.util.AttributeSet
import android.view.View
import android.view.accessibility.AccessibilityEvent
+import android.widget.FrameLayout
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.app.ActivityOptionsCompat
import androidx.core.content.withStyledAttributes
import androidx.core.view.ViewCompat
-import androidx.core.view.isGone
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
-import androidx.core.view.updatePadding
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver
+import androidx.transition.AutoTransition
+import androidx.transition.ChangeBounds
+import androidx.transition.ChangeTransform
+import androidx.transition.Fade
+import androidx.transition.Transition
+import androidx.transition.TransitionManager
+import androidx.transition.TransitionSet
+import com.glia.androidsdk.Engagement.MediaType
import com.glia.androidsdk.chat.AttachmentFile
-import com.glia.androidsdk.screensharing.ScreenSharing
import com.glia.widgets.Constants
import com.glia.widgets.GliaWidgets
import com.glia.widgets.R
@@ -41,13 +45,14 @@ import com.glia.widgets.chat.model.ChatInputMode
import com.glia.widgets.chat.model.ChatItem
import com.glia.widgets.chat.model.ChatState
import com.glia.widgets.chat.model.CustomCardChatItem
-import com.glia.widgets.core.configuration.EngagementConfiguration
import com.glia.widgets.core.dialog.DialogContract
import com.glia.widgets.core.dialog.model.DialogState
+import com.glia.widgets.core.dialog.model.LeaveDialogAction
import com.glia.widgets.core.fileupload.model.LocalAttachment
import com.glia.widgets.databinding.ChatViewBinding
import com.glia.widgets.di.Dependencies
-import com.glia.widgets.filepreview.ui.FilePreviewActivity
+import com.glia.widgets.entrywidget.EntryWidgetContract
+import com.glia.widgets.entrywidget.adapter.EntryWidgetAdapter
import com.glia.widgets.helper.Logger
import com.glia.widgets.helper.SimpleTextWatcher
import com.glia.widgets.helper.SimpleWindowInsetsAndAnimationHandler
@@ -60,28 +65,35 @@ import com.glia.widgets.helper.getColorCompat
import com.glia.widgets.helper.getColorStateListCompat
import com.glia.widgets.helper.getContentUriCompat
import com.glia.widgets.helper.getFontCompat
-import com.glia.widgets.helper.getFullHybridTheme
import com.glia.widgets.helper.hideKeyboard
import com.glia.widgets.helper.insetsController
import com.glia.widgets.helper.layoutInflater
import com.glia.widgets.helper.requireActivity
import com.glia.widgets.helper.setLocaleContentDescription
import com.glia.widgets.helper.setLocaleHint
+import com.glia.widgets.helper.setLocaleText
+import com.glia.widgets.launcher.ActivityLauncher
import com.glia.widgets.locale.LocaleString
import com.glia.widgets.view.Dialogs
import com.glia.widgets.view.SingleChoiceCardView.OnOptionClickedListener
+import com.glia.widgets.view.animation.SimpleTransitionListener
import com.glia.widgets.view.dialog.base.DialogDelegate
import com.glia.widgets.view.dialog.base.DialogDelegateImpl
import com.glia.widgets.view.head.ChatHeadContract
import com.glia.widgets.view.unifiedui.applyButtonTheme
import com.glia.widgets.view.unifiedui.applyColorTheme
+import com.glia.widgets.view.unifiedui.applyHintColor
+import com.glia.widgets.view.unifiedui.applyHintTheme
+import com.glia.widgets.view.unifiedui.applyImageColor
+import com.glia.widgets.view.unifiedui.applyImageColorTheme
import com.glia.widgets.view.unifiedui.applyLayerTheme
+import com.glia.widgets.view.unifiedui.applyTextColor
import com.glia.widgets.view.unifiedui.applyTextTheme
import com.glia.widgets.view.unifiedui.theme.UnifiedTheme
import com.glia.widgets.view.unifiedui.theme.base.HeaderTheme
import com.glia.widgets.view.unifiedui.theme.chat.InputTheme
import com.glia.widgets.view.unifiedui.theme.chat.UnreadIndicatorTheme
-import com.glia.widgets.webbrowser.WebBrowserActivity
+import com.glia.widgets.view.unifiedui.theme.securemessaging.SecureMessagingTheme
import com.google.android.material.shape.MarkerEdgeTreatment
import com.google.android.material.theme.overlay.MaterialThemeOverlay
import java.util.concurrent.Executor
@@ -90,11 +102,7 @@ import kotlin.properties.Delegates
internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : ConstraintLayout(
MaterialThemeOverlay.wrap(context, attrs, defStyleAttr, defStyleRes), attrs, defStyleAttr, defStyleRes
), OnFileItemClickListener, OnImageItemClickListener, ChatContract.View, DialogDelegate by DialogDelegateImpl() {
-
- @JvmOverloads
- constructor(
- context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = R.attr.gliaChatStyle
- ) : this(context, attrs, defStyleAttr, R.style.Application_Glia_Chat)
+ private val activityLauncher: ActivityLauncher by lazy { Dependencies.activityLauncher }
private var controller: ChatContract.Controller? = null
private var dialogCallback: DialogContract.Controller.Callback? = null
@@ -112,7 +120,6 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
private var onTitleUpdatedListener: OnTitleUpdatedListener? = null
private var onEndListener: OnEndListener? = null
private var onMinimizeListener: OnMinimizeListener? = null
- private var onNavigateToCallListener: OnNavigateToCallListener? = null
private var onBackToCallListener: OnBackToCallListener? = null
private val onRetryClickListener = ChatAdapter.OnRetryClickListener {
controller?.onRetryClicked(it)
@@ -166,9 +173,11 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
}
private var binding by Delegates.notNull()
+ private val rootConstraintLayout
+ get() = binding.root as ConstraintLayout
private val attachmentPopup by lazy {
AttachmentPopup(
- binding.chatMessageLayout, Dependencies.gliaThemeManager.theme?.chatTheme?.attachmentsPopup
+ context, Dependencies.gliaThemeManager.theme?.chatTheme?.attachmentsPopup
)
}
@@ -201,36 +210,20 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
SimpleWindowInsetsAndAnimationHandler(this, appBarOrToolBar = binding.appBarView)
}
- /**
- * @param uiTheme sets this view's appearance using the parameters provided in the
- * [com.glia.widgets.UiTheme]
- */
- fun setUiTheme(uiTheme: UiTheme?) {
- if (uiTheme == null) return
- theme = theme.getFullHybridTheme(uiTheme)
- setupViewAppearance()
+ @JvmOverloads
+ constructor(
+ context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = R.attr.gliaChatStyle
+ ) : this(context, attrs, defStyleAttr, R.style.Application_Glia_Chat)
+
+
+ fun startChat(intention: Intention) {
+ dialogCallback?.also { dialogController?.addCallback(it) }
+ controller?.initChat(intention)
}
- /**
- * Used to start the chat functionality.
- *
- * @param companyName Text shown in the chat while waiting in a queue.
- * @param queueIds The queue ids to which you would like to queue to and speak to operators from.
- * @param visitorContextAssetId Provide some context asset ID as to from where are you initiating the chat from.
- * @see [com.glia.widgets.GliaWidgets].USE_OVERLAY to see its full usage description.
- * Important! This parameter is ignored if the view is not used in the sdk's [ChatActivity]
- */
- @JvmOverloads
- fun startChat(
- companyName: String?,
- queueIds: List?,
- visitorContextAssetId: String?,
- screenSharingMode: ScreenSharing.Mode? = null,
- chatType: ChatType = ChatType.LIVE_CHAT
- ) {
- Dependencies.sdkConfigurationManager.screenSharingMode = screenSharingMode
+ fun restoreChat() {
dialogCallback?.also { dialogController?.addCallback(it) }
- controller?.initChat(companyName, queueIds, visitorContextAssetId, chatType)
+ controller?.restoreChat()
}
/**
@@ -275,19 +268,6 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
this.onMinimizeListener = onMinimizeListener
}
- /**
- * Add a listener here for when the user has accepted an audio or video call and should navigate
- * to a call.
- * Important! Should be used together with [.navigateToCallSuccess] to notify the view
- * of a completed navigation.
- *
- * @param onNavigateToCallListener The callback which is fired when the user accepts a media
- * upgrade offer.
- */
- fun setOnNavigateToCallListener(onNavigateToCallListener: OnNavigateToCallListener?) {
- this.onNavigateToCallListener = onNavigateToCallListener
- }
-
fun setOnBackToCallListener(onBackToCallListener: OnBackToCallListener?) {
this.onBackToCallListener = onBackToCallListener
}
@@ -314,7 +294,6 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
resetDialogStateAndDismiss()
onEndListener = null
- onNavigateToCallListener = null
destroyController()
adapter.unregisterAdapterDataObserver(dataObserver)
binding.chatRecyclerView.adapter = null
@@ -348,7 +327,7 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
// some state updates on core-sdk are coming from the computation thread
// need to update state on uiThread
post {
- updateShowSendButton(chatState)
+ updateSendButtonState(chatState)
updateChatEditText(chatState)
updateAppBar(chatState)
binding.newMessagesIndicatorLayout.isVisible = chatState.showMessagesUnseenIndicator
@@ -365,6 +344,7 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
binding.operatorTypingAnimationView.isVisible = chatState.isOperatorTyping
updateAttachmentButton(chatState)
updateQuickRepliesState(chatState)
+ updateSecureMessagingState(chatState)
}
}
@@ -372,10 +352,6 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
adapter.submitList(items)
}
- override fun navigateToCall(mediaType: String) {
- onNavigateToCallListener?.call(theme, mediaType)
- }
-
override fun backToCall() {
onBackToCallListener?.onBackToCall()
}
@@ -409,7 +385,7 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
context.requireActivity(), view, context.getString(R.string.glia_file_preview_transition_name) // Not translatable
)
- context.startActivity(FilePreviewActivity.intent(context, attachmentFile), options.toBundle())
+ activityLauncher.launchImagePreview(context, attachmentFile, options.toBundle())
insetsController?.hideKeyboard()
}
@@ -418,10 +394,15 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
context.requireActivity(), view, context.getString(R.string.glia_file_preview_transition_name) // Not translatable
)
- context.startActivity(FilePreviewActivity.intent(context, attachmentFile), options.toBundle())
+ activityLauncher.launchImagePreview(context, attachmentFile, options.toBundle())
insetsController?.hideKeyboard()
}
+ override fun launchCall(mediaType: MediaType) {
+ activityLauncher.launchCall(context, mediaType, false)
+ context.asActivity()?.finish()
+ }
+
override fun fileIsNotReadyForPreview() {
showToast(localeProvider.getString(R.string.android_file_not_ready_for_preview))
}
@@ -431,9 +412,7 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
}
override fun navigateToWebBrowserActivity(title: LocaleString, url: String) {
- context.startActivity(
- WebBrowserActivity.intent(context, title, url)
- )
+ activityLauncher.launchWebBrowser(context, title, url)
}
override fun showEngagementConfirmationDialog() {
@@ -462,36 +441,23 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
}
override fun requestOpenEmailClient(uri: Uri) {
- val intent = Intent(Intent.ACTION_SENDTO).setData(Uri.parse("mailto:")) // This step makes sure that only email apps handle this.
- .putExtra(Intent.EXTRA_EMAIL, arrayOf(uri.schemeSpecificPart))
-
- if (intent.resolveActivity(context.packageManager) != null) {
- context.startActivity(intent)
- } else {
+ activityLauncher.launchEmailClient(context, uri) {
Logger.e(TAG, "No email client, uri - $uri")
showToast(localeProvider.getString(R.string.error_general))
}
}
override fun requestOpenDialer(uri: Uri) {
- val intent = Intent(Intent.ACTION_DIAL).setData(uri)
-
- if (intent.resolveActivity(context.packageManager) != null) {
- context.startActivity(intent)
- } else {
+ activityLauncher.launchDialer(context, uri) {
Logger.e(TAG, "No dialer uri - $uri")
showToast(localeProvider.getString(R.string.error_general))
}
}
override fun requestOpenUri(uri: Uri) {
- Intent(Intent.ACTION_VIEW, uri).addCategory(Intent.CATEGORY_BROWSABLE).also {
- if (it.resolveActivity(context.packageManager) != null) {
- context.startActivity(it)
- } else {
- Logger.e(TAG, "No app to open url - $uri")
- showToast(localeProvider.getString(R.string.error_general))
- }
+ activityLauncher.launchUri(context, uri) {
+ Logger.e(TAG, "No app to open url - $uri")
+ showToast(localeProvider.getString(R.string.error_general))
}
}
@@ -510,12 +476,12 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
if (updateDialogState(it)) {
when (it) {
DialogState.None -> resetDialogStateAndDismiss()
- DialogState.MessageCenterUnavailable -> post { showChatUnavailableView() }
DialogState.UnexpectedError -> post { showUnexpectedErrorDialog() }
DialogState.ExitQueue -> post { showExitQueueDialog() }
DialogState.OverlayPermission -> post { showOverlayPermissionsDialog() }
DialogState.EndEngagement -> post { showEndEngagementDialog() }
DialogState.Confirmation -> post { controller?.onEngagementConfirmationDialogRequested() }
+ is DialogState.LeaveCurrentConversation -> post { showLeaveCurrentConversationDialog(it.action) }
DialogState.VisitorCode -> {
Logger.e(TAG, "DialogController callback in ChatView with MODE_VISITOR_CODE")
@@ -532,10 +498,11 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
binding.addAttachmentButton.isVisible = chatState.isAttachmentButtonVisible
}
- private fun updateShowSendButton(chatState: ChatState) {
- if (chatState.showSendButton == binding.sendButton.isVisible) return
-
- binding.sendButton.isVisible = chatState.showSendButton
+ private fun updateSendButtonState(chatState: ChatState) {
+ binding.sendButton.apply {
+ isVisible = chatState.isSendButtonVisible
+ isEnabled = chatState.isSendButtonEnabled
+ }
}
private fun updateChatEditText(chatState: ChatState) {
@@ -547,6 +514,12 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
else -> binding.chatEditText.setLocaleHint(R.string.chat_input_placeholder)
}
binding.chatEditText.isEnabled = chatState.chatInputMode.isEnabled
+ Dependencies.gliaThemeManager.theme?.chatTheme?.let {
+ binding.chatEditText.apply{
+ val inputTheme = if (isEnabled) it.input else it.inputDisabled
+ applyTextTheme(inputTheme?.text, withAlignment = false)
+ }
+ }
}
private fun updateAppBar(chatState: ChatState) {
@@ -566,9 +539,11 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
showToolbar(LocaleString(R.string.message_center_header))
binding.appBarView.hideBackButton()
binding.appBarView.showXButton()
+ binding.secureConversationsBottomBanner.visibility = View.VISIBLE
} else {
showToolbar(LocaleString(R.string.engagement_chat_title))
binding.appBarView.showBackButton()
+ binding.secureConversationsBottomBanner.visibility = View.GONE
}
}
@@ -605,7 +580,6 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
private fun setDefaultTheme(typedArray: TypedArray) {
theme = Utils.getThemeFromTypedArray(typedArray, this.context)
- theme = theme.getFullHybridTheme(Dependencies.sdkConfigurationManager.uiTheme)
}
private fun initConfigurations() {
@@ -660,33 +634,62 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
binding.newMessagesIndicatorImage.setShowRippleAnimation(false)
binding.newMessagesIndicatorImage.setTheme(theme)
binding.newMessagesIndicatorCard.shapeAppearanceModel = shapeAppearanceModel
- theme.brandPrimaryColor?.let(::getColorStateListCompat)?.also {
- binding.newMessagesBadgeView.backgroundTintList = it
- }
- theme.baseLightColor?.let(::getColorCompat)?.also(binding.newMessagesBadgeView::setTextColor)
-
- // colors
- theme.baseShadeColor?.let(::getColorCompat)?.also(binding.dividerView::setBackgroundColor)
-
- theme.brandPrimaryColor?.let(::getColorStateListCompat)?.also {
- binding.newMessagesBadgeView.backgroundTintList = it
- }
- binding.sendButton.imageTintList = theme.sendMessageButtonTintColor?.let(::getColorStateListCompat)
-
- theme.baseDarkColor?.let(::getColorCompat)?.also(binding.chatEditText::setTextColor)
- theme.baseNormalColor?.let(::getColorCompat)?.also(binding.chatEditText::setHintTextColor)
+ // global colors
+ theme.brandPrimaryColor
+ ?.also(binding.operatorTypingAnimationView::addColorFilter)
+ ?.let(::getColorStateListCompat)
+ ?.also(binding.newMessagesBadgeView::setBackgroundTintList)
+
+ theme.baseLightColor?.let(::getColorCompat)
+ ?.also(binding.newMessagesBadgeView::setTextColor)
+ ?.also(binding.scErrorLabel::setTextColor)
+ theme.baseLightColor?.let(::getColorStateListCompat)
+ ?.also(binding.scErrorLabel::setCompoundDrawableTintList)
+
+ theme.systemNegativeColor?.let(::getColorCompat)
+ ?.also(binding.scErrorLabel::setBackgroundColor)
+
+ val disabledInputColor = theme.baseShadeColor?.let(::getColorCompat)
+ ?.also(binding.chatDivider::setBackgroundColor)
+ ?.also(binding.scBottomBannerDivider::setBackgroundColor)
+
+ theme.baseDarkColor?.let(::getColorCompat)
+ ?.also{ binding.chatEditText.applyTextColor(it, disabledInputColor) }
+ theme.baseNormalColor?.let(::getColorCompat)
+ ?.also{ binding.chatEditText.applyHintColor(it, disabledInputColor) }
+ ?.also{ binding.addAttachmentButton.applyImageColor(it, disabledInputColor) }
+ ?.also(binding.scBottomBannerLabel::setTextColor)
+ theme.systemAgentBubbleColor?.let(::getColorCompat)?.also(binding.scBottomBannerLabel::setBackgroundColor)
+
+ // other
+ theme.sendMessageButtonTintColor?.let(::getColorCompat)
+ ?.also { binding.sendButton.applyImageColor(it, disabledInputColor) }
theme.gliaChatBackgroundColor?.let(::getColorCompat)?.also(::setBackgroundColor)
+ binding.gvaQuickRepliesLayout.updateTheme(theme)
// fonts
theme.fontRes?.also { binding.chatEditText.typeface = getFontCompat(it) }
- theme.brandPrimaryColor?.also {
- binding.operatorTypingAnimationView.addColorFilter(color = it)
+ // remote locales
+ binding.scErrorLabel.setLocaleText(R.string.secure_messaging_chat_unavailable_message)
+ binding.scBottomBannerLabel.setLocaleText(R.string.secure_messaging_chat_banner_bottom)
+ binding.sendButton.setLocaleContentDescription(R.string.general_send)
+ binding.scTopBannerTitle.setLocaleText(R.string.secure_messaging_chat_banner_top)
+
+ binding.scTopBannerOptions.apply {
+ val entryWidgetTheme = Dependencies.gliaThemeManager.theme?.chatTheme?.secureMessaging
+ val entryWidgetsAdapter = EntryWidgetAdapter(
+ EntryWidgetContract.ViewType.MESSAGING_LIVE_SUPPORT,
+ entryWidgetTheme
+ )
+ entryWidgetsAdapter.onItemClickListener = {
+ onNeedSupportButtonClicked(null)
+ controller?.onScTopBannerItemClicked(it)
+ }
+ setAdapter(entryWidgetsAdapter)
}
- binding.gvaQuickRepliesLayout.updateTheme(theme)
- binding.sendButton.setLocaleContentDescription(R.string.general_send)
applyTheme(Dependencies.gliaThemeManager.theme)
}
@@ -714,11 +717,15 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
viewHolder?.itemView?.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
}
}
+
+ binding.scTopBannerTitle.setOnClickListener(::onNeedSupportButtonClicked)
+ binding.scTopBannerIcon.setOnClickListener(::onNeedSupportButtonClicked)
+ binding.blockingCurtain.setOnClickListener(::onNeedSupportButtonClicked)
}
private fun setupAddAttachmentButton() {
binding.addAttachmentButton.setOnClickListener {
- attachmentPopup.show(binding.chatMessageLayout, {
+ attachmentPopup.show(binding.addAttachmentButton, {
getContentLauncher?.launch(arrayOf(Constants.MIME_TYPE_IMAGES))
}, {
controller?.onTakePhotoClicked()
@@ -766,17 +773,28 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
Dialogs.showOverlayPermissionsDialog(context = context, uiTheme = theme, positiveButtonClickListener = {
controller?.overlayPermissionsDialogDismissed()
resetDialogStateAndDismiss()
- val overlayIntent = Intent(
- Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + this.context.packageName)
- )
- overlayIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
- this.context.startActivity(overlayIntent)
+ activityLauncher.launchOverlayPermission(context)
}, negativeButtonClickListener = {
controller?.overlayPermissionsDialogDismissed()
resetDialogStateAndDismiss()
})
}
+ private fun showLeaveCurrentConversationDialog(action: LeaveDialogAction) = showDialog {
+ Dialogs.showLeaveCurrentConversationDialog(
+ context,
+ theme,
+ onStay = {
+ resetDialogStateAndDismiss()
+ controller?.leaveCurrentConversationDialogStayClicked()
+ },
+ onLeave = {
+ resetDialogStateAndDismiss()
+ controller?.leaveCurrentConversationDialogLeaveClicked(action)
+ }
+ )
+ }
+
private fun chatEnded() {
Dependencies.destroyControllers()
}
@@ -794,22 +812,15 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
}
override fun onFileOpenClick(file: AttachmentFile) {
- val contentUri = getContentUriCompat(file.fileName, context)
-
- with(Intent(Intent.ACTION_VIEW)) {
- clipData = ClipData.newRawUri("", contentUri)
- addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
- setDataAndType(contentUri, file.contentType)
- resolveActivity(context.packageManager)?.also { context.startActivity(this) }
- } ?: showToast(message = localeProvider.getString(R.string.android_file_view_error))
+ activityLauncher.launchFileReader(context, getContentUriCompat(file.fileName, context), file.contentType) {
+ showToast(message = localeProvider.getString(R.string.android_file_view_error))
+ }
}
override fun onLocalFileOpenClick(attachment: LocalAttachment) {
- with(Intent(Intent.ACTION_VIEW)) {
- setDataAndType(attachment.uri, attachment.mimeType)
- addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
- resolveActivity(context.packageManager)?.also { context.startActivity(this) }
- } ?: showToast(message = localeProvider.getString(R.string.android_file_view_error))
+ activityLauncher.launchFileReader(context, attachment.uri, attachment.mimeType.orEmpty()) {
+ showToast(message = localeProvider.getString(R.string.android_file_view_error))
+ }
}
override fun onImageItemClick(item: AttachmentFile, view: View) {
@@ -820,11 +831,6 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
controller?.onLocalImageItemClick(attachment, view)
}
- fun setConfiguration(configuration: EngagementConfiguration?) {
- serviceChatHeadController?.setBuildTimeTheme(theme)
- serviceChatHeadController?.setEngagementConfiguration(configuration)
- }
-
override fun showToast(message: String, duration: Int) {
Toast.makeText(context, message, duration).show()
}
@@ -836,24 +842,28 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
chatTheme.header?.also(::applyHeaderTheme)
- chatTheme.input?.also(::applyInputTheme)
+ chatTheme.input?.also{ applyInputTheme(it, chatTheme.inputDisabled) }
chatTheme.typingIndicator?.primaryColor?.also(binding.operatorTypingAnimationView::addColorFilter)
chatTheme.unreadIndicator?.also(::applyUnreadMessagesTheme)
+
+ chatTheme.secureMessaging?.also(::applySecureMessagingTheme)
}
private fun applyHeaderTheme(headerTheme: HeaderTheme) {
binding.appBarView.applyHeaderTheme(headerTheme)
}
- private fun applyInputTheme(inputTheme: InputTheme) {
- binding.sendButton.applyButtonTheme(inputTheme.sendButton)
- binding.addAttachmentButton.applyButtonTheme(inputTheme.mediaButton)
- binding.dividerView.applyColorTheme(inputTheme.divider)
- binding.chatMessageLayout.applyLayerTheme(inputTheme.background)
- binding.chatEditText.applyTextTheme(textTheme = inputTheme.text, withAlignment = false)
- inputTheme.placeholder?.textColor?.primaryColor?.also(binding.chatEditText::setHintTextColor)
+ private fun applyInputTheme(defaultTheme: InputTheme, disabledTheme: InputTheme?) {
+ binding.sendButton.applyButtonTheme(defaultTheme.sendButton, disabledTheme?.sendButton)
+ binding.addAttachmentButton.applyButtonTheme(defaultTheme.mediaButton, disabledTheme?.mediaButton)
+ binding.chatDivider.applyColorTheme(defaultTheme.divider, disabledTheme?.divider)
+ binding.messageInputBackground.applyLayerTheme(defaultTheme.background, disabledTheme?.background)
+ // It is impossible to support all possible change to text view related to disabled state
+ // So it is achived manually in [updateChatEditText] method
+ binding.chatEditText.applyTextTheme(textTheme = defaultTheme.text, withAlignment = false)
+ binding.chatEditText.applyHintTheme(defaultTheme.placeholder, disabledTheme?.placeholder)
}
private fun applyUnreadMessagesTheme(unreadIndicatorTheme: UnreadIndicatorTheme) {
@@ -862,19 +872,33 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
unreadIndicatorTheme.background?.primaryColor?.also(binding.newMessagesIndicatorCard::setCardBackgroundColor)
}
- private fun showChatUnavailableView() = showDialog {
- Dialogs.showMessageCenterUnavailableDialog(context, theme) {
- hideChatControls(it.window?.decorView?.height ?: 0)
- }
- }
-
- private fun hideChatControls(dialogHeight: Int) {
- binding.apply {
- chatRecyclerView.updatePadding(bottom = dialogHeight)
- chatRecyclerView.scrollBy(0, dialogHeight)
- chatMessageLayout.isGone = true
- operatorTypingAnimationView.isGone = true
+ private fun applySecureMessagingTheme(secureMessagingTheme: SecureMessagingTheme) {
+ binding.scBottomBannerLabel.applyTextTheme(secureMessagingTheme.bottomBannerText)
+ binding.scBottomBannerLabel.applyLayerTheme(secureMessagingTheme.bottomBannerBackground)
+ binding.scBottomBannerDivider.applyColorTheme(secureMessagingTheme.bottomBannerDividerColor)
+ binding.scErrorLabel.applyLayerTheme(secureMessagingTheme.unavailableStatusBackground)
+ binding.scErrorLabel.applyTextTheme(secureMessagingTheme.unavailableStatusText)
+ binding.scTopBannerBackground.applyLayerTheme(secureMessagingTheme.topBannerBackground)
+ binding.scTopBannerTitle.applyTextTheme(secureMessagingTheme.topBannerText)
+ binding.scTopBannerIcon.applyImageColorTheme(secureMessagingTheme.topBannerDropDownIconColor)
+ binding.scTopBannerDivider.applyColorTheme(secureMessagingTheme.topBannerDividerColor)
+ binding.scTopBannerOptions.setSecureMessagingTheme(secureMessagingTheme)
+ }
+
+ private fun updateSecureMessagingState(state: ChatState) {
+ TransitionManager.beginDelayedTransition(rootConstraintLayout, AutoTransition()
+ .addTarget(binding.scErrorLabel)
+ .addTarget(binding.scTopBannerTitle)
+ .addTarget(binding.scTopBannerIcon)
+ .addTarget(binding.scTopBannerBackground)
+ .addTarget(binding.scTopBannerDivider)
+ )
+ binding.scErrorLabel.isVisible = state.isSecureConversationsUnavailableLabelVisible
+ if (state.isSecureConversationsTopBannerVisible.not() && isNeedSupportDropDownShown()) {
+ onNeedSupportButtonClicked(null)
}
+ binding.scTopBannerGroup.isVisible = state.isSecureConversationsTopBannerVisible
+ binding.scTopBannerDivider.isVisible = state.isSecureConversationsTopBannerVisible
}
@VisibleForTesting
@@ -904,17 +928,6 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
fun onMinimize()
}
- fun interface OnNavigateToCallListener {
- /**
- * Callback which is fired when the user has accepted a media upgrade offer and should be
- * navigated to a view where they can visually see data about their media upgrade.
- *
- * @param theme Used to pass the finalized [UiTheme]
- * to the activity which is being navigated to.
- */
- fun call(theme: UiTheme?, mediaType: String?)
- }
-
fun interface OnBackToCallListener {
fun onBackToCall()
}
@@ -922,4 +935,60 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In
fun interface OnTitleUpdatedListener {
fun onTitleUpdated(title: LocaleString?)
}
+
+ private fun onNeedSupportButtonClicked(ignored: View?) {
+ val animationRules = createSecureConversationAnimationRules()
+
+ TransitionManager.beginDelayedTransition(rootConstraintLayout, animationRules)
+ updateLayoutToNewNeedSupportBannerState(!isNeedSupportDropDownShown())
+ }
+
+ private fun isNeedSupportDropDownShown(): Boolean {
+ return binding.blockingCurtain.isVisible
+ }
+
+ private fun createSecureConversationAnimationRules(): TransitionSet {
+ return TransitionSet()
+ .addTransition(
+ ChangeTransform()
+ .addTarget(binding.scTopBannerIcon)
+ )
+ .addTransition(
+ Fade()
+ .addTarget(binding.blockingCurtain)
+ .addTarget(binding.scTopBannerDivider)
+ )
+ .addTransition(
+ ChangeBounds()
+ .addTarget(binding.blockingCurtain)
+ .addTarget(binding.scTopBannerOptions)
+ )
+ .setDuration(500)
+ .addListener(object : SimpleTransitionListener() {
+ override fun onTransitionEnd(transition: Transition) {
+ binding.blockingCurtain.apply {
+ if (isInvisible) {
+ visibility = FrameLayout.GONE
+ }
+ }
+ }
+ })
+ }
+
+ private fun updateLayoutToNewNeedSupportBannerState(shouldShowDropDown: Boolean) {
+ ConstraintSet().apply {
+ clone(rootConstraintLayout)
+ if (shouldShowDropDown) {
+ clear(R.id.sc_top_banner_options, ConstraintSet.BOTTOM)
+ setVisibility(R.id.blocking_curtain, VISIBLE)
+ setVisibility(R.id.sc_top_banner_divider, View.INVISIBLE)
+ setRotation(R.id.sc_top_banner_icon, 180f)
+ } else {
+ connect(R.id.sc_top_banner_options, ConstraintSet.BOTTOM, R.id.header_barrier, ConstraintSet.BOTTOM)
+ setVisibility(R.id.blocking_curtain, INVISIBLE)
+ setVisibility(R.id.sc_top_banner_divider, VISIBLE)
+ setRotation(R.id.sc_top_banner_icon, 0f)
+ }
+ }.applyTo(rootConstraintLayout)
+ }
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/GvaChip.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/GvaChip.kt
index 28d82d126..23c3a703e 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/GvaChip.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/GvaChip.kt
@@ -77,6 +77,15 @@ internal class GvaChipGroup @JvmOverloads constructor(
isSelectionRequired = false
isSingleLine = false
isSingleSelection = false
+
+ if (isInEditMode) {
+ // Add dummy content to show on Studio layout previews
+ setButtons(listOf(
+ GvaButton(text = "GVA Option 1"),
+ GvaButton(text = "GVA Option 2"),
+ GvaButton(text = "GVA Option 3")
+ ))
+ }
}
internal fun updateTheme(theme: UiTheme?) {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/Intention.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/Intention.kt
new file mode 100644
index 000000000..8ad229774
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/Intention.kt
@@ -0,0 +1,28 @@
+package com.glia.widgets.chat
+
+internal enum class Intention {
+ /* Return to Chat. Used in cases when navigating from other screens with bubble */
+ RETURN_TO_CHAT,
+
+ /* Open Secure Conversation chat with Leave dialog, and if the visitor chooses to leave, open chat screen and start enqueueing for audio */
+ SC_DIALOG_START_AUDIO,
+
+ /* Open Secure Conversation chat with Leave dialog, and if the visitor chooses to leave, open chat screen and start enqueueing for video */
+ SC_DIALOG_START_VIDEO,
+
+ /* Open Secure Conversation chat with Leave dialog, and if the visitor chooses to leave, start enqueueing for chat */
+ SC_DIALOG_ENQUEUE_FOR_TEXT,
+
+ /* Open Secure Conversation chat */
+ SC_CHAT,
+
+ /* Open Live chat */
+ LIVE_CHAT;
+
+ val isSecureConversation: Boolean
+ get() = when (this) {
+ SC_CHAT, SC_DIALOG_ENQUEUE_FOR_TEXT, SC_DIALOG_START_AUDIO, SC_DIALOG_START_VIDEO -> true
+ else -> false
+ }
+
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/UploadAttachmentAdapter.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/UploadAttachmentAdapter.kt
index 720743099..4afd2707a 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/UploadAttachmentAdapter.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/UploadAttachmentAdapter.kt
@@ -93,7 +93,7 @@ internal class ViewHolder(
private val theme: FileUploadBarTheme? by lazy {
Dependencies.gliaThemeManager.theme?.run {
if (isMessageCenter) {
- secureConversationsWelcomeScreenTheme?.attachmentListTheme
+ secureMessagingWelcomeScreenTheme?.attachmentListTheme
} else {
chatTheme?.input?.fileUploadBar
}
@@ -133,7 +133,7 @@ internal class ViewHolder(
) {
if (attachment.attachmentStatus.isError) {
extensionContainerView.setCardBackgroundColor(
- itemView.getColorStateListCompat(R.color.glia_system_agent_bubble_color)
+ itemView.getColorStateListCompat(R.color.glia_neutral_color)
)
showExtensionTypeImage()
extensionTypeImage.setImageResource(R.drawable.ic_info)
@@ -143,7 +143,7 @@ internal class ViewHolder(
extensionTypeImage.applyImageColorTheme(filePreviewTheme?.errorIcon)
} else {
extensionContainerView.setCardBackgroundColor(
- itemView.getColorCompat(R.color.glia_brand_primary_color)
+ itemView.getColorCompat(R.color.glia_primary_color)
)
extensionTypeImage.scaleType = ImageView.ScaleType.FIT_XY
extensionTypeImage.imageTintList = null
@@ -193,7 +193,7 @@ internal class ViewHolder(
private fun updateTitleAndStatusText(fileName: String, byteSize: String, status: Status) {
val textColorRes =
- if (status.isError) Material_R.color.design_default_color_error else R.color.glia_base_normal_color
+ if (status.isError) Material_R.color.design_default_color_error else R.color.glia_normal_color
titleText.setTextColor(itemView.getColorCompat(textColorRes))
titleText.text =
@@ -241,7 +241,7 @@ internal class ViewHolder(
private fun setProgressIndicatorState(status: Status) {
val normalColor = theme?.progress?.primaryColor
- ?: itemView.getColorCompat(R.color.glia_brand_primary_color)
+ ?: itemView.getColorCompat(R.color.glia_primary_color)
val errorColor = theme?.errorProgress?.primaryColor
?: itemView.getColorCompat(com.google.android.material.R.color.design_default_color_error)
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/OperatorStatusViewHolder.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/OperatorStatusViewHolder.kt
index 4376560f6..8f9862a5f 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/OperatorStatusViewHolder.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/OperatorStatusViewHolder.kt
@@ -85,7 +85,7 @@ internal class OperatorStatusViewHolder(
chatStartingHeadingView.setLocaleText(R.string.general_company_name)
when (item) {
is OperatorStatusItem.Connected -> applyConnectedState(item.operatorName, item.profileImgUrl)
- is OperatorStatusItem.InQueue -> applyInQueueState(item.companyName)
+ is OperatorStatusItem.InQueue -> applyInQueueState()
is OperatorStatusItem.Joined -> applyConnectedState(item.operatorName, item.profileImgUrl)
is OperatorStatusItem.Transferring -> applyTransferringState()
}
@@ -93,13 +93,13 @@ internal class OperatorStatusViewHolder(
statusPictureView.setShowRippleAnimation(isShowStatusViewRippleAnimation(item))
}
- private fun applyInQueueState(companyName: String?) {
+ private fun applyInQueueState() {
statusPictureView.showPlaceholder()
applyChatStartingViewsVisibility()
applyChatStartedViewsVisibility(false)
itemView.setLocaleContentDescription(
R.string.android_chat_queue_message_accessibility_label,
- StringKeyPair(StringKey.COMPANY_NAME, companyName ?: "")
+ StringKeyPair(StringKey.COMPANY_NAME, "")
)
engagementStatesTheme?.queue.also(::applyEngagementState)
@@ -124,24 +124,6 @@ internal class OperatorStatusViewHolder(
engagementStatesTheme?.connected.also(::applyEngagementState)
}
- private fun applyJoinedState(operatorName: String, profileImgUrl: String?) {
- profileImgUrl?.let { statusPictureView.showProfileImage(it) }
- ?: statusPictureView.showPlaceholder()
- chatStartedNameView.text = operatorName
- chatStartedCaptionView.setLocaleText(
- R.string.chat_operator_joined_system_message, StringKeyPair(StringKey.OPERATOR_NAME, operatorName)
- )
- itemView.setLocaleContentDescription(
- R.string.chat_operator_joined_system_message,
- StringKeyPair(StringKey.OPERATOR_NAME, operatorName)
- )
-
- applyChatStartingViewsVisibility(false)
- applyChatStartedViewsVisibility()
-
- engagementStatesTheme?.connecting.also(::applyEngagementState)
- }
-
private fun applyTransferringState() {
statusPictureView.showPlaceholder()
chatStartingCaptionView.isVisible = true
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/fileattachment/FileAttachmentViewHolder.java b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/fileattachment/FileAttachmentViewHolder.java
index b1a6c01a3..8b87be54e 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/fileattachment/FileAttachmentViewHolder.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/adapter/holder/fileattachment/FileAttachmentViewHolder.java
@@ -51,7 +51,7 @@ protected void setData(LocalAttachment attachment) {
}
private void setupExtensionContainer(@NonNull View itemView) {
- extensionContainerView.setCardBackgroundColor(ContextCompat.getColor(itemView.getContext(), R.color.glia_brand_primary_color));
+ extensionContainerView.setCardBackgroundColor(ContextCompat.getColor(itemView.getContext(), R.color.glia_primary_color));
}
private void updateTitle(String name, long byteSize) {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/controller/ChatController.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/controller/ChatController.kt
index 0638ad2d7..6f93102d2 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/controller/ChatController.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/controller/ChatController.kt
@@ -2,6 +2,7 @@ package com.glia.widgets.chat.controller
import android.net.Uri
import android.view.View
+import com.glia.androidsdk.Engagement
import com.glia.androidsdk.GliaException
import com.glia.androidsdk.Operator
import com.glia.androidsdk.chat.AttachmentFile
@@ -15,8 +16,8 @@ import com.glia.androidsdk.site.SiteInfo
import com.glia.widgets.Constants
import com.glia.widgets.chat.ChatContract
import com.glia.widgets.chat.ChatManager
-import com.glia.widgets.chat.ChatType
import com.glia.widgets.chat.ChatView
+import com.glia.widgets.chat.Intention
import com.glia.widgets.chat.domain.DecideOnQueueingUseCase
import com.glia.widgets.chat.domain.GliaSendMessagePreviewUseCase
import com.glia.widgets.chat.domain.GliaSendMessageUseCase
@@ -39,14 +40,13 @@ import com.glia.widgets.chat.model.VisitorChatItem
import com.glia.widgets.core.dialog.DialogContract
import com.glia.widgets.core.dialog.domain.ConfirmationDialogLinksUseCase
import com.glia.widgets.core.dialog.domain.IsShowOverlayPermissionRequestDialogUseCase
+import com.glia.widgets.core.dialog.model.LeaveDialogAction
import com.glia.widgets.core.dialog.model.Link
import com.glia.widgets.core.engagement.domain.ConfirmationDialogUseCase
-import com.glia.widgets.core.engagement.domain.SetEngagementConfigUseCase
import com.glia.widgets.core.engagement.domain.UpdateOperatorDefaultImageUrlUseCase
import com.glia.widgets.core.fileupload.domain.AddFileAttachmentsObserverUseCase
import com.glia.widgets.core.fileupload.domain.AddFileToAttachmentAndUploadUseCase
import com.glia.widgets.core.fileupload.domain.GetFileAttachmentsUseCase
-import com.glia.widgets.core.fileupload.domain.RemoveFileAttachmentObserverUseCase
import com.glia.widgets.core.fileupload.domain.RemoveFileAttachmentUseCase
import com.glia.widgets.core.fileupload.domain.SupportedFileCountCheckUseCase
import com.glia.widgets.core.fileupload.model.LocalAttachment
@@ -54,8 +54,10 @@ import com.glia.widgets.core.notification.domain.CallNotificationUseCase
import com.glia.widgets.core.permissions.domain.RequestNotificationPermissionIfPushNotificationsSetUpUseCase
import com.glia.widgets.core.permissions.domain.WithCameraPermissionUseCase
import com.glia.widgets.core.permissions.domain.WithReadWritePermissionsUseCase
-import com.glia.widgets.core.secureconversations.domain.GetAvailableQueueIdsForSecureMessagingUseCase
-import com.glia.widgets.core.secureconversations.domain.IsSecureEngagementUseCase
+import com.glia.widgets.core.secureconversations.domain.IsMessagingAvailableUseCase
+import com.glia.widgets.core.secureconversations.domain.ManageSecureMessagingStatusUseCase
+import com.glia.widgets.core.secureconversations.domain.SecureConversationTopBannerVisibilityUseCase
+import com.glia.widgets.core.secureconversations.domain.SetLeaveSecureConversationDialogVisibleUseCase
import com.glia.widgets.di.Dependencies
import com.glia.widgets.engagement.EngagementUpdateState
import com.glia.widgets.engagement.ScreenSharingState
@@ -65,11 +67,12 @@ import com.glia.widgets.engagement.domain.EndEngagementUseCase
import com.glia.widgets.engagement.domain.EngagementStateUseCase
import com.glia.widgets.engagement.domain.EnqueueForEngagementUseCase
import com.glia.widgets.engagement.domain.IsCurrentEngagementCallVisualizerUseCase
-import com.glia.widgets.engagement.domain.IsQueueingOrEngagementUseCase
+import com.glia.widgets.engagement.domain.IsQueueingOrLiveEngagementUseCase
import com.glia.widgets.engagement.domain.OperatorMediaUseCase
import com.glia.widgets.engagement.domain.OperatorTypingUseCase
import com.glia.widgets.engagement.domain.ReleaseResourcesUseCase
import com.glia.widgets.engagement.domain.ScreenSharingUseCase
+import com.glia.widgets.entrywidget.EntryWidgetContract
import com.glia.widgets.filepreview.domain.usecase.DownloadFileUseCase
import com.glia.widgets.filepreview.domain.usecase.IsFileReadyForPreviewUseCase
import com.glia.widgets.helper.Logger
@@ -86,8 +89,8 @@ import com.glia.widgets.view.MinimizeHandler
import com.glia.widgets.webbrowser.domain.GetUrlFromLinkUseCase
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.disposables.CompositeDisposable
+import io.reactivex.rxjava3.functions.Consumer
import io.reactivex.rxjava3.schedulers.Schedulers
-import java.util.Observer
internal class ChatController(
private val callTimer: TimeCounter,
@@ -101,7 +104,6 @@ internal class ChatController(
private val endEngagementUseCase: EndEngagementUseCase,
private val addFileToAttachmentAndUploadUseCase: AddFileToAttachmentAndUploadUseCase,
private val addFileAttachmentsObserverUseCase: AddFileAttachmentsObserverUseCase,
- private val removeFileAttachmentObserverUseCase: RemoveFileAttachmentObserverUseCase,
private val getFileAttachmentsUseCase: GetFileAttachmentsUseCase,
private val removeFileAttachmentUseCase: RemoveFileAttachmentUseCase,
private val supportedFileCountCheckUseCase: SupportedFileCountCheckUseCase,
@@ -111,9 +113,7 @@ internal class ChatController(
private val siteInfoUseCase: SiteInfoUseCase,
private val isFromCallScreenUseCase: IsFromCallScreenUseCase,
private val updateFromCallScreenUseCase: UpdateFromCallScreenUseCase,
- private val isSecureEngagementUseCase: IsSecureEngagementUseCase,
- private val engagementConfigUseCase: SetEngagementConfigUseCase,
- private val getAvailableQueueIdsForSecureMessagingUseCase: GetAvailableQueueIdsForSecureMessagingUseCase,
+ private val manageSecureMessagingStatusUseCase: ManageSecureMessagingStatusUseCase,
private val isCurrentEngagementCallVisualizerUseCase: IsCurrentEngagementCallVisualizerUseCase,
private val isFileReadyForPreviewUseCase: IsFileReadyForPreviewUseCase,
private val determineGvaButtonTypeUseCase: DetermineGvaButtonTypeUseCase,
@@ -125,7 +125,7 @@ internal class ChatController(
private val engagementStateUseCase: EngagementStateUseCase,
private val operatorMediaUseCase: OperatorMediaUseCase,
private val acceptMediaUpgradeOfferUseCase: AcceptMediaUpgradeOfferUseCase,
- private val isQueueingOrEngagementUseCase: IsQueueingOrEngagementUseCase,
+ private val isQueueingOrLiveEngagementUseCase: IsQueueingOrLiveEngagementUseCase,
private val enqueueForEngagementUseCase: EnqueueForEngagementUseCase,
private val decideOnQueueingUseCase: DecideOnQueueingUseCase,
private val screenSharingUseCase: ScreenSharingUseCase,
@@ -135,7 +135,10 @@ internal class ChatController(
private val withReadWritePermissionsUseCase: WithReadWritePermissionsUseCase,
private val requestNotificationPermissionIfPushNotificationsSetUpUseCase: RequestNotificationPermissionIfPushNotificationsSetUpUseCase,
private val releaseResourcesUseCase: ReleaseResourcesUseCase,
- private val getUrlFromLinkUseCase: GetUrlFromLinkUseCase
+ private val getUrlFromLinkUseCase: GetUrlFromLinkUseCase,
+ private val isMessagingAvailableUseCase: IsMessagingAvailableUseCase,
+ private val shouldShowTopBannerUseCase: SecureConversationTopBannerVisibilityUseCase,
+ private val setLeaveSecureConversationDialogVisibleUseCase: SetLeaveSecureConversationDialogVisibleUseCase
) : ChatContract.Controller {
private var backClickedListener: ChatView.OnBackClickedListener? = null
private var view: ChatContract.View? = null
@@ -180,10 +183,10 @@ internal class ChatController(
@Volatile
private var isChatViewPaused = false
- private val fileAttachmentObserver = Observer { _, _ ->
+ private val fileAttachmentCallback = Consumer> { attachments ->
view?.apply {
- emitUploadAttachments(getFileAttachmentsUseCase())
emitViewState {
+ emitUploadAttachments(attachments)
chatState.setShowSendButton(isShowSendButtonUseCase(chatState.lastTypedText))
.setIsAttachmentButtonEnabled(supportedFileCountCheckUseCase())
}
@@ -193,9 +196,7 @@ internal class ChatController(
@Volatile
private var chatState: ChatState
- private val isSecureEngagement get() = isSecureEngagementUseCase()
-
- private val isQueueingOrOngoingEngagement get() = isQueueingOrEngagementUseCase()
+ private val isQueueingOrOngoingEngagement get() = isQueueingOrLiveEngagementUseCase()
override val isChatVisible: Boolean
get() = chatState.isVisible
@@ -225,22 +226,64 @@ internal class ChatController(
screenSharingUseCase.end()
}
- override fun initChat(companyName: String?, queueIds: List?, visitorContextAssetId: String?, chatType: ChatType) {
- engagementConfigUseCase(chatType, queueIds ?: emptyList())
+ override fun initChat(intention: Intention) {
updateOperatorDefaultImageUrlUseCase()
- ensureSecureMessagingAvailable()
+ when (intention) {
+ Intention.RETURN_TO_CHAT -> returnToChat()
+ Intention.SC_DIALOG_START_AUDIO -> initLeaveCurrentConversationDialog(LeaveDialogAction.AUDIO)
+ Intention.SC_DIALOG_START_VIDEO -> initLeaveCurrentConversationDialog(LeaveDialogAction.VIDEO)
+ Intention.SC_DIALOG_ENQUEUE_FOR_TEXT -> initLeaveCurrentConversationDialog(LeaveDialogAction.LIVE_CHAT)
+ Intention.SC_CHAT -> initSecureMessaging()
+ Intention.LIVE_CHAT -> initLiveChat()
+ }
+ }
- if (chatState.integratorChatStarted || dialogController.isShowingUnexpectedErrorDialog) {
- if (isSecureEngagement) {
- emitViewState { chatState.setSecureMessagingState() }
- }
- chatManager.onChatAction(ChatManager.Action.ChatRestored)
- return
+ private fun returnToChat() {
+ if (chatState.isInitialized) {
+ restoreChat()
+ } else {
+ initLiveChat()
}
+ }
+
+ private fun initLeaveCurrentConversationDialog(action: LeaveDialogAction) {
+ setLeaveSecureConversationDialogVisibleUseCase(true)
+ initSecureMessaging()
+ dialogController.showLeaveCurrentConversationDialog(action)
+ }
- emitViewState { chatState.initChat(companyName, queueIds, visitorContextAssetId) }
+ private fun initSecureMessaging() {
initChatManager()
+ emitViewState { chatState.initChat().setSecureMessagingState() }
+ ensureSecureMessagingAvailable()
+ observeTopBannerUseCase()
+ }
+
+ private fun observeTopBannerUseCase() {
+ disposable.add(shouldShowTopBannerUseCase().subscribe(
+ { result ->
+ result.getOrNull()?.let { shouldBeVisible ->
+ emitViewState {
+ chatState.setSecureConversationsTopBannerVisibility(shouldBeVisible && manageSecureMessagingStatusUseCase.shouldBehaveAsSecureMessaging)
+ }
+ } ?: run {
+ Logger.w(TAG, "Failed to get secure messaging top banner visibility flag.\n ${result.exceptionOrNull()}")
+ }
+ },
+ { error ->
+ Logger.w(TAG, "Secure messaging top banner visibility flag observable failed.\n $error")
+ }
+ ))
+ }
+
+ private fun initLiveChat() {
+ initChatManager()
+ emitViewState { chatState.initChat().setLiveChatState() }
+ }
+
+ override fun restoreChat() {
+ chatManager.onChatAction(ChatManager.Action.ChatRestored)
}
private fun subscribeToEngagement() {
@@ -250,26 +293,30 @@ internal class ChatController(
}
private fun ensureSecureMessagingAvailable() {
- if (!isSecureEngagement) return
+ disposable.add(isMessagingAvailableUseCase().subscribe(::handleMessagingAvailableResult))
+ }
- disposable.add(
- getAvailableQueueIdsForSecureMessagingUseCase().subscribe({
- if (it.result != null) {
- Logger.d(TAG, "Messaging is available")
- engagementConfigUseCase(ChatType.SECURE_MESSAGING, it.result)
- } else {
- Logger.d(TAG, "Messaging is unavailable")
- dialogController.showMessageCenterUnavailableDialog()
- }
- }, {
- Logger.e(TAG, "Checking for Messaging availability failed", it)
- dialogController.showUnexpectedErrorDialog()
- })
- )
+ private fun handleMessagingAvailableResult(result: Result) {
+ result.onFailure { error ->
+ Logger.e(TAG, "Checking for Messaging availability failed", error)
+ dialogController.showUnexpectedErrorDialog()
+ }
+
+ result.onSuccess { isMessagingAvailable ->
+ if (!isMessagingAvailable && manageSecureMessagingStatusUseCase.shouldBehaveAsSecureMessaging) {
+ Logger.d(TAG, "Messaging is unavailable")
+ emitViewState { chatState.setSecureMessagingUnavailable() }
+ } else {
+ Logger.d(TAG, "Messaging is available")
+ emitViewState { chatState.setSecureMessagingAvailable() }
+ }
+ }
}
private fun prepareChatComponents() {
- addFileAttachmentsObserverUseCase.execute(fileAttachmentObserver)
+ disposable.add(
+ addFileAttachmentsObserverUseCase().subscribe(fileAttachmentCallback)
+ )
minimizeHandler.addListener { minimizeView() }
timerStatusListener?.also { callTimer.removeFormattedValueListener(it) }
val newTimerListener = createNewTimerCallback()
@@ -279,7 +326,7 @@ internal class ChatController(
}
override fun onEngagementConfirmationDialogRequested() {
- if (isQueueingOrEngagementUseCase()) return
+ if (isQueueingOrLiveEngagementUseCase()) return
view?.showEngagementConfirmationDialog()
}
@@ -302,14 +349,12 @@ internal class ChatController(
override fun onLiveObservationDialogRejected() {
Logger.d(TAG, "onLiveObservationDialogRejected")
- stop()
+ endChat()
dialogController.dismissDialogs()
}
private fun enqueueForEngagement() {
- requestNotificationPermissionIfPushNotificationsSetUpUseCase {
- enqueueForEngagementUseCase(queueIds = chatState.queueIds, visitorContextAssetId = chatState.visitorContextAssetId)
- }
+ requestNotificationPermissionIfPushNotificationsSetUpUseCase(enqueueForEngagementUseCase::invoke)
}
@Synchronized
@@ -324,7 +369,6 @@ internal class ChatController(
override fun onDestroy(retain: Boolean) {
Logger.d(TAG, "onDestroy, retain:$retain")
- dialogController.dismissMessageCenterUnavailableDialog()
// view is accessed from multiple threads
// and must be protected from race condition
@@ -335,7 +379,6 @@ internal class ChatController(
timerStatusListener = null
callTimer.clear()
minimizeHandler.clear()
- removeFileAttachmentObserverUseCase(fileAttachmentObserver)
chatManager.reset()
}
}
@@ -447,19 +490,19 @@ internal class ChatController(
override fun noMoreOperatorsAvailableDismissed() {
Logger.d(TAG, "noMoreOperatorsAvailableDismissed")
- stop()
+ endChat()
dialogController.dismissCurrentDialog()
}
override fun unexpectedErrorDialogDismissed() {
Logger.d(TAG, "unexpectedErrorDialogDismissed")
- stop()
+ endChat()
dialogController.dismissCurrentDialog()
}
override fun endEngagementDialogYesClicked() {
Logger.d(TAG, "endEngagementDialogYesClicked")
- stop()
+ endChat()
dialogController.dismissDialogs()
}
@@ -543,7 +586,8 @@ internal class ChatController(
State.StartedOmniCore -> newEngagementLoaded()
is State.Update -> handleEngagementStateUpdate(state.updateState)
is State.PreQueuing, is State.Queuing -> queueForEngagementStarted()
- is State.QueueUnstaffed, is State.UnexpectedErrorHappened, is State.QueueingCanceled -> emitViewState { chatState.stop() }
+ is State.QueueUnstaffed, is State.UnexpectedErrorHappened, is State.QueueingCanceled -> emitViewState { chatState.chatUnavailableState() }
+ State.TransferredToSecureConversation -> onTransferredToSecureConversation()
else -> {
// no op
@@ -551,6 +595,12 @@ internal class ChatController(
}
}
+ private fun onTransferredToSecureConversation() {
+ emitViewState { chatState.setSecureMessagingState().setSecureMessagingAvailable() }
+ ensureSecureMessagingAvailable()
+ observeTopBannerUseCase()
+ }
+
private fun handleEngagementStateUpdate(state: EngagementUpdateState) {
when (state) {
is EngagementUpdateState.Ongoing -> onEngagementOngoing(state.operator)
@@ -604,14 +654,14 @@ internal class ChatController(
private fun error(error: String) {
Logger.e(TAG, error)
dialogController.showUnexpectedErrorDialog()
- emitViewState { chatState.stop() }
+ emitViewState { chatState.chatUnavailableState() }
}
private fun viewInitPreQueueing() {
if (isQueueingOrOngoingEngagement) return
Logger.d(TAG, "viewInitPreQueueing")
- chatManager.onChatAction(ChatManager.Action.QueuingStarted(chatState.companyName.orEmpty()))
+ chatManager.onChatAction(ChatManager.Action.QueuingStarted)
confirmationDialogUseCase { shouldShow ->
if (shouldShow) {
dialogController.showEngagementConfirmationDialog()
@@ -632,28 +682,24 @@ internal class ChatController(
private fun operatorConnected(formattedOperatorName: String, profileImgUrl: String?) {
chatManager.onChatAction(
- ChatManager.Action.OperatorConnected(
- chatState.companyName.orEmpty(), formattedOperatorName, profileImgUrl
- )
+ ChatManager.Action.OperatorConnected(formattedOperatorName, profileImgUrl)
)
emitViewState { chatState.operatorConnected(formattedOperatorName, profileImgUrl).setLiveChatState() }
}
private fun operatorChanged(formattedOperatorName: String, profileImgUrl: String?) {
chatManager.onChatAction(
- ChatManager.Action.OperatorJoined(
- chatState.companyName.orEmpty(), formattedOperatorName, profileImgUrl
- )
+ ChatManager.Action.OperatorJoined(formattedOperatorName, profileImgUrl)
)
- emitViewState { chatState.operatorConnected(formattedOperatorName, profileImgUrl) }
+ emitViewState { chatState.operatorConnected(formattedOperatorName, profileImgUrl).setLiveChatState() }
}
- private fun stop() {
+ private fun endChat() {
Logger.d(TAG, "Stop, engagement ended")
endEngagementUseCase()
chatManager.reset()
mediaUpgradeDisposable.clear()
- emitViewState { chatState.stop() }
+ emitViewState { chatState.chatUnavailableState() }
}
private fun addQuickReplyButtons(options: List) {
@@ -748,18 +794,22 @@ internal class ChatController(
private fun onHistoryLoaded(hasHistory: Boolean) {
Logger.d(TAG, "historyLoaded")
+ val isSecureEngagement = manageSecureMessagingStatusUseCase.shouldBehaveAsSecureMessaging
+
if (!hasHistory) {
if (!isSecureEngagement && !isQueueingOrOngoingEngagement) {
viewInitPreQueueing()
} else {
- Logger.d(TAG, "Opened empty Secure Conversations chat")
+ Logger.d(TAG, "Opened empty Secure Messaging chat")
}
}
when {
- isSecureEngagement -> emitViewState { chatState.setSecureMessagingState() }
- isQueueingOrEngagementUseCase.hasOngoingEngagement -> emitViewState { chatState.engagementStarted() }
- else -> emitViewState { chatState.historyLoaded() }
+ isSecureEngagement -> { /* to prevent calling chatState.liveChatHistoryLoaded() */
+ }
+
+ isQueueingOrLiveEngagementUseCase.hasOngoingLiveEngagement -> emitViewState { chatState.engagementStarted() }
+ else -> emitViewState { chatState.liveChatHistoryLoaded() }
}
prepareChatComponents()
@@ -807,7 +857,7 @@ internal class ChatController(
}
private fun onAttachmentReceived(file: LocalAttachment) {
- addFileToAttachmentAndUploadUseCase.execute(file, object : AddFileToAttachmentAndUploadUseCase.Listener {
+ addFileToAttachmentAndUploadUseCase(file, object : AddFileToAttachmentAndUploadUseCase.Listener {
override fun onFinished() {
Logger.d(TAG, "fileUploadFinished")
//We need this file locally, so clearing only file uri reference
@@ -907,4 +957,38 @@ internal class ChatController(
override fun onContentChosen(uri: Uri) {
uriToFileAttachmentUseCase(uri)?.also(::onAttachmentReceived)
}
+
+ override fun leaveCurrentConversationDialogLeaveClicked(action: LeaveDialogAction) {
+ dialogController.dismissCurrentDialog()
+
+ when (action) {
+ LeaveDialogAction.LIVE_CHAT -> leaveCurrentConversationAndStartEnqueueing()
+ LeaveDialogAction.VIDEO -> view?.launchCall(Engagement.MediaType.VIDEO)
+ LeaveDialogAction.AUDIO -> view?.launchCall(Engagement.MediaType.AUDIO)
+ }
+
+ setLeaveSecureConversationDialogVisibleUseCase(false)
+ }
+
+ override fun onScTopBannerItemClicked(itemType: EntryWidgetContract.ItemType) {
+ when (itemType) {
+ EntryWidgetContract.ItemType.AudioCall -> dialogController.showLeaveCurrentConversationDialog(LeaveDialogAction.AUDIO)
+ EntryWidgetContract.ItemType.Chat -> dialogController.showLeaveCurrentConversationDialog(LeaveDialogAction.LIVE_CHAT)
+ EntryWidgetContract.ItemType.VideoCall -> dialogController.showLeaveCurrentConversationDialog(LeaveDialogAction.VIDEO)
+ else -> {
+ /*no op*/
+ }
+ }
+ }
+
+ private fun leaveCurrentConversationAndStartEnqueueing() {
+ manageSecureMessagingStatusUseCase.updateSecureMessagingStatus(false)
+ emitViewState { chatState.setLiveChatState() }
+ viewInitPreQueueing()
+ }
+
+ override fun leaveCurrentConversationDialogStayClicked() {
+ dialogController.dismissCurrentDialog()
+ setLeaveSecureConversationDialogVisibleUseCase(false)
+ }
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/data/GliaChatRepository.java b/widgetssdk/src/main/java/com/glia/widgets/chat/data/GliaChatRepository.java
index 4725d6e30..5e83647f6 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/data/GliaChatRepository.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/data/GliaChatRepository.java
@@ -11,6 +11,7 @@
import com.glia.widgets.chat.domain.GliaSendMessageUseCase.Listener;
import com.glia.widgets.di.GliaCore;
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -27,7 +28,7 @@ public GliaChatRepository(GliaCore gliaCore) {
* @hide
*/
public interface HistoryLoadedListener {
- void loaded(ChatMessage[] messages, Throwable error);
+ void loaded(List messages, Throwable error);
}
public void loadHistory(HistoryLoadedListener historyLoadedListener) {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaLoadHistoryUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaLoadHistoryUseCase.kt
index 2fc0b5ca4..d4ab999b7 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaLoadHistoryUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaLoadHistoryUseCase.kt
@@ -5,20 +5,18 @@ import com.glia.widgets.core.engagement.domain.MapOperatorUseCase
import com.glia.widgets.core.engagement.domain.model.ChatHistoryResponse
import com.glia.widgets.core.engagement.domain.model.ChatMessageInternal
import com.glia.widgets.core.secureconversations.SecureConversationsRepository
-import com.glia.widgets.core.secureconversations.domain.GetUnreadMessagesCountWithTimeoutUseCase
-import com.glia.widgets.core.secureconversations.domain.IsSecureEngagementUseCase
+import com.glia.widgets.core.secureconversations.domain.ManageSecureMessagingStatusUseCase
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.core.Single
internal class GliaLoadHistoryUseCase(
private val gliaChatRepository: GliaChatRepository,
private val secureConversationsRepository: SecureConversationsRepository,
- private val isSecureEngagementUseCase: IsSecureEngagementUseCase,
- private val mapOperatorUseCase: MapOperatorUseCase,
- private val getUnreadMessagesCountUseCase: GetUnreadMessagesCountWithTimeoutUseCase
+ private val shouldUseSecureMessagingApis: ManageSecureMessagingStatusUseCase,
+ private val mapOperatorUseCase: MapOperatorUseCase
) {
- private val isSecureEngagement get() = isSecureEngagementUseCase()
+ private val isSecureEngagement get() = shouldUseSecureMessagingApis.shouldUseSecureMessagingEndpoints
operator fun invoke(): Single = if (isSecureEngagement) {
loadHistoryWithNewMessagesCount()
@@ -28,11 +26,11 @@ internal class GliaLoadHistoryUseCase(
private fun loadHistoryWithNewMessagesCount() = Single.zip(
loadHistoryAndMapOperator(),
- getUnreadMessagesCountUseCase()
+ secureConversationsRepository.unreadMessagesCountObservable.firstOrError()
) { messages, count -> ChatHistoryResponse(messages, count) }
private fun loadHistoryAndMapOperator(): Single> = loadHistory()
- .flatMapPublisher { Flowable.fromArray(*it) }
+ .flatMapPublisher { Flowable.fromIterable(it) }
.concatMapSingle { mapOperatorUseCase(chatMessage = it) }
.toSortedList(Comparator.comparingLong { it.chatMessage.timestamp })
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaSendMessageUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaSendMessageUseCase.kt
index f6d90ba73..fc5356238 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaSendMessageUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/GliaSendMessageUseCase.kt
@@ -9,20 +9,18 @@ import com.glia.widgets.chat.data.GliaChatRepository
import com.glia.widgets.chat.model.VisitorAttachmentItem
import com.glia.widgets.chat.model.VisitorChatItem
import com.glia.widgets.chat.model.VisitorMessageItem
-import com.glia.widgets.core.engagement.GliaEngagementConfigRepository
import com.glia.widgets.core.fileupload.FileAttachmentRepository
import com.glia.widgets.core.fileupload.model.LocalAttachment
import com.glia.widgets.core.secureconversations.SecureConversationsRepository
-import com.glia.widgets.core.secureconversations.domain.IsSecureEngagementUseCase
+import com.glia.widgets.core.secureconversations.domain.ManageSecureMessagingStatusUseCase
import com.glia.widgets.engagement.domain.IsOperatorPresentUseCase
internal class GliaSendMessageUseCase(
private val chatRepository: GliaChatRepository,
private val fileAttachmentRepository: FileAttachmentRepository,
private val isOperatorPresentUseCase: IsOperatorPresentUseCase,
- private val engagementConfigRepository: GliaEngagementConfigRepository,
private val secureConversationsRepository: SecureConversationsRepository,
- private val isSecureEngagementUseCase: IsSecureEngagementUseCase
+ private val shouldUseSecureMessagingApis: ManageSecureMessagingStatusUseCase
) {
interface Listener {
fun messageSent(message: VisitorMessage?)
@@ -34,11 +32,11 @@ internal class GliaSendMessageUseCase(
}
private val isSecureEngagement: Boolean
- get() = isSecureEngagementUseCase()
+ get() = shouldUseSecureMessagingApis.shouldUseSecureMessagingEndpoints
private fun sendMessage(payload: SendMessagePayload, listener: Listener) {
if (isSecureEngagement) {
- secureConversationsRepository.send(payload, engagementConfigRepository.queueIds, listener)
+ secureConversationsRepository.send(payload, listener)
} else {
chatRepository.sendMessage(payload, listener)
}
@@ -46,7 +44,7 @@ internal class GliaSendMessageUseCase(
fun execute(message: String, listener: Listener) {
val localAttachments: List? = fileAttachmentRepository
- .readyToSendLocalAttachments
+ .getReadyToSendFileAttachments()
.filter { it.engagementFile != null }
.takeIf { it.isNotEmpty() }
@@ -82,7 +80,7 @@ internal class GliaSendMessageUseCase(
val messageItem = VisitorMessageItem(payload.content, payload.messageId)
listener.onMessagePrepared(messageItem, payload)
when {
- isSecureEngagement -> secureConversationsRepository.send(payload, engagementConfigRepository.queueIds, listener)
+ isSecureEngagement -> secureConversationsRepository.send(payload, listener)
isOperatorOnline -> chatRepository.sendMessage(payload, listener)
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/IsShowSendButtonUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/IsShowSendButtonUseCase.kt
index f070009e0..c5601db0e 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/IsShowSendButtonUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/IsShowSendButtonUseCase.kt
@@ -1,17 +1,17 @@
package com.glia.widgets.chat.domain
import com.glia.widgets.core.fileupload.FileAttachmentRepository
-import com.glia.widgets.core.secureconversations.domain.IsSecureEngagementUseCase
-import com.glia.widgets.engagement.domain.IsQueueingOrEngagementUseCase
+import com.glia.widgets.core.secureconversations.domain.ManageSecureMessagingStatusUseCase
+import com.glia.widgets.engagement.domain.IsQueueingOrLiveEngagementUseCase
internal class IsShowSendButtonUseCase(
- private val isQueueingOrEngagementUseCase: IsQueueingOrEngagementUseCase,
+ private val isQueueingOrLiveEngagementUseCase: IsQueueingOrLiveEngagementUseCase,
private val fileAttachmentRepository: FileAttachmentRepository,
- private val isSecureEngagementUseCase: IsSecureEngagementUseCase
+ private val manageSecureMessagingStatusUseCase: ManageSecureMessagingStatusUseCase
) {
operator fun invoke(message: String?): Boolean {
return when {
- isSecureEngagementUseCase() -> hasText(message) || hadReadyToSendUnsentAttachments()
+ manageSecureMessagingStatusUseCase.shouldBehaveAsSecureMessaging -> hasText(message) || hadReadyToSendUnsentAttachments()
else -> hasText(message) || hasEngagementOngoingAndReadyToSendUnsentAttachments()
}
}
@@ -21,10 +21,10 @@ internal class IsShowSendButtonUseCase(
}
private fun hadReadyToSendUnsentAttachments(): Boolean {
- return fileAttachmentRepository.readyToSendLocalAttachments.isNotEmpty()
+ return fileAttachmentRepository.getReadyToSendFileAttachments().isNotEmpty()
}
private fun hasEngagementOngoingAndReadyToSendUnsentAttachments(): Boolean {
- return isQueueingOrEngagementUseCase.hasOngoingEngagement && hadReadyToSendUnsentAttachments()
+ return isQueueingOrLiveEngagementUseCase.hasOngoingLiveEngagement && hadReadyToSendUnsentAttachments()
}
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/SendUnsentMessagesUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/SendUnsentMessagesUseCase.kt
index aa5216a2d..75e0f01fc 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/domain/SendUnsentMessagesUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/domain/SendUnsentMessagesUseCase.kt
@@ -5,16 +5,14 @@ import com.glia.androidsdk.RequestCallback
import com.glia.androidsdk.chat.SendMessagePayload
import com.glia.androidsdk.chat.VisitorMessage
import com.glia.widgets.chat.data.GliaChatRepository
-import com.glia.widgets.core.engagement.GliaEngagementConfigRepository
import com.glia.widgets.core.secureconversations.SecureConversationsRepository
-import com.glia.widgets.core.secureconversations.domain.IsSecureEngagementUseCase
+import com.glia.widgets.core.secureconversations.domain.ManageSecureMessagingStatusUseCase
internal class SendUnsentMessagesUseCase(
private val chatRepository: GliaChatRepository,
private val secureConversationsRepository: SecureConversationsRepository,
- private val engagementConfigRepository: GliaEngagementConfigRepository,
- private val isSecureEngagementUseCase: IsSecureEngagementUseCase
+ private val shouldUseSecureMessagingApis: ManageSecureMessagingStatusUseCase
) {
operator fun invoke(
payload: SendMessagePayload,
@@ -32,8 +30,8 @@ internal class SendUnsentMessagesUseCase(
}
private fun sendMessage(payload: SendMessagePayload, callback: RequestCallback) {
- if (isSecureEngagementUseCase()) {
- secureConversationsRepository.send(payload, engagementConfigRepository.queueIds, callback)
+ if (shouldUseSecureMessagingApis.shouldUseSecureMessagingEndpoints) {
+ secureConversationsRepository.send(payload, callback)
} else {
chatRepository.sendMessage(payload, callback)
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/ChatItems.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/model/ChatItems.kt
index 15ce2c958..81338cfe9 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/model/ChatItems.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/model/ChatItems.kt
@@ -165,25 +165,19 @@ internal sealed class OperatorStatusItem : ChatItem(ChatAdapter.OPERATOR_STATUS_
override val id: String = "operator_status_item"
override val timestamp: Long = -1
- abstract val companyName: String?
-
- data class InQueue(override val companyName: String?) : OperatorStatusItem()
+ data object InQueue : OperatorStatusItem()
data class Connected(
- override val companyName: String?,
val operatorName: String,
val profileImgUrl: String?
) : OperatorStatusItem()
data class Joined(
- override val companyName: String?,
val operatorName: String,
val profileImgUrl: String?
) : OperatorStatusItem()
- object Transferring : OperatorStatusItem() {
- override val companyName: String? = null
- }
+ data object Transferring : OperatorStatusItem()
}
// Visitor
diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/model/ChatState.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/model/ChatState.kt
index 3ee59b76f..4f1805908 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/chat/model/ChatState.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/model/ChatState.kt
@@ -1,28 +1,28 @@
package com.glia.widgets.chat.model
internal data class ChatState(
- val integratorChatStarted: Boolean = false,
val isVisible: Boolean = false,
val isChatInBottom: Boolean = true,
val messagesNotSeen: Int = 0,
val formattedOperatorName: String? = null,
val operatorProfileImgUrl: String? = null,
- val companyName: String? = null,
- val queueIds: List? = null,
- val visitorContextAssetId: String? = null,
val isMediaUpgradeVide: Boolean? = null,
val chatInputMode: ChatInputMode = ChatInputMode.ENABLED_NO_ENGAGEMENT,
val lastTypedText: String = "",
val engagementRequested: Boolean = false,
val operatorStatusItem: OperatorStatusItem? = null,
- val showSendButton: Boolean = false,
+ val isSendButtonVisible: Boolean = false,
+ val isSendButtonEnabled: Boolean = false,
+ val isSecureConversationsUnavailableLabelVisible: Boolean = false,
+ val isSecureConversationsTopBannerVisible: Boolean = false,
val isAttachmentButtonEnabled: Boolean = false,
val isAttachmentButtonNeeded: Boolean = false,
val isOperatorTyping: Boolean = false,
val isAttachmentAllowed: Boolean = true,
val isSecureMessaging: Boolean = false,
val gvaQuickReplies: List = emptyList(),
- val isSharingScreen: Boolean = false
+ val isSharingScreen: Boolean = false,
+ val isInitialized: Boolean = false
) {
val isOperatorOnline: Boolean get() = formattedOperatorName != null
@@ -35,15 +35,13 @@ internal data class ChatState(
val isAttachmentButtonVisible: Boolean get() = isAttachmentButtonNeeded && isAttachmentAllowed
- fun initChat(companyName: String?, queueIds: List?, visitorContextAssetId: String?): ChatState = copy(
- integratorChatStarted = true,
- companyName = companyName,
- queueIds = queueIds,
- visitorContextAssetId = visitorContextAssetId,
+ fun initChat(): ChatState = copy(
isVisible = true,
- showSendButton = false,
+ isSendButtonVisible = false,
+ isSendButtonEnabled = true,
+ isAttachmentAllowed = true,
isAttachmentButtonEnabled = true,
- isAttachmentAllowed = true
+ isInitialized = true
)
fun queueingStarted(): ChatState = copy(
@@ -59,7 +57,12 @@ internal data class ChatState(
isAttachmentButtonNeeded = true
)
- fun setLiveChatState(): ChatState = copy(isSecureMessaging = false)
+ fun setLiveChatState(): ChatState = copy(
+ isSecureMessaging = false,
+ chatInputMode = ChatInputMode.ENABLED_NO_ENGAGEMENT,
+ isSecureConversationsUnavailableLabelVisible = false,
+ isSecureConversationsTopBannerVisible = false
+ )
fun allowSendAttachmentStateChanged(isAttachmentAllowed: Boolean): ChatState = copy(isAttachmentAllowed = isAttachmentAllowed)
@@ -75,7 +78,7 @@ internal data class ChatState(
engagementRequested = false,
operatorStatusItem = OperatorStatusItem.Transferring,
chatInputMode = ChatInputMode.DISABLED,
- showSendButton = false,
+ isSendButtonVisible = false,
isAttachmentButtonNeeded = false
)
@@ -89,7 +92,7 @@ internal data class ChatState(
isAttachmentButtonNeeded = true
)
- fun historyLoaded(): ChatState = copy(
+ fun liveChatHistoryLoaded(): ChatState = copy(
chatInputMode = ChatInputMode.ENABLED_NO_ENGAGEMENT,
isAttachmentButtonNeeded = false
)
@@ -104,17 +107,38 @@ internal data class ChatState(
fun messagesNotSeenChanged(messagesNotSeen: Int): ChatState = copy(messagesNotSeen = messagesNotSeen)
- fun setShowSendButton(isShow: Boolean): ChatState = copy(showSendButton = isShow)
+ fun setShowSendButton(isShow: Boolean): ChatState = copy(isSendButtonVisible = isShow)
+
+ fun setSecureMessagingUnavailable(): ChatState = copy(
+ isSecureConversationsUnavailableLabelVisible = true,
+ isAttachmentButtonNeeded = true,
+ isAttachmentButtonEnabled = false,
+ isSendButtonVisible = true,
+ isSendButtonEnabled = false,
+ chatInputMode = ChatInputMode.DISABLED
+ )
+
+ fun setSecureMessagingAvailable(): ChatState = copy(
+ isSecureConversationsUnavailableLabelVisible = false,
+ isAttachmentButtonNeeded = true,
+ isAttachmentButtonEnabled = true,
+ isSendButtonVisible = true,
+ isSendButtonEnabled = true,
+ chatInputMode = ChatInputMode.ENABLED
+ )
+
+ fun setSecureConversationsTopBannerVisibility(isVisible: Boolean): ChatState = copy(
+ isSecureConversationsTopBannerVisible = isVisible
+ )
fun setIsOperatorTyping(isOperatorTyping: Boolean): ChatState = copy(isOperatorTyping = isOperatorTyping)
fun setIsAttachmentButtonEnabled(isAttachmentButtonEnabled: Boolean): ChatState = copy(isAttachmentButtonEnabled = isAttachmentButtonEnabled)
- fun stop(): ChatState = copy(
+ fun chatUnavailableState(): ChatState = copy(
formattedOperatorName = null,
operatorProfileImgUrl = null,
isVisible = false,
- integratorChatStarted = false,
isAttachmentButtonNeeded = false,
isMediaUpgradeVide = null
)
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/callvisualizer/CallVisualizerManager.kt b/widgetssdk/src/main/java/com/glia/widgets/core/callvisualizer/CallVisualizerManager.kt
index d6152726c..f0acc6426 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/callvisualizer/CallVisualizerManager.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/callvisualizer/CallVisualizerManager.kt
@@ -3,11 +3,9 @@ package com.glia.widgets.core.callvisualizer
import android.annotation.SuppressLint
import android.content.Context
import android.view.View
-import com.glia.androidsdk.GliaException
import com.glia.widgets.callvisualizer.controller.CallVisualizerContract
import com.glia.widgets.core.callvisualizer.domain.CallVisualizer
import com.glia.widgets.core.callvisualizer.domain.VisitorCodeViewBuilderUseCase
-import com.glia.widgets.di.Dependencies
import com.glia.widgets.helper.unSafeSubscribe
internal class CallVisualizerManager(
@@ -23,8 +21,8 @@ internal class CallVisualizerManager(
callVisualizerController.showVisitorCodeDialog()
}
- override fun addVisitorContext(visitorContext: String) {
- callVisualizerController.saveVisitorContextAssetId(visitorContext)
+ override fun addVisitorContext(visitorContextAssetId: String) {
+ callVisualizerController.saveVisitorContextAssetId(visitorContextAssetId)
}
@SuppressLint("CheckResult")
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/callvisualizer/domain/VisitorCodeViewBuilderUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/callvisualizer/domain/VisitorCodeViewBuilderUseCase.kt
index 3fa2965c2..0ff613456 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/callvisualizer/domain/VisitorCodeViewBuilderUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/callvisualizer/domain/VisitorCodeViewBuilderUseCase.kt
@@ -1,8 +1,11 @@
package com.glia.widgets.core.callvisualizer.domain
import android.content.Context
+import com.glia.widgets.helper.wrapWithTheme
import com.glia.widgets.view.VisitorCodeView
internal class VisitorCodeViewBuilderUseCase {
- operator fun invoke(context: Context, closable: Boolean): VisitorCodeView = VisitorCodeView(context = context).apply { setClosable(closable) }
+ //Wrapping with Glia theme to make the Theme available for embedded view
+ operator fun invoke(context: Context, closable: Boolean): VisitorCodeView =
+ VisitorCodeView(context = context.wrapWithTheme()).apply { setClosable(closable) }
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/chathead/domain/IsDisplayBubbleInsideAppUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/chathead/domain/IsDisplayBubbleInsideAppUseCase.kt
index 9fbc2ed3d..70e435f52 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/chathead/domain/IsDisplayBubbleInsideAppUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/chathead/domain/IsDisplayBubbleInsideAppUseCase.kt
@@ -1,21 +1,21 @@
package com.glia.widgets.core.chathead.domain
-import com.glia.widgets.core.configuration.GliaSdkConfigurationManager
import com.glia.widgets.core.permissions.PermissionManager
import com.glia.widgets.engagement.domain.EngagementTypeUseCase
import com.glia.widgets.engagement.domain.IsCurrentEngagementCallVisualizerUseCase
-import com.glia.widgets.engagement.domain.IsQueueingOrEngagementUseCase
+import com.glia.widgets.engagement.domain.IsQueueingOrLiveEngagementUseCase
import com.glia.widgets.engagement.domain.ScreenSharingUseCase
+import com.glia.widgets.launcher.ConfigurationManager
internal class IsDisplayBubbleInsideAppUseCase(
- isQueueingOrEngagementUseCase: IsQueueingOrEngagementUseCase,
+ isQueueingOrLiveEngagementUseCase: IsQueueingOrLiveEngagementUseCase,
isCurrentEngagementCallVisualizerUseCase: IsCurrentEngagementCallVisualizerUseCase,
screenSharingUseCase: ScreenSharingUseCase,
permissionManager: PermissionManager,
- configurationManager: GliaSdkConfigurationManager,
+ configurationManager: ConfigurationManager,
engagementTypeUseCase: EngagementTypeUseCase
) : IsDisplayBubbleUseCase(
- isQueueingOrEngagementUseCase,
+ isQueueingOrLiveEngagementUseCase,
isCurrentEngagementCallVisualizerUseCase,
screenSharingUseCase,
permissionManager,
@@ -23,12 +23,12 @@ internal class IsDisplayBubbleInsideAppUseCase(
engagementTypeUseCase
) {
override fun isBubbleEnabled(): Boolean {
- return configurationManager.isEnableBubbleInsideApp
+ return configurationManager.enableBubbleInsideApp
}
override fun isShowBasedOnForegroundBackground(viewName: String?): Boolean {
return viewName != null && // App is in foreground
// Use only ChatHeadService instead of ChatHeadService + app bubble if bubble is enabled outside and inside
- (!configurationManager.isEnableBubbleOutsideApp || !configurationManager.isEnableBubbleInsideApp || !permissionManager.hasOverlayPermission())
+ (!configurationManager.enableBubbleOutsideApp || !configurationManager.enableBubbleInsideApp || !permissionManager.hasOverlayPermission())
}
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/chathead/domain/IsDisplayBubbleOutsideAppUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/chathead/domain/IsDisplayBubbleOutsideAppUseCase.kt
index 08ef94ec8..0f1e6854b 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/chathead/domain/IsDisplayBubbleOutsideAppUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/chathead/domain/IsDisplayBubbleOutsideAppUseCase.kt
@@ -1,14 +1,14 @@
package com.glia.widgets.core.chathead.domain
import com.glia.widgets.core.chathead.ChatHeadManager
-import com.glia.widgets.core.configuration.GliaSdkConfigurationManager
import com.glia.widgets.core.permissions.PermissionManager
import com.glia.widgets.engagement.domain.EngagementTypeUseCase
import com.glia.widgets.engagement.domain.IsCurrentEngagementCallVisualizerUseCase
-import com.glia.widgets.engagement.domain.IsQueueingOrEngagementUseCase
+import com.glia.widgets.engagement.domain.IsQueueingOrLiveEngagementUseCase
import com.glia.widgets.engagement.domain.ScreenSharingUseCase
import com.glia.widgets.helper.Logger
import com.glia.widgets.helper.TAG
+import com.glia.widgets.launcher.ConfigurationManager
/**
* This use case:
@@ -16,15 +16,15 @@ import com.glia.widgets.helper.TAG
* 2) starts or stops the chat head service (bubble outside the app)
*/
internal class IsDisplayBubbleOutsideAppUseCase(
- isQueueingOrEngagementUseCase: IsQueueingOrEngagementUseCase,
+ isQueueingOrLiveEngagementUseCase: IsQueueingOrLiveEngagementUseCase,
isCurrentEngagementCallVisualizerUseCase: IsCurrentEngagementCallVisualizerUseCase,
screenSharingUseCase: ScreenSharingUseCase,
private val chatHeadManager: ChatHeadManager,
permissionManager: PermissionManager,
- configurationManager: GliaSdkConfigurationManager,
+ configurationManager: ConfigurationManager,
engagementTypeUseCase: EngagementTypeUseCase
) : IsDisplayBubbleUseCase(
- isQueueingOrEngagementUseCase,
+ isQueueingOrLiveEngagementUseCase,
isCurrentEngagementCallVisualizerUseCase,
screenSharingUseCase,
permissionManager,
@@ -50,13 +50,13 @@ internal class IsDisplayBubbleOutsideAppUseCase(
// global device bubble is NOT turned off by integrator (turned ON by default)
// AND
// global device bubble is allowed by visitor (overlay permission).
- return configurationManager.isEnableBubbleOutsideApp && permissionManager.hasOverlayPermission()
+ return configurationManager.enableBubbleOutsideApp && permissionManager.hasOverlayPermission()
}
override fun isShowBasedOnForegroundBackground(viewName: String?): Boolean {
return viewName == null || // true when app is in background
// Use only ChatHeadService instead of ChatHeadService + app bubble if bubble is enabled outside and inside
- (configurationManager.isEnableBubbleOutsideApp && configurationManager.isEnableBubbleInsideApp && permissionManager.hasOverlayPermission())
+ (configurationManager.enableBubbleOutsideApp && configurationManager.enableBubbleInsideApp && permissionManager.hasOverlayPermission())
}
fun onDestroy() {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/chathead/domain/IsDisplayBubbleUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/chathead/domain/IsDisplayBubbleUseCase.kt
index 9b24c945a..8f04469a9 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/chathead/domain/IsDisplayBubbleUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/chathead/domain/IsDisplayBubbleUseCase.kt
@@ -3,22 +3,22 @@ package com.glia.widgets.core.chathead.domain
import com.glia.widgets.call.CallView
import com.glia.widgets.callvisualizer.EndScreenSharingView
import com.glia.widgets.chat.ChatView
-import com.glia.widgets.core.configuration.GliaSdkConfigurationManager
import com.glia.widgets.core.permissions.PermissionManager
import com.glia.widgets.engagement.domain.EngagementTypeUseCase
import com.glia.widgets.engagement.domain.IsCurrentEngagementCallVisualizerUseCase
-import com.glia.widgets.engagement.domain.IsQueueingOrEngagementUseCase
+import com.glia.widgets.engagement.domain.IsQueueingOrLiveEngagementUseCase
import com.glia.widgets.engagement.domain.ScreenSharingUseCase
-import com.glia.widgets.filepreview.ui.FilePreviewView
+import com.glia.widgets.filepreview.ui.ImagePreviewView
import com.glia.widgets.helper.DialogHolderView
+import com.glia.widgets.launcher.ConfigurationManager
import com.glia.widgets.messagecenter.MessageCenterView
internal abstract class IsDisplayBubbleUseCase(
- private val isQueueingOrEngagementUseCase: IsQueueingOrEngagementUseCase,
+ private val isQueueingOrLiveEngagementUseCase: IsQueueingOrLiveEngagementUseCase,
private val isCurrentEngagementCallVisualizerUseCase: IsCurrentEngagementCallVisualizerUseCase,
private val screenSharingUseCase: ScreenSharingUseCase,
val permissionManager: PermissionManager,
- internal val configurationManager: GliaSdkConfigurationManager,
+ internal val configurationManager: ConfigurationManager,
private val engagementTypeUseCase: EngagementTypeUseCase
) {
open operator fun invoke(viewName: String?): Boolean {
@@ -52,7 +52,7 @@ internal abstract class IsDisplayBubbleUseCase(
private fun isGliaView(viewName: String?): Boolean {
return viewName == ChatView::class.java.simpleName ||
viewName == CallView::class.java.simpleName ||
- viewName == FilePreviewView::class.java.simpleName ||
+ viewName == ImagePreviewView::class.java.simpleName ||
viewName == EndScreenSharingView::class.java.simpleName ||
viewName == MessageCenterView::class.java.simpleName ||
viewName == DialogHolderView::class.java.simpleName
@@ -60,8 +60,8 @@ internal abstract class IsDisplayBubbleUseCase(
private val isChatEngagementOrQueueingOngoing: Boolean get() = isChatEngagementOngoing || isChatQueueingOngoing
private val isMediaEngagementOrQueueingOngoing: Boolean get() = isMediaEngagementOngoing || isMediaQueueingOngoing
- private val isMediaQueueingOngoing: Boolean get() = isQueueingOrEngagementUseCase.isQueueingForMedia
+ private val isMediaQueueingOngoing: Boolean get() = isQueueingOrLiveEngagementUseCase.isQueueingForMedia
private val isMediaEngagementOngoing: Boolean get() = engagementTypeUseCase.isMediaEngagement
- private val isChatQueueingOngoing: Boolean get() = isQueueingOrEngagementUseCase.isQueueingForChat
+ private val isChatQueueingOngoing: Boolean get() = isQueueingOrLiveEngagementUseCase.isQueueingForLiveChat
private val isChatEngagementOngoing: Boolean get() = engagementTypeUseCase.isChatEngagement
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/chathead/domain/ResolveChatHeadNavigationUseCase.java b/widgetssdk/src/main/java/com/glia/widgets/core/chathead/domain/ResolveChatHeadNavigationUseCase.java
index 94eaa3602..3bc196e1a 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/chathead/domain/ResolveChatHeadNavigationUseCase.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/chathead/domain/ResolveChatHeadNavigationUseCase.java
@@ -2,22 +2,22 @@
import com.glia.widgets.core.callvisualizer.domain.IsCallVisualizerScreenSharingUseCase;
import com.glia.widgets.engagement.domain.EngagementTypeUseCase;
-import com.glia.widgets.engagement.domain.IsQueueingOrEngagementUseCase;
+import com.glia.widgets.engagement.domain.IsQueueingOrLiveEngagementUseCase;
/**
* @hide
*/
public class ResolveChatHeadNavigationUseCase {
- private final IsQueueingOrEngagementUseCase isQueueingOrEngagementUseCase;
+ private final IsQueueingOrLiveEngagementUseCase isQueueingOrLiveEngagementUseCase;
private final EngagementTypeUseCase engagementTypeUseCase;
private final IsCallVisualizerScreenSharingUseCase isCallVisualizerScreenSharingUseCase;
public ResolveChatHeadNavigationUseCase(
- IsQueueingOrEngagementUseCase isQueueingOrEngagementUseCase,
+ IsQueueingOrLiveEngagementUseCase isQueueingOrLiveEngagementUseCase,
EngagementTypeUseCase engagementTypeUseCase,
IsCallVisualizerScreenSharingUseCase isCallVisualizerScreenSharingUseCase
) {
- this.isQueueingOrEngagementUseCase = isQueueingOrEngagementUseCase;
+ this.isQueueingOrLiveEngagementUseCase = isQueueingOrLiveEngagementUseCase;
this.engagementTypeUseCase = engagementTypeUseCase;
this.isCallVisualizerScreenSharingUseCase = isCallVisualizerScreenSharingUseCase;
}
@@ -42,11 +42,11 @@ public enum Destinations {
}
private boolean isMediaQueueingOngoing() {
- return isQueueingOrEngagementUseCase.isQueueingForMedia();
+ return isQueueingOrLiveEngagementUseCase.isQueueingForMedia();
}
private boolean isMediaEngagementOngoing() {
- return isQueueingOrEngagementUseCase.getHasOngoingEngagement() && engagementTypeUseCase.isMediaEngagement();
+ return isQueueingOrLiveEngagementUseCase.getHasOngoingLiveEngagement() && engagementTypeUseCase.isMediaEngagement();
}
private boolean isSharingScreen() {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/configuration/EngagementConfiguration.kt b/widgetssdk/src/main/java/com/glia/widgets/core/configuration/EngagementConfiguration.kt
deleted file mode 100644
index e797c6451..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/core/configuration/EngagementConfiguration.kt
+++ /dev/null
@@ -1,126 +0,0 @@
-package com.glia.widgets.core.configuration
-
-import android.content.Intent
-import com.glia.androidsdk.screensharing.ScreenSharing
-import com.glia.widgets.GliaWidgets
-import com.glia.widgets.UiTheme
-import com.glia.widgets.chat.ChatType
-import com.glia.widgets.di.Dependencies
-import com.glia.widgets.helper.Logger.logDeprecatedMethodUse
-import com.glia.widgets.helper.TAG
-
-internal class EngagementConfiguration private constructor(builder: Builder) {
- val companyName: String?
- val queueIds: List?
- val contextAssetId: String?
- private val contextUrl: String?
- val runTimeTheme: UiTheme?
- get() = field ?: Dependencies.sdkConfigurationManager.uiTheme
- val screenSharingMode: ScreenSharing.Mode?
- get() = field ?: Dependencies.sdkConfigurationManager.screenSharingMode
- val chatType: ChatType?
- private val manualStringOverrideL: String?
-
- init {
- companyName = builder.companyName
- queueIds = builder.queueIds
- contextAssetId = builder.contextAssetId
- contextUrl = builder.contextUrl
- runTimeTheme = builder.runTimeTheme
- screenSharingMode = builder.screenSharingMode
- chatType = builder.chatType
- manualStringOverrideL = builder.manualLocaleOverride
- }
-
- @Deprecated("")
- fun getContextUrl(): String? {
- logDeprecatedMethodUse(TAG, "getContextUrl()")
- return contextUrl
- }
-
- class Builder {
- var companyName: String? = null
- var queueIds: List? = null
- var contextAssetId: String? = null
- var contextUrl: String? = null
- var runTimeTheme: UiTheme? = null
- var screenSharingMode: ScreenSharing.Mode? = null
- var chatType: ChatType? = null
- var manualLocaleOverride: String? = null
- fun companyName(companyName: String?): Builder {
- this.companyName = companyName
- return this
- }
-
- @Deprecated("")
- fun queueId(queueId: String?): Builder {
- this.queueIds = queueId?.let { listOf(queueId) }
- return this
- }
-
- fun queueIds(queueIds: List?): Builder {
- this.queueIds = queueIds
- return this
- }
-
- @Deprecated("")
- fun contextUrl(contextUrl: String?): Builder {
- logDeprecatedMethodUse(TAG, "contextUrl(String)")
- this.contextUrl = contextUrl
- return this
- }
-
- fun contextAssetId(contextAssetId: String?): Builder {
- this.contextAssetId = contextAssetId
- return this
- }
-
- @Deprecated(
- "While UiTheme can still be used for UI customization, we strongly encourage adopting remote configurations(GliaWidgetsConfig.Builder.setUiJsonRemoteConfig). " +
- "The remote configurations approach is more versatile and better suited for future development."
- )
- fun runTimeTheme(runTimeTheme: UiTheme?): Builder {
- this.runTimeTheme = runTimeTheme
- return this
- }
-
- fun screenSharingMode(screenSharingMode: ScreenSharing.Mode?): Builder {
- this.screenSharingMode = screenSharingMode
- return this
- }
-
- fun manualLocaleOverride(manualLocaleOverride: String?): Builder {
- this.manualLocaleOverride = manualLocaleOverride
- return this
- }
-
- fun intent(intent: Intent): Builder {
- companyName = Dependencies.sdkConfigurationManager.companyName
- val queueId = intent.getStringExtra(GliaWidgets.QUEUE_ID)
- queueIds = intent.getStringArrayListExtra(GliaWidgets.QUEUE_IDS)
- if (queueIds == null && queueId != null) {
- queueIds = listOf(queueId)
- }
- val tempTheme = intent.getParcelableExtra(GliaWidgets.UI_THEME)
- runTimeTheme = tempTheme ?: Dependencies.sdkConfigurationManager.uiTheme
- contextAssetId = intent.getStringExtra(GliaWidgets.CONTEXT_ASSET_ID)
- val tempMode =
- if (intent.hasExtra(GliaWidgets.SCREEN_SHARING_MODE)) intent.getSerializableExtra(
- GliaWidgets.SCREEN_SHARING_MODE
- ) as ScreenSharing.Mode? else Dependencies.sdkConfigurationManager.screenSharingMode
- screenSharingMode =
- tempMode ?: Dependencies.sdkConfigurationManager.screenSharingMode
- chatType =
- if (intent.hasExtra(GliaWidgets.CHAT_TYPE)) intent.getParcelableExtra(GliaWidgets.CHAT_TYPE) else DEFAULT_CHAT_TYPE
- return this
- }
-
- fun build(): EngagementConfiguration {
- return EngagementConfiguration(this)
- }
- }
-
- companion object {
- private val DEFAULT_CHAT_TYPE = ChatType.LIVE_CHAT
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/configuration/GliaSdkConfigurationManager.java b/widgetssdk/src/main/java/com/glia/widgets/core/configuration/GliaSdkConfigurationManager.java
deleted file mode 100644
index 766791e4d..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/core/configuration/GliaSdkConfigurationManager.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package com.glia.widgets.core.configuration;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-
-import com.glia.androidsdk.screensharing.ScreenSharing;
-import com.glia.widgets.GliaWidgetsConfig;
-import com.glia.widgets.UiTheme;
-import com.glia.widgets.di.Dependencies;
-import com.glia.widgets.helper.Logger;
-import com.glia.widgets.helper.ResourceProvider;
-
-import org.jetbrains.annotations.Nullable;
-
-/**
- * @hide
- */
-public class GliaSdkConfigurationManager {
-
- private static final String TAG = GliaSdkConfigurationManager.class.getSimpleName();
-
- private ScreenSharing.Mode screenSharingMode = null;
- private String companyName = null;
- private String legacyCompanyName = null;
- private boolean enableBubbleOutsideApp = true; // default values
- private boolean enableBubbleInsideApp = true; // default values
-
- private UiTheme uiTheme = null;
-
- public void fromConfiguration(@NonNull GliaWidgetsConfig configuration) {
- this.screenSharingMode = configuration.screenSharingMode;
- this.companyName = configuration.companyName;
- this.uiTheme = configuration.uiTheme;
-
- Boolean enableBubbleOutsideApp = configuration.enableBubbleOutsideApp;
- if (enableBubbleOutsideApp != null) {
- this.enableBubbleOutsideApp = enableBubbleOutsideApp;
- }
-
- Boolean enableBubbleInsideApp = configuration.enableBubbleInsideApp;
- if (enableBubbleInsideApp != null) {
- this.enableBubbleInsideApp = enableBubbleInsideApp;
- }
- }
-
- public boolean isEnableBubbleOutsideApp() {
- return this.enableBubbleOutsideApp;
- }
-
- public boolean isEnableBubbleInsideApp() {
- return this.enableBubbleInsideApp;
- }
-
- /**
- * @deprecated Should be removed together with GliaWidgetsConfig.USE_OVERLAY
- */
- @Deprecated
- public void setLegacyUseOverlay(boolean useOverlay) {
- Logger.logDeprecatedMethodUse(TAG, "setLegacyUseOverlay()");
- this.enableBubbleOutsideApp = useOverlay;
- this.enableBubbleInsideApp = useOverlay;
- }
-
- public void setLegacyCompanyName(String companyName) {
- this.legacyCompanyName = companyName;
- }
-
- public String getCompanyName() {
- if (companyName == null && legacyCompanyName != null) {
- // Legacy company name configuration method used before local default
- companyName = legacyCompanyName;
- }
- return companyName;
- }
-
- @VisibleForTesting
- public ResourceProvider getResourceProvider() {
- return Dependencies.getResourceProvider();
- }
-
- public void setCompanyName(String companyName) {
- this.companyName = companyName;
- }
-
- public UiTheme getUiTheme() {
- return uiTheme;
- }
-
- public void setUiTheme(UiTheme uiTheme) {
- this.uiTheme = uiTheme;
- }
-
- public ScreenSharing.Mode getScreenSharingMode() {
- return screenSharingMode;
- }
-
- public void setScreenSharingMode(ScreenSharing.Mode screenSharingMode) {
- this.screenSharingMode = screenSharingMode;
- }
-
- @Nullable
- public EngagementConfiguration buildEngagementConfiguration() {
- return new EngagementConfiguration.Builder()
- .companyName(companyName)
- .screenSharingMode(screenSharingMode)
- .runTimeTheme(uiTheme)
- .build();
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/dialog/DialogController.kt b/widgetssdk/src/main/java/com/glia/widgets/core/dialog/DialogController.kt
index 292fe45c1..401c28ab3 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/dialog/DialogController.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/dialog/DialogController.kt
@@ -1,6 +1,7 @@
package com.glia.widgets.core.dialog
import com.glia.widgets.core.dialog.model.DialogState
+import com.glia.widgets.core.dialog.model.LeaveDialogAction
import com.glia.widgets.engagement.domain.MediaUpgradeOfferData
import com.glia.widgets.helper.Logger
@@ -28,6 +29,7 @@ internal interface DialogContract {
fun showEngagementConfirmationDialog()
fun showCVEngagementConfirmationDialog()
fun showUpgradeDialog(data: MediaUpgradeOfferData)
+ fun showLeaveCurrentConversationDialog(action: LeaveDialogAction)
fun addCallback(callback: Callback)
fun removeCallback(callback: Callback)
fun interface Callback {
@@ -150,6 +152,13 @@ internal class DialogController : DialogContract.Controller {
dialogManager.addAndEmit(DialogState.MediaUpgrade(data))
}
+ override fun showLeaveCurrentConversationDialog(action: LeaveDialogAction) {
+ if (isShowingUnexpectedErrorDialog) return
+
+ Logger.d(TAG, "Show Leave Current Conversation Dialog")
+ dialogManager.addAndEmit(DialogState.LeaveCurrentConversation(action))
+ }
+
override fun addCallback(callback: DialogContract.Controller.Callback) {
Logger.d(TAG, "addCallback")
viewCallbacks.add(callback)
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/dialog/domain/IsShowOverlayPermissionRequestDialogUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/dialog/domain/IsShowOverlayPermissionRequestDialogUseCase.kt
index 04125e4d7..4533bf9ee 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/dialog/domain/IsShowOverlayPermissionRequestDialogUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/dialog/domain/IsShowOverlayPermissionRequestDialogUseCase.kt
@@ -1,8 +1,8 @@
package com.glia.widgets.core.dialog.domain
-import com.glia.widgets.core.configuration.GliaSdkConfigurationManager
import com.glia.widgets.core.dialog.PermissionDialogManager
import com.glia.widgets.core.permissions.PermissionManager
+import com.glia.widgets.launcher.ConfigurationManager
internal interface IsShowOverlayPermissionRequestDialogUseCase {
operator fun invoke(): Boolean
@@ -11,11 +11,11 @@ internal interface IsShowOverlayPermissionRequestDialogUseCase {
internal class IsShowOverlayPermissionRequestDialogUseCaseImpl(
private val permissionManager: PermissionManager,
private val permissionDialogManager: PermissionDialogManager,
- private val gliaSdkConfigurationManager: GliaSdkConfigurationManager
+ private val gliaSdkConfigurationManager: ConfigurationManager
) : IsShowOverlayPermissionRequestDialogUseCase {
private val hasNoOverlayPermissions: Boolean get() = !permissionManager.hasOverlayPermission()
private val hasNotShownOverlayPermissionRequest: Boolean get() = !permissionDialogManager.hasOverlayPermissionDialogShown()
- private val isUseOverlay: Boolean get() = gliaSdkConfigurationManager.isEnableBubbleOutsideApp
+ private val isUseOverlay: Boolean get() = gliaSdkConfigurationManager.enableBubbleOutsideApp
override fun invoke(): Boolean = hasNoOverlayPermissions && hasNotShownOverlayPermissionRequest && isUseOverlay
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/dialog/model/DialogState.kt b/widgetssdk/src/main/java/com/glia/widgets/core/dialog/model/DialogState.kt
index 9d5d3a2a2..094a0e275 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/dialog/model/DialogState.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/dialog/model/DialogState.kt
@@ -3,18 +3,19 @@ package com.glia.widgets.core.dialog.model
import com.glia.widgets.engagement.domain.MediaUpgradeOfferData
internal sealed interface DialogState {
- object None : DialogState
- object OverlayPermission : DialogState
- object CVOverlayPermission : DialogState
- object ExitQueue : DialogState
- object StartScreenSharing : DialogState
- object VisitorCode : DialogState
- object MessageCenterUnavailable : DialogState
- object Unauthenticated : DialogState
- object Confirmation : DialogState
- object CVConfirmation : DialogState
- object EndEngagement : DialogState
- object UnexpectedError : DialogState
+ data object None : DialogState
+ data object OverlayPermission : DialogState
+ data object CVOverlayPermission : DialogState
+ data object ExitQueue : DialogState
+ data object StartScreenSharing : DialogState
+ data object VisitorCode : DialogState
+ data object MessageCenterUnavailable : DialogState
+ data object Unauthenticated : DialogState
+ data object Confirmation : DialogState
+ data object CVConfirmation : DialogState
+ data object EndEngagement : DialogState
+ data object UnexpectedError : DialogState
data class MediaUpgrade(val data: MediaUpgradeOfferData) : DialogState
+ data class LeaveCurrentConversation(val action: LeaveDialogAction) : DialogState
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/dialog/model/LeaveDialogAction.kt b/widgetssdk/src/main/java/com/glia/widgets/core/dialog/model/LeaveDialogAction.kt
new file mode 100644
index 000000000..8705377fc
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/dialog/model/LeaveDialogAction.kt
@@ -0,0 +1,7 @@
+package com.glia.widgets.core.dialog.model
+
+internal enum class LeaveDialogAction {
+ LIVE_CHAT,
+ VIDEO,
+ AUDIO
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/GliaEngagementConfigRepository.kt b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/GliaEngagementConfigRepository.kt
deleted file mode 100644
index 6b274b40d..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/GliaEngagementConfigRepository.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.glia.widgets.core.engagement
-
-import com.glia.widgets.chat.ChatType
-
-internal class GliaEngagementConfigRepository {
- var queueIds = emptyList()
- var chatType = ChatType.LIVE_CHAT
-
- fun reset() {
- queueIds = emptyList()
- chatType = ChatType.LIVE_CHAT
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/SetEngagementConfigUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/SetEngagementConfigUseCase.kt
deleted file mode 100644
index d320a6a03..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/SetEngagementConfigUseCase.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.glia.widgets.core.engagement.domain
-
-import com.glia.widgets.chat.ChatType
-import com.glia.widgets.core.engagement.GliaEngagementConfigRepository
-
-internal class SetEngagementConfigUseCase(private val engagementConfigRepository: GliaEngagementConfigRepository) {
- operator fun invoke(chatType: ChatType, queueIds: List) {
- engagementConfigRepository.chatType = chatType
- engagementConfigRepository.queueIds = queueIds
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/ShouldShowMediaEngagementViewUseCase.java b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/ShouldShowMediaEngagementViewUseCase.java
index d6052e494..566627656 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/ShouldShowMediaEngagementViewUseCase.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/ShouldShowMediaEngagementViewUseCase.java
@@ -1,19 +1,19 @@
package com.glia.widgets.core.engagement.domain;
import com.glia.widgets.engagement.domain.EngagementTypeUseCase;
-import com.glia.widgets.engagement.domain.IsQueueingOrEngagementUseCase;
+import com.glia.widgets.engagement.domain.IsQueueingOrLiveEngagementUseCase;
/**
* @hide
*/
public class ShouldShowMediaEngagementViewUseCase {
- private final IsQueueingOrEngagementUseCase isQueueingOrEngagementUseCase;
+ private final IsQueueingOrLiveEngagementUseCase isQueueingOrLiveEngagementUseCase;
private final EngagementTypeUseCase engagementTypeUseCase;
public ShouldShowMediaEngagementViewUseCase(
- IsQueueingOrEngagementUseCase isQueueingOrEngagementUseCase,
+ IsQueueingOrLiveEngagementUseCase isQueueingOrLiveEngagementUseCase,
EngagementTypeUseCase engagementTypeUseCase) {
- this.isQueueingOrEngagementUseCase = isQueueingOrEngagementUseCase;
+ this.isQueueingOrLiveEngagementUseCase = isQueueingOrLiveEngagementUseCase;
this.engagementTypeUseCase = engagementTypeUseCase;
}
@@ -25,11 +25,11 @@ public boolean execute(boolean isUpgradeToCall) {
}
private boolean hasNoQueueingAndEngagementOngoing() {
- return !isQueueingOrEngagementUseCase.invoke();
+ return !isQueueingOrLiveEngagementUseCase.invoke();
}
private boolean hasMediaQueueingOngoing() {
- return isQueueingOrEngagementUseCase.isQueueingForMedia();
+ return isQueueingOrLiveEngagementUseCase.isQueueingForMedia();
}
private boolean hasOngoingMediaEngagement() {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/model/ChatHistoryResponse.kt b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/model/ChatHistoryResponse.kt
index 67d2e1524..40d6a686a 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/model/ChatHistoryResponse.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/engagement/domain/model/ChatHistoryResponse.kt
@@ -1,8 +1,3 @@
package com.glia.widgets.core.engagement.domain.model
-import com.glia.widgets.core.secureconversations.domain.NO_UNREAD_MESSAGES
-
-internal data class ChatHistoryResponse(
- val items: List,
- val newMessagesCount: Int = NO_UNREAD_MESSAGES
-)
+internal data class ChatHistoryResponse(val items: List, val newMessagesCount: Int = 0)
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/FileAttachmentRepository.kt b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/FileAttachmentRepository.kt
index 5d4f17b62..3613b1e2a 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/FileAttachmentRepository.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/FileAttachmentRepository.kt
@@ -5,56 +5,87 @@ import com.glia.androidsdk.GliaException
import com.glia.androidsdk.RequestCallback
import com.glia.androidsdk.engagement.EngagementFile
import com.glia.androidsdk.secureconversations.SecureConversations
-import com.glia.widgets.chat.ChatType
-import com.glia.widgets.core.engagement.GliaEngagementConfigRepository
import com.glia.widgets.core.engagement.exception.EngagementMissingException
import com.glia.widgets.core.fileupload.domain.AddFileToAttachmentAndUploadUseCase
import com.glia.widgets.core.fileupload.model.LocalAttachment
import com.glia.widgets.di.GliaCore
-import java.util.Observable
-import java.util.Observer
+import io.reactivex.rxjava3.core.Observable
+import io.reactivex.rxjava3.subjects.BehaviorSubject
import kotlin.jvm.optionals.getOrNull
-internal class FileAttachmentRepository(
- private val gliaCore: GliaCore,
- private val engagementConfigRepository: GliaEngagementConfigRepository
-) {
+internal interface FileAttachmentRepository {
+ val observable: Observable>
+
+ fun getFileAttachments(): List
+ fun getReadyToSendFileAttachments(): List
+ fun getAttachedFilesCount(): Int
+ fun isFileAttached(uri: Uri): Boolean
+ fun attachFile(file: LocalAttachment)
+ fun uploadFile(shouldUseSecureMessagingEndpoints: Boolean, file: LocalAttachment, listener: AddFileToAttachmentAndUploadUseCase.Listener)
+ fun detachFile(attachment: LocalAttachment)
+ fun detachFiles(attachments: List)
+ fun detachAllFiles()
+ fun setFileAttachmentTooLarge(uri: Uri)
+ fun setSupportedFileAttachmentCountExceeded(uri: Uri)
+ fun setFileAttachmentEngagementMissing(uri: Uri)
+}
+
+internal class FileAttachmentRepositoryImpl(
+ private val gliaCore: GliaCore
+) : FileAttachmentRepository {
private val secureConversations: SecureConversations by lazy {
gliaCore.secureConversations
}
- private val observable = ObservableFileAttachmentList()
+ private val _observable = BehaviorSubject.createDefault(emptyList())
+
+ override val observable: Observable> = _observable
- val localAttachments: List
- get() = observable.localAttachments
+ override fun getFileAttachments(): List {
+ return _observable.value ?: emptyList()
+ }
- val readyToSendLocalAttachments: List
- get() = observable.localAttachments
+ override fun getReadyToSendFileAttachments(): List {
+ return getFileAttachments()
.filter { obj: LocalAttachment -> obj.isReadyToSend }
+ }
- val attachedFilesCount: Long
- get() = observable.localAttachments.size.toLong()
+ override fun getAttachedFilesCount(): Int {
+ return getFileAttachments().size
+ }
- fun isFileAttached(uri: Uri): Boolean {
- return observable.localAttachments
+ override fun isFileAttached(uri: Uri): Boolean {
+ return getFileAttachments()
.any { it.uri == uri }
}
- fun attachFile(file: LocalAttachment) {
- observable.notifyUpdate(
- observable.localAttachments + file
- )
+ override fun attachFile(file: LocalAttachment) {
+ _observable.onNext(getFileAttachments() + file)
}
- fun uploadFile(file: LocalAttachment, listener: AddFileToAttachmentAndUploadUseCase.Listener) {
- val engagement = gliaCore.currentEngagement.getOrNull()
- if (engagement != null) {
- engagement.uploadFile(file.uri, handleFileUpload(file, listener))
- } else if (engagementConfigRepository.chatType == ChatType.SECURE_MESSAGING) {
- secureConversations.uploadFile(file.uri, handleFileUpload(file, listener))
+ override fun uploadFile(shouldUseSecureMessagingEndpoints: Boolean, file: LocalAttachment, listener: AddFileToAttachmentAndUploadUseCase.Listener) {
+ if (shouldUseSecureMessagingEndpoints) {
+ uploadFileForSecureConversation(file, listener)
} else {
- setFileAttachmentEngagementMissing(file.uri)
- listener.onError(EngagementMissingException())
+ uploadFileForLiveEngagement(file, listener)
+ }
+ }
+
+ private fun uploadFileForSecureConversation(file: LocalAttachment, listener: AddFileToAttachmentAndUploadUseCase.Listener) {
+ secureConversations.uploadFile(file.uri, handleFileUpload(file, listener))
+ }
+
+ private fun uploadFileForLiveEngagement(file: LocalAttachment, listener: AddFileToAttachmentAndUploadUseCase.Listener) {
+ val engagement = gliaCore.currentEngagement.getOrNull()
+ when {
+ engagement != null -> {
+ engagement.uploadFile(file.uri, handleFileUpload(file, listener))
+ }
+
+ else -> {
+ setFileAttachmentEngagementMissing(file.uri)
+ listener.onError(EngagementMissingException())
+ }
}
}
@@ -74,51 +105,26 @@ internal class FileAttachmentRepository(
}
}
- fun setFileAttachmentTooLarge(uri: Uri) {
- setFileAttachmentStatus(uri, LocalAttachment.Status.ERROR_FILE_TOO_LARGE)
- }
-
- fun setSupportedFileAttachmentCountExceeded(uri: Uri) {
- setFileAttachmentStatus(
- uri,
- LocalAttachment.Status.ERROR_SUPPORTED_FILE_ATTACHMENT_COUNT_EXCEEDED
- )
- }
-
- fun setFileAttachmentEngagementMissing(uri: Uri) {
- setFileAttachmentStatus(uri, LocalAttachment.Status.ERROR_ENGAGEMENT_MISSING)
- }
-
- fun detachFile(attachment: LocalAttachment?) {
- observable.notifyUpdate(
- observable.localAttachments
- .filter { it.uri !== attachment?.uri }
+ override fun detachFile(attachment: LocalAttachment) {
+ _observable.onNext(
+ getFileAttachments()
+ .filter { it.uri !== attachment.uri }
)
}
- fun detachFiles(attachments: List) {
- observable.notifyUpdate(
- observable.localAttachments
+ override fun detachFiles(attachments: List) {
+ _observable.onNext(
+ getFileAttachments()
.filter { attachment: LocalAttachment? ->
- !attachments.contains(attachment)
+ !attachments.contains(
+ attachment
+ )
}
)
}
- fun detachAllFiles() {
- observable.notifyUpdate(ArrayList())
- }
-
- fun addObserver(observer: Observer?) {
- observable.addObserver(observer)
- }
-
- fun removeObserver(observer: Observer?) {
- observable.deleteObserver(observer)
- }
-
- fun clearObservers() {
- observable.deleteObservers()
+ override fun detachAllFiles() {
+ _observable.onNext(emptyList())
}
private fun onUploadFileSecurityScanRequired(
@@ -128,7 +134,7 @@ internal class FileAttachmentRepository(
) {
setFileAttachmentStatus(uri, LocalAttachment.Status.SECURITY_SCAN)
listener.onSecurityCheckStarted()
- engagementFile.on(EngagementFile.Events.SCAN_RESULT) { scanResult: EngagementFile.ScanResult ->
+ engagementFile.on(EngagementFile.Events.SCAN_RESULT) { scanResult: EngagementFile.ScanResult? ->
engagementFile.off(EngagementFile.Events.SCAN_RESULT)
listener.onSecurityCheckFinished(scanResult)
onUploadFileSecurityScanReceived(uri, engagementFile, scanResult, listener)
@@ -138,7 +144,7 @@ internal class FileAttachmentRepository(
private fun onUploadFileSecurityScanReceived(
uri: Uri,
engagementFile: EngagementFile?,
- scanResult: EngagementFile.ScanResult,
+ scanResult: EngagementFile.ScanResult?,
listener: AddFileToAttachmentAndUploadUseCase.Listener
) {
if (scanResult == EngagementFile.ScanResult.CLEAN && engagementFile != null) {
@@ -159,11 +165,11 @@ internal class FileAttachmentRepository(
}
private fun setFileAttachmentStatus(uri: Uri, status: LocalAttachment.Status) {
- observable.notifyUpdate(
- observable.localAttachments
+ _observable.onNext(
+ getFileAttachments()
.map { localAttachment: LocalAttachment ->
if (localAttachment.uri === uri) {
- localAttachment.setAttachmentStatus(status)
+ localAttachment.copy(attachmentStatus = status)
} else {
localAttachment
}
@@ -172,13 +178,11 @@ internal class FileAttachmentRepository(
}
private fun onEngagementFileReceived(uri: Uri, engagementFile: EngagementFile) {
- observable.notifyUpdate(
- observable.localAttachments
+ _observable.onNext(
+ getFileAttachments()
.map { attachment: LocalAttachment ->
if (attachment.uri == uri) {
- attachment
- .setEngagementFile(engagementFile)
- .setAttachmentStatus(LocalAttachment.Status.READY_TO_SEND)
+ attachment.copy(engagementFile = engagementFile, attachmentStatus = LocalAttachment.Status.READY_TO_SEND)
} else {
attachment
}
@@ -186,6 +190,18 @@ internal class FileAttachmentRepository(
)
}
+ override fun setFileAttachmentTooLarge(uri: Uri) {
+ setFileAttachmentStatus(uri, LocalAttachment.Status.ERROR_FILE_TOO_LARGE)
+ }
+
+ override fun setSupportedFileAttachmentCountExceeded(uri: Uri) {
+ setFileAttachmentStatus(uri, LocalAttachment.Status.ERROR_SUPPORTED_FILE_ATTACHMENT_COUNT_EXCEEDED)
+ }
+
+ override fun setFileAttachmentEngagementMissing(uri: Uri) {
+ setFileAttachmentStatus(uri, LocalAttachment.Status.ERROR_ENGAGEMENT_MISSING)
+ }
+
private fun getAttachmentStatus(exception: GliaException): LocalAttachment.Status {
return when (exception.cause) {
GliaException.Cause.FILE_UPLOAD_FORBIDDEN -> LocalAttachment.Status.ERROR_FILE_UPLOAD_FORBIDDEN
@@ -198,14 +214,4 @@ internal class FileAttachmentRepository(
else -> LocalAttachment.Status.ERROR_UNKNOWN
}
}
-
- class ObservableFileAttachmentList : Observable() {
- var localAttachments: List = ArrayList()
-
- fun notifyUpdate(newObject: List) {
- localAttachments = newObject
- setChanged()
- notifyObservers()
- }
- }
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/SecureFileAttachmentRepository.kt b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/SecureFileAttachmentRepository.kt
deleted file mode 100644
index 73a84de83..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/SecureFileAttachmentRepository.kt
+++ /dev/null
@@ -1,180 +0,0 @@
-package com.glia.widgets.core.fileupload
-
-import android.net.Uri
-import com.glia.androidsdk.GliaException
-import com.glia.androidsdk.RequestCallback
-import com.glia.androidsdk.engagement.EngagementFile
-import com.glia.androidsdk.secureconversations.SecureConversations
-import com.glia.widgets.core.fileupload.domain.AddFileToAttachmentAndUploadUseCase
-import com.glia.widgets.core.fileupload.model.LocalAttachment
-import com.glia.widgets.di.GliaCore
-import io.reactivex.rxjava3.core.Observable
-import io.reactivex.rxjava3.subjects.BehaviorSubject
-import kotlin.jvm.optionals.getOrNull
-
-internal class SecureFileAttachmentRepository(private val gliaCore: GliaCore) {
- private val secureConversations: SecureConversations by lazy {
- gliaCore.secureConversations
- }
-
- private val _observable = BehaviorSubject.createDefault(emptyList())
-
- val observable: Observable> = _observable
-
- fun getFileAttachments(): List {
- return _observable.value ?: emptyList()
- }
-
- fun getReadyToSendFileAttachments(): List {
- return getFileAttachments()
- .filter { obj: LocalAttachment -> obj.isReadyToSend }
- }
-
- fun getAttachedFilesCount(): Int {
- return getFileAttachments().size
- }
-
- fun isFileAttached(uri: Uri): Boolean {
- return getFileAttachments()
- .any { it.uri == uri }
- }
-
- fun attachFile(file: LocalAttachment) {
- _observable.onNext(getFileAttachments() + file)
- }
-
- fun uploadFile(file: LocalAttachment, listener: AddFileToAttachmentAndUploadUseCase.Listener) {
- val engagement = gliaCore.currentEngagement.getOrNull()
- if (engagement != null) {
- engagement.uploadFile(file.uri, handleFileUpload(file, listener))
- } else {
- secureConversations.uploadFile(file.uri, handleFileUpload(file, listener))
- }
- }
-
- private fun handleFileUpload(
- file: LocalAttachment,
- listener: AddFileToAttachmentAndUploadUseCase.Listener
- ) = RequestCallback { engagementFile: EngagementFile?, e: GliaException? ->
- if (engagementFile != null) {
- if (!engagementFile.isSecurityScanRequired) {
- onUploadFileSuccess(file.uri, engagementFile, listener)
- } else {
- onUploadFileSecurityScanRequired(file.uri, engagementFile, listener)
- }
- } else if (e != null) {
- setFileAttachmentStatus(file.uri, getAttachmentStatus(e))
- listener.onError(e)
- }
- }
-
- fun detachFile(attachment: LocalAttachment) {
- _observable.onNext(
- getFileAttachments()
- .filter { it.uri !== attachment.uri }
- )
- }
-
- fun detachFiles(attachments: List) {
- _observable.onNext(
- getFileAttachments()
- .filter { attachment: LocalAttachment? ->
- !attachments.contains(
- attachment
- )
- }
- )
- }
-
- fun detachAllFiles() {
- _observable.onNext(emptyList())
- }
-
- private fun onUploadFileSecurityScanRequired(
- uri: Uri,
- engagementFile: EngagementFile,
- listener: AddFileToAttachmentAndUploadUseCase.Listener
- ) {
- setFileAttachmentStatus(uri, LocalAttachment.Status.SECURITY_SCAN)
- listener.onSecurityCheckStarted()
- engagementFile.on(
- EngagementFile.Events.SCAN_RESULT
- ) { scanResult: EngagementFile.ScanResult? ->
- engagementFile.off(EngagementFile.Events.SCAN_RESULT)
- listener.onSecurityCheckFinished(scanResult)
- onUploadFileSecurityScanReceived(uri, engagementFile, scanResult, listener)
- }
- }
-
- private fun onUploadFileSecurityScanReceived(
- uri: Uri,
- engagementFile: EngagementFile?,
- scanResult: EngagementFile.ScanResult?,
- listener: AddFileToAttachmentAndUploadUseCase.Listener
- ) {
- if (scanResult == EngagementFile.ScanResult.CLEAN && engagementFile != null) {
- onUploadFileSuccess(uri, engagementFile, listener)
- } else {
- setFileAttachmentStatus(uri, LocalAttachment.Status.ERROR_SECURITY_SCAN_FAILED)
- listener.onFinished()
- }
- }
-
- private fun onUploadFileSuccess(
- uri: Uri,
- engagementFile: EngagementFile,
- listener: AddFileToAttachmentAndUploadUseCase.Listener
- ) {
- onEngagementFileReceived(uri, engagementFile)
- listener.onFinished()
- }
-
- private fun onEngagementFileReceived(uri: Uri, engagementFile: EngagementFile) {
- _observable.onNext(
- getFileAttachments()
- .map { attachment: LocalAttachment ->
- if (attachment.uri == uri) {
- attachment
- .setEngagementFile(engagementFile)
- .setAttachmentStatus(LocalAttachment.Status.READY_TO_SEND)
- } else {
- attachment
- }
- }
- )
- }
-
- fun setFileAttachmentTooLarge(uri: Uri) {
- setFileAttachmentStatus(uri, LocalAttachment.Status.ERROR_FILE_TOO_LARGE)
- }
-
- fun setSupportedFileAttachmentCountExceeded(uri: Uri) {
- setFileAttachmentStatus(uri, LocalAttachment.Status.ERROR_SUPPORTED_FILE_ATTACHMENT_COUNT_EXCEEDED)
- }
-
- private fun setFileAttachmentStatus(uri: Uri, status: LocalAttachment.Status) {
- _observable.onNext(
- getFileAttachments()
- .map { localAttachment: LocalAttachment ->
- if (localAttachment.uri === uri) {
- localAttachment.setAttachmentStatus(status)
- } else {
- localAttachment
- }
- }
- )
- }
-
- private fun getAttachmentStatus(exception: GliaException): LocalAttachment.Status {
- return when (exception.cause) {
- GliaException.Cause.FILE_UPLOAD_FORBIDDEN -> LocalAttachment.Status.ERROR_FILE_UPLOAD_FORBIDDEN
- GliaException.Cause.INVALID_INPUT -> LocalAttachment.Status.ERROR_INVALID_INPUT
- GliaException.Cause.NETWORK_TIMEOUT -> LocalAttachment.Status.ERROR_NETWORK_TIMEOUT
- GliaException.Cause.INTERNAL_ERROR -> LocalAttachment.Status.ERROR_INTERNAL
- GliaException.Cause.PERMISSIONS_DENIED -> LocalAttachment.Status.ERROR_PERMISSIONS_DENIED
- GliaException.Cause.FILE_FORMAT_UNSUPPORTED -> LocalAttachment.Status.ERROR_FORMAT_UNSUPPORTED
- GliaException.Cause.FILE_TOO_LARGE -> LocalAttachment.Status.ERROR_FILE_TOO_LARGE
- else -> LocalAttachment.Status.ERROR_UNKNOWN
- }
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/AddFileAttachmentsObserverUseCase.java b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/AddFileAttachmentsObserverUseCase.java
deleted file mode 100644
index f4fe078df..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/AddFileAttachmentsObserverUseCase.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.glia.widgets.core.fileupload.domain;
-
-import com.glia.widgets.core.fileupload.FileAttachmentRepository;
-
-import java.util.Observer;
-
-/**
- * @hide
- */
-public class AddFileAttachmentsObserverUseCase {
- private final FileAttachmentRepository repository;
-
- public AddFileAttachmentsObserverUseCase(FileAttachmentRepository repository) {
- this.repository = repository;
- }
-
- public void execute(Observer observer) {
- this.repository.addObserver(observer);
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/AddSecureFileAttachmentsObserverUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/AddFileAttachmentsObserverUseCase.kt
similarity index 61%
rename from widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/AddSecureFileAttachmentsObserverUseCase.kt
rename to widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/AddFileAttachmentsObserverUseCase.kt
index 2342d7443..08f6ec40c 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/AddSecureFileAttachmentsObserverUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/AddFileAttachmentsObserverUseCase.kt
@@ -1,12 +1,15 @@
-package com.glia.widgets.core.secureconversations.domain
+package com.glia.widgets.core.fileupload.domain
-import com.glia.widgets.core.fileupload.SecureFileAttachmentRepository
+import com.glia.widgets.core.fileupload.FileAttachmentRepository
import com.glia.widgets.core.fileupload.model.LocalAttachment
import com.glia.widgets.helper.rx.Schedulers
import io.reactivex.rxjava3.core.Observable
-internal class AddSecureFileAttachmentsObserverUseCase(
- private val repository: SecureFileAttachmentRepository,
+/**
+ * @hide
+ */
+internal class AddFileAttachmentsObserverUseCase(
+ private val repository: FileAttachmentRepository,
private val schedulers: Schedulers
) {
operator fun invoke(): Observable> = repository.observable
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/AddFileToAttachmentAndUploadUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/AddFileToAttachmentAndUploadUseCase.kt
index 432aacd14..4e2976446 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/AddFileToAttachmentAndUploadUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/AddFileToAttachmentAndUploadUseCase.kt
@@ -1,31 +1,24 @@
package com.glia.widgets.core.fileupload.domain
import com.glia.androidsdk.engagement.EngagementFile
-import com.glia.widgets.chat.ChatType
-import com.glia.widgets.core.engagement.GliaEngagementConfigRepository
import com.glia.widgets.core.engagement.exception.EngagementMissingException
import com.glia.widgets.core.fileupload.FileAttachmentRepository
import com.glia.widgets.core.fileupload.exception.RemoveBeforeReUploadingException
import com.glia.widgets.core.fileupload.exception.SupportedFileCountExceededException
import com.glia.widgets.core.fileupload.exception.SupportedFileSizeExceededException
import com.glia.widgets.core.fileupload.model.LocalAttachment
-import com.glia.widgets.engagement.domain.IsQueueingOrEngagementUseCase
+import com.glia.widgets.core.secureconversations.domain.ManageSecureMessagingStatusUseCase
+import com.glia.widgets.engagement.domain.IsQueueingOrLiveEngagementUseCase
internal class AddFileToAttachmentAndUploadUseCase(
- private val isQueueingOrEngagementUseCase: IsQueueingOrEngagementUseCase,
+ private val isQueueingOrLiveEngagementUseCase: IsQueueingOrLiveEngagementUseCase,
private val fileAttachmentRepository: FileAttachmentRepository,
- private val engagementConfigRepository: GliaEngagementConfigRepository
+ private val manageSecureMessagingStatusUseCase: ManageSecureMessagingStatusUseCase
) {
private val isSupportedFileCountExceeded: Boolean
- get() = fileAttachmentRepository.attachedFilesCount > SupportedFileCountCheckUseCase.SUPPORTED_FILE_COUNT
+ get() = fileAttachmentRepository.getAttachedFilesCount() > SupportedFileCountCheckUseCase.SUPPORTED_FILE_COUNT
- private val hasNoOngoingEngagement: Boolean
- get() = !isQueueingOrEngagementUseCase.hasOngoingEngagement
-
- private val isNotSecureEngagement: Boolean
- get() = engagementConfigRepository.chatType != ChatType.SECURE_MESSAGING
-
- fun execute(file: LocalAttachment, listener: Listener) {
+ operator fun invoke(file: LocalAttachment, listener: Listener) {
if (fileAttachmentRepository.isFileAttached(file.uri)) {
listener.onError(RemoveBeforeReUploadingException())
} else {
@@ -35,11 +28,11 @@ internal class AddFileToAttachmentAndUploadUseCase(
private fun onFileNotAttached(file: LocalAttachment, listener: Listener) {
fileAttachmentRepository.attachFile(file)
- if (hasNoOngoingEngagement && isNotSecureEngagement) {
+ if (isQueueingOrLiveEngagementUseCase.hasOngoingLiveEngagement || manageSecureMessagingStatusUseCase.shouldBehaveAsSecureMessaging) {
+ onHasOngoingOrSecureEngagement(file, listener)
+ } else {
fileAttachmentRepository.setFileAttachmentEngagementMissing(file.uri)
listener.onError(EngagementMissingException())
- } else {
- onHasOngoingOrSecureEngagement(file, listener)
}
}
@@ -52,7 +45,7 @@ internal class AddFileToAttachmentAndUploadUseCase(
listener.onError(SupportedFileSizeExceededException())
} else {
listener.onStarted()
- fileAttachmentRepository.uploadFile(file, listener)
+ fileAttachmentRepository.uploadFile(manageSecureMessagingStatusUseCase.shouldUseSecureMessagingEndpoints, file, listener)
}
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/GetFileAttachmentsUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/GetFileAttachmentsUseCase.kt
index 20687a3f2..ebc7b729f 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/GetFileAttachmentsUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/GetFileAttachmentsUseCase.kt
@@ -5,6 +5,6 @@ import com.glia.widgets.core.fileupload.model.LocalAttachment
internal class GetFileAttachmentsUseCase(private val repository: FileAttachmentRepository) {
operator fun invoke(): List {
- return repository.localAttachments
+ return repository.getFileAttachments()
}
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/RemoveFileAttachmentObserverUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/RemoveFileAttachmentObserverUseCase.kt
deleted file mode 100644
index e758c064a..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/RemoveFileAttachmentObserverUseCase.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.glia.widgets.core.fileupload.domain
-
-import com.glia.widgets.core.fileupload.FileAttachmentRepository
-import java.util.Observer
-
-internal class RemoveFileAttachmentObserverUseCase(private val repository: FileAttachmentRepository) {
- operator fun invoke(observer: Observer?) {
- repository.removeObserver(observer)
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/RemoveFileAttachmentUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/RemoveFileAttachmentUseCase.kt
index 733bf3fb2..14b83efdf 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/RemoveFileAttachmentUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/RemoveFileAttachmentUseCase.kt
@@ -4,7 +4,7 @@ import com.glia.widgets.core.fileupload.FileAttachmentRepository
import com.glia.widgets.core.fileupload.model.LocalAttachment
internal class RemoveFileAttachmentUseCase(private val repository: FileAttachmentRepository) {
- operator fun invoke(attachment: LocalAttachment?) {
+ operator fun invoke(attachment: LocalAttachment) {
repository.detachFile(attachment)
}
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/SupportedFileCountCheckUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/SupportedFileCountCheckUseCase.kt
index ede900353..e8a001396 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/SupportedFileCountCheckUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/domain/SupportedFileCountCheckUseCase.kt
@@ -4,10 +4,10 @@ import com.glia.widgets.core.fileupload.FileAttachmentRepository
internal class SupportedFileCountCheckUseCase(private val repository: FileAttachmentRepository) {
operator fun invoke(): Boolean {
- return repository.localAttachments.size <= SUPPORTED_FILE_COUNT
+ return repository.getAttachedFilesCount() <= SUPPORTED_FILE_COUNT
}
companion object {
- const val SUPPORTED_FILE_COUNT: Long = 25
+ const val SUPPORTED_FILE_COUNT: Int = 25
}
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/model/LocalAttachment.kt b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/model/LocalAttachment.kt
index a2634b35e..de37333dc 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/model/LocalAttachment.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/fileupload/model/LocalAttachment.kt
@@ -21,32 +21,6 @@ internal data class LocalAttachment(
val id: String
get() = engagementFile?.id ?: UUID.randomUUID().toString()
- constructor(attachment: LocalAttachment, status: Status) : this(
- uri = attachment.uri,
- engagementFile = attachment.engagementFile,
- displayName = attachment.displayName,
- size = attachment.size,
- attachmentStatus = status,
- mimeType = attachment.mimeType,
- )
-
- constructor(attachment: LocalAttachment, engagementFile: EngagementFile?) : this(
- uri = attachment.uri,
- attachmentStatus = attachment.attachmentStatus,
- displayName = attachment.displayName,
- size = attachment.size,
- engagementFile = engagementFile,
- mimeType = attachment.mimeType
- )
-
- fun setEngagementFile(engagementFile: EngagementFile?): LocalAttachment {
- return LocalAttachment(this, engagementFile)
- }
-
- fun setAttachmentStatus(status: Status): LocalAttachment {
- return LocalAttachment(this, status)
- }
-
fun toVisitorAttachmentItem(messageId: String): VisitorAttachmentItem = if (isImage) {
VisitorAttachmentItem.LocalImage(id, messageId, this)
} else {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/notification/Extensions.kt b/widgetssdk/src/main/java/com/glia/widgets/core/notification/Extensions.kt
index 798a3700e..991ff0996 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/notification/Extensions.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/notification/Extensions.kt
@@ -2,25 +2,9 @@ package com.glia.widgets.core.notification
import android.app.NotificationManager
import android.content.Context
-import android.content.Intent
-import android.net.Uri
import android.os.Build
-import android.provider.Settings
import androidx.core.app.NotificationManagerCompat
-internal fun Context.openNotificationChannelScreen() {
- val intent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- .putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
- } else {
- Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- .setData(Uri.fromParts("package", packageName, null))
- }
- startActivity(intent)
-}
-
internal fun Context.areNotificationsEnabled(): Boolean =
NotificationManagerCompat.from(this).areNotificationsEnabled()
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/queue/GliaQueueRepository.kt b/widgetssdk/src/main/java/com/glia/widgets/core/queue/GliaQueueRepository.kt
deleted file mode 100644
index 7cc48080d..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/core/queue/GliaQueueRepository.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.glia.widgets.core.queue
-
-import com.glia.androidsdk.queuing.Queue
-import com.glia.widgets.di.GliaCore
-import io.reactivex.rxjava3.core.Single
-
-internal class GliaQueueRepository(private val gliaCore: GliaCore) {
- /**
- * Emits a list of all Queues
- */
- val queues: Single>
- get() = Single.create { emitter ->
- gliaCore.getQueues { queues, exception ->
- when {
- exception != null -> emitter.onError(exception)
- queues != null -> emitter.onSuccess(queues)
- else -> emitter.onError(RuntimeException("Fetching file failed"))
- }
- }
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/queue/QueueRepository.kt b/widgetssdk/src/main/java/com/glia/widgets/core/queue/QueueRepository.kt
new file mode 100644
index 000000000..e8d1d232b
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/queue/QueueRepository.kt
@@ -0,0 +1,150 @@
+package com.glia.widgets.core.queue
+
+import com.glia.androidsdk.GliaException
+import com.glia.androidsdk.queuing.Queue
+import com.glia.widgets.di.GliaCore
+import com.glia.widgets.helper.Logger
+import com.glia.widgets.helper.TAG
+import com.glia.widgets.helper.unSafeSubscribe
+import com.glia.widgets.launcher.ConfigurationManager
+import io.reactivex.rxjava3.core.Flowable
+import io.reactivex.rxjava3.core.Single
+import io.reactivex.rxjava3.processors.BehaviorProcessor
+import java.util.function.Consumer
+
+internal sealed interface QueuesState {
+ fun queuesOrEmpty(): List = when (this) {
+ is Queues -> queues
+ else -> emptyList()
+ }
+
+ data object Loading : QueuesState
+ data object Empty : QueuesState
+ data class Queues(val queues: List) : QueuesState
+ data class Error(val error: Throwable) : QueuesState
+}
+
+internal interface QueueRepository {
+ val queuesState: Flowable
+ val relevantQueueIds: Single>
+ fun initialize()
+ fun fetchQueues()
+}
+
+internal class QueueRepositoryImpl(private val gliaCore: GliaCore, private val configurationManager: ConfigurationManager) : QueueRepository {
+
+ private val queueUpdateCallback: Consumer = Consumer { updateQueues(it) }
+ private val siteQueues: BehaviorProcessor> = BehaviorProcessor.create()
+
+ private val _queuesState: BehaviorProcessor = BehaviorProcessor.create()
+ override val queuesState = _queuesState.hide()
+ .doOnSubscribe { fetchQueues() }
+ .distinctUntilChanged()
+
+ private val _relevantQueueIds: Flowable>
+ get() = queuesState
+ .filter { it !is QueuesState.Loading }
+ .map { it.queuesOrEmpty() }
+ .map { queues -> queues.map { it.id } }
+
+ override val relevantQueueIds: Single>
+ get() = _relevantQueueIds.firstOrError()
+
+ override fun initialize() {
+ fetchQueues()
+ }
+
+ override fun fetchQueues() {
+ if (gliaCore.isInitialized && siteQueues.value == null) {
+ _queuesState.onNext(QueuesState.Loading)
+
+ gliaCore.getQueues { queues, exception ->
+ queues?.let { siteQueuesReceived(it) } ?: reportGetSiteQueuesError(exception)
+ }
+ }
+ }
+
+ private fun siteQueuesReceived(queues: List) {
+ siteQueues.onNext(queues)
+ subscribeToQueues()
+ subscribeToQueueUpdates()
+ }
+
+ private fun reportGetSiteQueuesError(exception: GliaException?) {
+ val ex = exception ?: RuntimeException("Fetching queues failed: queues were null")
+ Logger.e(TAG, "Setting up queues. Failed to get site queues.", ex)
+ _queuesState.onNext(QueuesState.Error(ex))
+ }
+
+ private fun subscribeToQueueUpdates() {
+ _relevantQueueIds
+ .filter { it.isNotEmpty() }
+ .distinctUntilChanged()
+ .unSafeSubscribe { gliaCore.subscribeToQueueStateUpdates(it, {}, queueUpdateCallback) }
+ }
+
+ private fun subscribeToQueues() {
+ Flowable.combineLatest(configurationManager.queueIdsObservable, siteQueues, ::Pair)
+ .distinctUntilChanged()
+ .unSafeSubscribe { (integratorQueueIds, siteQueues) ->
+ Logger.d(TAG, "Setting up queues. Site has ${siteQueues.count()} queues.")
+ onQueuesReceived(integratorQueueIds, siteQueues)
+ }
+ }
+
+ private fun onQueuesReceived(queueIds: List, siteQueues: List) {
+ when {
+ siteQueues.isEmpty() -> {
+ Logger.w(TAG, "Setting up queues. Site has no queues.")
+ _queuesState.onNext(QueuesState.Empty)
+ }
+
+ queueIds.isEmpty() -> {
+ Logger.i(TAG, "Setting up queues. Integrator specified an empty list of queues.")
+ setDefaultQueues(siteQueues)
+ }
+
+ else -> {
+ matchQueues(queueIds, siteQueues)
+ }
+ }
+ }
+
+ private fun setDefaultQueues(siteQueues: List) {
+ val defaultQueues = siteQueues.filter { it.isDefault == true }
+
+ Logger.i(TAG, "Setting up queues. Using ${defaultQueues.count()} default queues.")
+ if (defaultQueues.isEmpty()) {
+ _queuesState.onNext(QueuesState.Empty)
+ } else {
+ _queuesState.onNext(QueuesState.Queues(defaultQueues))
+ }
+ }
+
+ private fun updateQueues(queue: Queue) {
+ val currentQueues = _queuesState.value
+ ?.let { it as? QueuesState.Queues }
+ ?.queues
+ ?.toMutableList() ?: return
+
+ val index = currentQueues.indexOfFirst { it.id == queue.id }.takeIf { it != -1 } ?: return
+
+ currentQueues[index] = queue
+
+ _queuesState.onNext(QueuesState.Queues(currentQueues.toList()))
+ }
+
+ private fun matchQueues(queueIds: List, siteQueues: List) {
+ val matchedQueues = siteQueues.filter { queueIds.contains(it.id) }
+ Logger.i(
+ TAG,
+ "Setting up queues. ${matchedQueues.count()} out of ${queueIds.count()} queues provided by an integrator match with site queues."
+ )
+ if (matchedQueues.isEmpty()) {
+ setDefaultQueues(siteQueues)
+ } else {
+ _queuesState.onNext(QueuesState.Queues(matchedQueues))
+ }
+ }
+
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/SecureConversationsRepository.kt b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/SecureConversationsRepository.kt
index 3efaf2adc..bf3fa3202 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/SecureConversationsRepository.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/SecureConversationsRepository.kt
@@ -7,34 +7,70 @@ import com.glia.androidsdk.chat.VisitorMessage
import com.glia.androidsdk.secureconversations.SecureConversations
import com.glia.widgets.chat.data.GliaChatRepository
import com.glia.widgets.chat.domain.GliaSendMessageUseCase
+import com.glia.widgets.core.queue.QueueRepository
+import com.glia.widgets.di.GliaCore
+import com.glia.widgets.helper.asStateFlowable
+import com.glia.widgets.helper.unSafeSubscribe
+import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.core.Observable
+import io.reactivex.rxjava3.processors.BehaviorProcessor
import io.reactivex.rxjava3.subjects.BehaviorSubject
import io.reactivex.rxjava3.subjects.Subject
-internal class SecureConversationsRepository(private val secureConversations: SecureConversations) {
- private val _messageSendingObservable: Subject = BehaviorSubject.createDefault(false)
+internal class SecureConversationsRepository(private val core: GliaCore, private val queueRepository: QueueRepository) {
+ private val secureConversations: SecureConversations by lazy { core.secureConversations }
+ private val _messageSendingObservable: Subject = BehaviorSubject.createDefault(false)
val messageSendingObservable: Observable = _messageSendingObservable
+ private val _unreadMessagesCountObservable: BehaviorProcessor = BehaviorProcessor.createDefault(0)
+ val unreadMessagesCountObservable: Flowable get() = _unreadMessagesCountObservable.asStateFlowable()
+
+ private val _pendingSecureConversationsStatusObservable: BehaviorProcessor = BehaviorProcessor.createDefault(false)
+ val pendingSecureConversationsStatusObservable: Flowable get() = _pendingSecureConversationsStatusObservable.asStateFlowable()
+
+ private val _isLeaveSecureConversationDialogVisibleObservable: BehaviorProcessor = BehaviorProcessor.createDefault(false)
+ val isLeaveSecureConversationDialogVisibleObservable: Flowable get() = _isLeaveSecureConversationDialogVisibleObservable.asStateFlowable()
+
+ fun initialize() {
+ secureConversations.apply {
+ subscribeToUnreadMessageCount { count, _ -> count?.let(_unreadMessagesCountObservable::onNext) }
+ subscribeToPendingSecureConversationStatus { hasPendingSecureConversations, _ ->
+ hasPendingSecureConversations?.let(_pendingSecureConversationsStatusObservable::onNext)
+ }
+ }
+ }
+
fun fetchChatTranscript(listener: GliaChatRepository.HistoryLoadedListener) {
- secureConversations.fetchChatTranscript(listener::loaded)
+ secureConversations.fetchChatTranscript { messages, exception ->
+ listener.loaded(messages?.toList(), exception)
+ }
}
- fun send(payload: SendMessagePayload, queueIds: List, callback: RequestCallback) {
+ fun send(payload: SendMessagePayload, callback: RequestCallback) {
_messageSendingObservable.onNext(true)
- secureConversations.send(payload, queueIds.toTypedArray(), handleResult(callback))
- }
+ queueRepository.relevantQueueIds.unSafeSubscribe { queueIds ->
+ if (queueIds.isNotEmpty()) {
+ secureConversations.send(payload, queueIds.toTypedArray(), handleResult(callback))
+ } else {
+ handleResult(callback).onResult(null, GliaException("relevant queues are empty", GliaException.Cause.INVALID_INPUT))
+ }
- fun send(payload: SendMessagePayload, queueIds: List, listener: GliaSendMessageUseCase.Listener) {
- send(payload, queueIds) { visitorMessage, ex ->
- onMessageReceived(visitorMessage, ex, listener, payload)
}
}
+ fun send(payload: SendMessagePayload, listener: GliaSendMessageUseCase.Listener) {
+ send(payload) { visitorMessage, ex -> onMessageReceived(visitorMessage, ex, listener, payload) }
+ }
+
fun markMessagesRead(callback: RequestCallback) {
secureConversations.markMessagesRead(callback)
}
+ fun setLeaveSecureConversationDialogVisible(visible: Boolean) {
+ _isLeaveSecureConversationDialogVisibleObservable.onNext(visible)
+ }
+
private fun handleResult(callback: RequestCallback): RequestCallback {
return RequestCallback { message: VisitorMessage?, exception: GliaException? ->
_messageSendingObservable.onNext(false)
@@ -54,7 +90,4 @@ internal class SecureConversationsRepository(private val secureConversations: Se
listener.messageSent(visitorMessage)
}
}
-
- fun getUnreadMessagesCount(callback: RequestCallback) =
- secureConversations.getUnreadMessageCount(callback)
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/AddSecureFileToAttachmentAndUploadUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/AddSecureFileToAttachmentAndUploadUseCase.kt
index eec73c2ae..fc242e39d 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/AddSecureFileToAttachmentAndUploadUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/AddSecureFileToAttachmentAndUploadUseCase.kt
@@ -1,16 +1,17 @@
package com.glia.widgets.core.secureconversations.domain
-import com.glia.widgets.core.fileupload.SecureFileAttachmentRepository
+import com.glia.widgets.core.fileupload.FileAttachmentRepository
import com.glia.widgets.core.fileupload.domain.AddFileToAttachmentAndUploadUseCase
+import com.glia.widgets.core.fileupload.domain.AddFileToAttachmentAndUploadUseCase.Listener
import com.glia.widgets.core.fileupload.domain.SupportedFileCountCheckUseCase
import com.glia.widgets.core.fileupload.exception.RemoveBeforeReUploadingException
import com.glia.widgets.core.fileupload.exception.SupportedFileCountExceededException
import com.glia.widgets.core.fileupload.exception.SupportedFileSizeExceededException
import com.glia.widgets.core.fileupload.model.LocalAttachment
-internal class AddSecureFileToAttachmentAndUploadUseCase(private val fileAttachmentRepository: SecureFileAttachmentRepository) {
+internal class AddSecureFileToAttachmentAndUploadUseCase(private val fileAttachmentRepository: FileAttachmentRepository) {
- fun execute(file: LocalAttachment, listener: AddFileToAttachmentAndUploadUseCase.Listener) {
+ operator fun invoke(file: LocalAttachment, listener: Listener) {
if (fileAttachmentRepository.isFileAttached(file.uri)) {
listener.onError(RemoveBeforeReUploadingException())
} else {
@@ -31,7 +32,7 @@ internal class AddSecureFileToAttachmentAndUploadUseCase(private val fileAttachm
listener.onError(SupportedFileSizeExceededException())
} else {
listener.onStarted()
- fileAttachmentRepository.uploadFile(file, listener)
+ fileAttachmentRepository.uploadFile(true, file, listener)
}
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/GetAvailableQueueIdsForSecureMessagingUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/GetAvailableQueueIdsForSecureMessagingUseCase.kt
deleted file mode 100644
index 77c663ff6..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/GetAvailableQueueIdsForSecureMessagingUseCase.kt
+++ /dev/null
@@ -1,91 +0,0 @@
-package com.glia.widgets.core.secureconversations.domain
-
-import com.glia.androidsdk.GliaException
-import com.glia.androidsdk.RequestCallback
-import com.glia.androidsdk.queuing.Queue
-import com.glia.widgets.core.engagement.GliaEngagementConfigRepository
-import com.glia.widgets.core.queue.GliaQueueRepository
-import com.glia.widgets.helper.Data.Value
-import com.glia.widgets.helper.Logger
-import com.glia.widgets.helper.TAG
-import com.glia.widgets.helper.rx.Schedulers
-import io.reactivex.rxjava3.disposables.CompositeDisposable
-import java.util.UUID
-
-internal class GetAvailableQueueIdsForSecureMessagingUseCase(
- private val engagementConfigRepository: GliaEngagementConfigRepository,
- private val queueRepository: GliaQueueRepository,
- private val isMessagingAvailableUseCase: IsMessagingAvailableUseCase,
- private val schedulers: Schedulers
-) {
- private val disposable = CompositeDisposable()
-
- operator fun invoke() = queueRepository.queues
- .map { queues ->
- val queueIds = engagementConfigRepository.queueIds
- validateIds(queueIds)
-
- val matchedQueues = matchQueues(queueIds, queues)
-
- if (matchedQueues.isEmpty()) {
- if (queueIds.isNotEmpty()) {
- Logger.w(TAG, "Provided queue IDs do not match with any queue.")
- }
-
- val defaultQueues = queues
- .filter { it.isDefault == true }
-
- return@map Value(
- if (isMessagingAvailableUseCase(defaultQueues)) {
- Logger.i(TAG, "Secure Messaging is available using queues that are set as **Default**.")
- defaultQueues.map { it.id }
- } else {
- Logger.w(TAG, "No default queues that have status other than closed and support messaging were found.")
- null
- }
- )
- }
-
- Value(
- if (isMessagingAvailableUseCase(matchedQueues)) {
- Logger.i(TAG, "Secure Messaging is available in queues with IDs: $queueIds.")
- queueIds
- } else {
- Logger.w(TAG, "Provided queue IDs do not match with queues that have status other than closed and support messaging.")
- null
- }
- )
- }
- .subscribeOn(schedulers.computationScheduler)
- .observeOn(schedulers.mainScheduler)
-
- private fun validateIds(queueIds: List) {
- val invalidIds = queueIds.filter { queueId ->
- try {
- UUID.fromString(queueId)
- false
- } catch (e: IllegalArgumentException) {
- true
- }
- }
- if (invalidIds.isNotEmpty()) {
- Logger.w(TAG, "Queue ID array for Secure Messaging contains invalid queue IDs: $invalidIds.")
- }
- }
-
- private fun matchQueues(queueIds: List, queues: Array) = queues
- .filter { queueIds.contains(it.id) }
-
- operator fun invoke(callback: RequestCallback>) {
- disposable.add(
- invoke().subscribe(
- { callback.onResult(it.result, null) },
- { callback.onResult(null, GliaException.from(it)) }
- )
- )
- }
-
- fun dispose() {
- disposable.clear()
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/GetSecureFileAttachmentsUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/GetSecureFileAttachmentsUseCase.kt
deleted file mode 100644
index 8ffb74fa9..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/GetSecureFileAttachmentsUseCase.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.glia.widgets.core.secureconversations.domain
-
-import com.glia.widgets.core.fileupload.SecureFileAttachmentRepository
-import com.glia.widgets.core.fileupload.model.LocalAttachment
-
-internal class GetSecureFileAttachmentsUseCase(private val repository: SecureFileAttachmentRepository) {
- operator fun invoke(): List {
- return repository.getFileAttachments()
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/GetUnreadMessagesCountWithTimeoutUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/GetUnreadMessagesCountWithTimeoutUseCase.kt
deleted file mode 100644
index bb5e59e94..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/GetUnreadMessagesCountWithTimeoutUseCase.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.glia.widgets.core.secureconversations.domain
-
-import androidx.annotation.VisibleForTesting
-import com.glia.widgets.core.secureconversations.SecureConversationsRepository
-import com.glia.widgets.helper.Logger
-import io.reactivex.rxjava3.core.Single
-import java.util.concurrent.TimeUnit
-
-private const val TAG = "GetUnreadMessagesCountUseCase"
-
-/**
- * Timeout for the [SecureConversationsRepository.getUnreadMessagesCount]
- *
- * This timeout is not related to the timeout for marking the messages read.
- *
- * @see [GetUnreadMessagesCountWithTimeoutUseCase.invoke]
- */
-@VisibleForTesting
-internal const val TIMEOUT_SEC = 3L
-
-internal const val NO_UNREAD_MESSAGES = 0
-
-internal class GetUnreadMessagesCountWithTimeoutUseCase(private val repository: SecureConversationsRepository) {
-
- /**
- * @return [NO_UNREAD_MESSAGES] if the current socket doesn't signal a success value within the specified [TIMEOUT_SEC] window.
- *
- * This is combined with the chat transcript result to avoid "Jumping UI" when adding new messages'
- * divider [SecureConversationsRepository.getUnreadMessagesCount] is a socket call and
- * there is no warranty that it will return anything so timeout is added to make sure that it will return [NO_UNREAD_MESSAGES]
- * after the timeout if there is no answer from socket yet.
- */
- operator fun invoke(): Single = Single.create {
- repository.getUnreadMessagesCount { count, exception ->
- if (it.isDisposed) return@getUnreadMessagesCount
- if (exception != null) {
- it.tryOnError(exception)
- Logger.e(TAG, "Failed to get unread messages count", exception)
- } else {
- it.onSuccess(count ?: NO_UNREAD_MESSAGES)
- }
- }
- }.timeout(TIMEOUT_SEC, TimeUnit.SECONDS).onErrorReturnItem(NO_UNREAD_MESSAGES)
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/HasOngoingSecureConversationUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/HasOngoingSecureConversationUseCase.kt
new file mode 100644
index 000000000..0e6b50031
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/HasOngoingSecureConversationUseCase.kt
@@ -0,0 +1,30 @@
+package com.glia.widgets.core.secureconversations.domain
+
+import com.glia.widgets.core.secureconversations.SecureConversationsRepository
+import com.glia.widgets.engagement.State
+import com.glia.widgets.engagement.domain.EngagementStateUseCase
+import com.glia.widgets.helper.unSafeSubscribe
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
+import io.reactivex.rxjava3.core.Flowable
+
+internal class HasOngoingSecureConversationUseCase(
+ private val secureConversationsRepository: SecureConversationsRepository,
+ private val engagementStateUseCase: EngagementStateUseCase
+) {
+ /**
+ * @return `true` if there are pending secure conversations, unread messages, or current engagement is Transferred.
+ */
+ private val hasOngoingInteraction: Flowable
+ get() = Flowable.combineLatest(
+ secureConversationsRepository.pendingSecureConversationsStatusObservable,
+ secureConversationsRepository.unreadMessagesCountObservable,
+ engagementStateUseCase()
+ ) { pendingSecureConversations, unreadMessagesCount, state ->
+ pendingSecureConversations || unreadMessagesCount > 0 || state is State.TransferredToSecureConversation
+ }
+
+ operator fun invoke(): Flowable = hasOngoingInteraction.distinctUntilChanged().observeOn(AndroidSchedulers.mainThread())
+
+ operator fun invoke(callback: (Boolean) -> Unit) = invoke().firstOrError().unSafeSubscribe(callback)
+
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/IsMessagingAvailableUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/IsMessagingAvailableUseCase.kt
index 571e84497..0d4663d19 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/IsMessagingAvailableUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/IsMessagingAvailableUseCase.kt
@@ -2,12 +2,28 @@ package com.glia.widgets.core.secureconversations.domain
import com.glia.androidsdk.queuing.Queue
import com.glia.androidsdk.queuing.QueueState
+import com.glia.widgets.core.queue.QueueRepository
+import com.glia.widgets.core.queue.QueuesState
+import com.glia.widgets.engagement.EngagementRepository
+import com.glia.widgets.engagement.State
import com.glia.widgets.helper.supportMessaging
+import io.reactivex.rxjava3.core.Flowable
-internal class IsMessagingAvailableUseCase {
+internal class IsMessagingAvailableUseCase(private val queueRepository: QueueRepository, private val engagementRepository: EngagementRepository) {
- operator fun invoke(queues: List): Boolean = queues
- .asSequence()
+ operator fun invoke(): Flowable> =
+ Flowable.combineLatest(queueRepository.queuesState, engagementRepository.engagementState) { queueState, engagementState ->
+ mapResult(queueState, engagementState)
+ }.distinctUntilChanged()
+
+ private fun mapResult(queuesState: QueuesState, engagementState: State): Result = when {
+ engagementState is State.TransferredToSecureConversation -> Result.success(true)
+ queuesState is QueuesState.Queues -> Result.success(isMessagingAvailable(queuesState.queues))
+ queuesState is QueuesState.Error -> Result.failure(queuesState.error)
+ else -> Result.success(false)
+ }
+
+ private fun isMessagingAvailable(queues: List): Boolean = queues.asSequence()
.filterNot { it.state.status == QueueState.Status.CLOSED }
.filterNot { it.state.status == QueueState.Status.UNKNOWN }
.any { it.supportMessaging() }
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/IsSecureEngagementUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/IsSecureEngagementUseCase.kt
deleted file mode 100644
index c681e2712..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/IsSecureEngagementUseCase.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.glia.widgets.core.secureconversations.domain
-
-import com.glia.widgets.chat.ChatType
-import com.glia.widgets.core.engagement.GliaEngagementConfigRepository
-import com.glia.widgets.engagement.domain.IsQueueingOrEngagementUseCase
-
-internal class IsSecureEngagementUseCase(
- private val engagementConfigRepository: GliaEngagementConfigRepository,
- private val isQueueingOrEngagementUseCase: IsQueueingOrEngagementUseCase
-) {
- operator fun invoke(): Boolean {
- return engagementConfigRepository.chatType == ChatType.SECURE_MESSAGING && !isQueueingOrEngagementUseCase()
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/ManageSecureMessagingStatusUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/ManageSecureMessagingStatusUseCase.kt
new file mode 100644
index 000000000..733bca6a7
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/ManageSecureMessagingStatusUseCase.kt
@@ -0,0 +1,16 @@
+package com.glia.widgets.core.secureconversations.domain
+
+import com.glia.widgets.engagement.EngagementRepository
+
+internal class ManageSecureMessagingStatusUseCase(private val engagementRepository: EngagementRepository) {
+
+ val shouldUseSecureMessagingEndpoints: Boolean
+ get() = engagementRepository.isSecureMessagingRequested
+ && !engagementRepository.isQueueingOrLiveEngagement
+ && !engagementRepository.isTransferredSecureConversation
+
+ val shouldBehaveAsSecureMessaging: Boolean
+ get() = engagementRepository.isSecureMessagingRequested || engagementRepository.isTransferredSecureConversation
+
+ fun updateSecureMessagingStatus(isRequested: Boolean) = engagementRepository.updateIsSecureMessagingRequested(isRequested)
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/MarkMessagesReadWithDelayUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/MarkMessagesReadWithDelayUseCase.kt
index 688c6b428..4463b404f 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/MarkMessagesReadWithDelayUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/MarkMessagesReadWithDelayUseCase.kt
@@ -13,7 +13,11 @@ const val DELAY_SEC = 6L
internal class MarkMessagesReadWithDelayUseCase(private val repository: SecureConversationsRepository) {
- operator fun invoke(): Completable = Completable.timer(DELAY_SEC, TimeUnit.SECONDS)
+ operator fun invoke(): Completable = repository.isLeaveSecureConversationDialogVisibleObservable
+ .filter { !it }
+ .take(1)
+ .delay(DELAY_SEC, TimeUnit.SECONDS)
+ .ignoreElements()
.andThen(markMessagesRead())
.doOnComplete { Logger.d(TAG, "Messages successfully marked as read") }
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/RemoveSecureFileAttachmentUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/RemoveSecureFileAttachmentUseCase.kt
deleted file mode 100644
index b1671fe7d..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/RemoveSecureFileAttachmentUseCase.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.glia.widgets.core.secureconversations.domain
-
-import com.glia.widgets.core.fileupload.SecureFileAttachmentRepository
-import com.glia.widgets.core.fileupload.model.LocalAttachment
-
-internal class RemoveSecureFileAttachmentUseCase(private val repository: SecureFileAttachmentRepository) {
- fun execute(attachment: LocalAttachment) {
- repository.detachFile(attachment)
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/ResetMessageCenterUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/ResetMessageCenterUseCase.kt
index 2e4f2296b..055bd83de 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/ResetMessageCenterUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/ResetMessageCenterUseCase.kt
@@ -1,10 +1,10 @@
package com.glia.widgets.core.secureconversations.domain
-import com.glia.widgets.core.fileupload.SecureFileAttachmentRepository
+import com.glia.widgets.core.fileupload.FileAttachmentRepository
import com.glia.widgets.core.secureconversations.SendMessageRepository
internal class ResetMessageCenterUseCase(
- private val fileAttachmentRepository: SecureFileAttachmentRepository,
+ private val fileAttachmentRepository: FileAttachmentRepository,
private val sendMessageRepository: SendMessageRepository
) {
operator fun invoke() {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SecureConversationTopBannerVisibilityUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SecureConversationTopBannerVisibilityUseCase.kt
new file mode 100644
index 000000000..8551676d8
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SecureConversationTopBannerVisibilityUseCase.kt
@@ -0,0 +1,51 @@
+package com.glia.widgets.core.secureconversations.domain
+
+import com.glia.androidsdk.Engagement.MediaType
+import com.glia.androidsdk.queuing.Queue
+import com.glia.androidsdk.queuing.QueueState
+import com.glia.widgets.core.queue.QueueRepository
+import com.glia.widgets.core.queue.QueuesState
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
+import io.reactivex.rxjava3.core.Flowable
+/**
+ * Should show top banner in secure conversations chat only if both conditions are met
+ * - There is an open queue with text, audio or video media
+ * - Has pending secure conversations
+ */
+internal class SecureConversationTopBannerVisibilityUseCase(
+ private val queueRepository: QueueRepository,
+ private val hasOngoingInteraction: HasOngoingSecureConversationUseCase
+) {
+
+ operator fun invoke(): Flowable> = getPendingSecureConversationState()
+ .flatMap { hasPendingSecureConversation ->
+ when (hasPendingSecureConversation) {
+ true -> checkAvailableQueues()
+ else -> Flowable.just(false)
+ }
+ }
+ .map { Result.success(it) }
+ .observeOn(AndroidSchedulers.mainThread())
+ .onErrorReturn { Result.failure(it) }
+ .distinctUntilChanged()
+
+ private fun getPendingSecureConversationState() = hasOngoingInteraction()
+
+ private fun checkAvailableQueues() = queueRepository.queuesState
+ .filter { it !is QueuesState.Loading }
+ .map { receivedState ->
+ when (receivedState) {
+ is QueuesState.Error -> throw receivedState.error
+ else -> receivedState.queuesOrEmpty()
+ }
+ }
+ .map(::hasOpenQueueWithLiveMedia)
+
+ private fun hasOpenQueueWithLiveMedia(queues: List): Boolean = queues.asSequence()
+ .filter { it.state.status == QueueState.Status.OPEN }
+ .any { it.state.medias.hasLiveMediaType() }
+
+
+ private fun Array.hasLiveMediaType() = asSequence()
+ .any { it == MediaType.TEXT || it == MediaType.AUDIO || it == MediaType.VIDEO }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SendMessageButtonStateUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SendMessageButtonStateUseCase.kt
index 91a6e7856..def2b7927 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SendMessageButtonStateUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SendMessageButtonStateUseCase.kt
@@ -1,6 +1,6 @@
package com.glia.widgets.core.secureconversations.domain
-import com.glia.widgets.core.fileupload.SecureFileAttachmentRepository
+import com.glia.widgets.core.fileupload.FileAttachmentRepository
import com.glia.widgets.core.secureconversations.SecureConversationsRepository
import com.glia.widgets.core.secureconversations.SendMessageRepository
import com.glia.widgets.helper.rx.Schedulers
@@ -9,7 +9,7 @@ import io.reactivex.rxjava3.core.Observable
internal class SendMessageButtonStateUseCase(
private val sendMessageRepository: SendMessageRepository,
- private val fileAttachmentRepository: SecureFileAttachmentRepository,
+ private val fileAttachmentRepository: FileAttachmentRepository,
private val secureConversationsRepository: SecureConversationsRepository,
private val showMessageLimitErrorUseCase: ShowMessageLimitErrorUseCase,
private val schedulers: Schedulers
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SendSecureMessageUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SendSecureMessageUseCase.kt
index 5d069fd22..c6197b488 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SendSecureMessageUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SendSecureMessageUseCase.kt
@@ -5,41 +5,38 @@ import com.glia.androidsdk.chat.FilesAttachment
import com.glia.androidsdk.chat.SendMessagePayload
import com.glia.androidsdk.chat.VisitorMessage
import com.glia.widgets.chat.data.GliaChatRepository
-import com.glia.widgets.core.engagement.GliaEngagementConfigRepository
-import com.glia.widgets.core.fileupload.SecureFileAttachmentRepository
+import com.glia.widgets.core.fileupload.FileAttachmentRepository
import com.glia.widgets.core.fileupload.model.LocalAttachment
import com.glia.widgets.core.secureconversations.SecureConversationsRepository
import com.glia.widgets.core.secureconversations.SendMessageRepository
-import com.glia.widgets.engagement.domain.IsQueueingOrEngagementUseCase
+import com.glia.widgets.engagement.domain.IsQueueingOrLiveEngagementUseCase
internal class SendSecureMessageUseCase(
- private val engagementConfigRepository: GliaEngagementConfigRepository,
private val sendMessageRepository: SendMessageRepository,
private val secureConversationsRepository: SecureConversationsRepository,
- private val fileAttachmentRepository: SecureFileAttachmentRepository,
+ private val fileAttachmentRepository: FileAttachmentRepository,
private val chatRepository: GliaChatRepository,
- private val isQueueingOrEngagementUseCase: IsQueueingOrEngagementUseCase
+ private val isQueueingOrLiveEngagementUseCase: IsQueueingOrLiveEngagementUseCase
) {
private val hasOngoingEngagement: Boolean
- get() = isQueueingOrEngagementUseCase.hasOngoingEngagement
+ get() = isQueueingOrLiveEngagementUseCase.hasOngoingLiveEngagement
operator fun invoke(
callback: RequestCallback
) {
val message = sendMessageRepository.value
val fileAttachments = fileAttachmentRepository.getReadyToSendFileAttachments()
- sendMessage(message, engagementConfigRepository.queueIds, fileAttachments, callback)
+ sendMessage(message, fileAttachments, callback)
}
private fun sendMessage(
message: String,
- queueIds: List,
localAttachments: List,
callback: RequestCallback
) {
if (localAttachments.isNotEmpty()) {
- sendMessageWithAttachments(message, queueIds, localAttachments) { result, ex ->
+ sendMessageWithAttachments(message, localAttachments) { result, ex ->
if (ex == null) {
sendMessageRepository.reset()
fileAttachmentRepository.detachFiles(localAttachments)
@@ -47,13 +44,12 @@ internal class SendSecureMessageUseCase(
callback.onResult(result, ex)
}
} else {
- sendMessage(message, queueIds, callback)
+ sendMessage(message, callback)
}
}
private fun sendMessage(
message: String,
- queueIds: List,
callback: RequestCallback
) {
val payload = SendMessagePayload(content = message)
@@ -61,13 +57,12 @@ internal class SendSecureMessageUseCase(
if (hasOngoingEngagement) {
chatRepository.sendMessage(payload, callback)
} else {
- secureConversationsRepository.send(payload, queueIds, callback)
+ secureConversationsRepository.send(payload, callback)
}
}
private fun sendMessageWithAttachments(
message: String,
- queueIds: List,
localAttachments: List,
callback: RequestCallback
) {
@@ -81,7 +76,7 @@ internal class SendSecureMessageUseCase(
if (hasOngoingEngagement) {
chatRepository.sendMessage(payload, callback)
} else {
- secureConversationsRepository.send(payload, queueIds, callback)
+ secureConversationsRepository.send(payload, callback)
}
}
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SetLeaveSecureConversationDialogVisibleUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SetLeaveSecureConversationDialogVisibleUseCase.kt
new file mode 100644
index 000000000..e4ac237b0
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/core/secureconversations/domain/SetLeaveSecureConversationDialogVisibleUseCase.kt
@@ -0,0 +1,16 @@
+package com.glia.widgets.core.secureconversations.domain
+
+import com.glia.widgets.core.secureconversations.SecureConversationsRepository
+
+internal interface SetLeaveSecureConversationDialogVisibleUseCase {
+ operator fun invoke(isVisible: Boolean)
+}
+
+internal class SetLeaveSecureConversationDialogVisibleUseCaseImpl(
+ private val repository: SecureConversationsRepository
+) : SetLeaveSecureConversationDialogVisibleUseCase {
+
+ override operator fun invoke(isVisible: Boolean) {
+ repository.setLeaveSecureConversationDialogVisible(isVisible)
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/visitor/GliaVisitorInfo.kt b/widgetssdk/src/main/java/com/glia/widgets/core/visitor/GliaVisitorInfo.kt
deleted file mode 100644
index 3b36fbc1b..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/core/visitor/GliaVisitorInfo.kt
+++ /dev/null
@@ -1,86 +0,0 @@
-package com.glia.widgets.core.visitor
-
-import com.glia.androidsdk.visitor.VisitorInfo
-import com.glia.widgets.helper.Logger.logDeprecatedClassUse
-import com.glia.widgets.helper.TAG
-import com.google.gson.internal.LinkedTreeMap
-
-/**
- * Deprecated. Use [VisitorInfo] instead
- */
-@Deprecated("since 1.9.0 use @see {@link VisitorInfo}")
-class GliaVisitorInfo(visitorInfo: VisitorInfo) : VisitorInfo {
- private var name: String
- private var email: String
- private var phone: String
- private var note: String
- private var customAttributes: Map
- private var generatedName: String
- private var banned: Boolean
- private var href: String
- private var id: String
- private var externalId: String?
-
- init {
- logDeprecatedClassUse(TAG)
- name = visitorInfo.name
- email = visitorInfo.email
- phone = visitorInfo.phone
- note = visitorInfo.note
- customAttributes = visitorInfo.customAttributesMap
- generatedName = visitorInfo.generatedName
- banned = visitorInfo.banned
- href = visitorInfo.href
- id = visitorInfo.id
- externalId = visitorInfo.externalId
- }
-
- override fun getName(): String {
- return name
- }
-
- override fun getEmail(): String {
- return email
- }
-
- override fun getPhone(): String {
- return phone
- }
-
- override fun getNote(): String {
- return note
- }
-
- /** @deprecated */
- @Deprecated("Will be removed")
- @Suppress("removal")
- override fun getCustomAttributes(): LinkedTreeMap {
- val attrs = LinkedTreeMap()
- attrs.putAll(customAttributes)
- return attrs
- }
-
- override fun getCustomAttributesMap(): Map {
- return customAttributes
- }
-
- override fun getGeneratedName(): String {
- return generatedName
- }
-
- override fun getBanned(): Boolean {
- return banned
- }
-
- override fun getHref(): String {
- return href
- }
-
- override fun getId(): String {
- return id
- }
-
- override fun getExternalId(): String? {
- return externalId
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/core/visitor/VisitorInfoUpdate.java b/widgetssdk/src/main/java/com/glia/widgets/core/visitor/VisitorInfoUpdate.java
deleted file mode 100644
index fc942287f..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/core/visitor/VisitorInfoUpdate.java
+++ /dev/null
@@ -1,107 +0,0 @@
-package com.glia.widgets.core.visitor;
-
-import com.glia.androidsdk.visitor.VisitorInfoUpdateRequest;
-import com.glia.widgets.helper.Logger;
-import com.google.gson.internal.LinkedTreeMap;
-
-import java.util.Map;
-
-/**
- * @deprecated since 1.9.0 use @see {@link VisitorInfoUpdateRequest}
- */
-@Deprecated
-public class VisitorInfoUpdate implements VisitorInfoUpdateRequest {
- private final String TAG = VisitorInfoUpdate.class.getSimpleName();
-
- public String name;
- public String email;
- public String phone;
- public String note;
- public Map customAttributes;
- public VisitorInfoUpdateRequest.NoteUpdateMethod noteUpdateMethod = VisitorInfoUpdateRequest.NoteUpdateMethod.APPEND;
- public VisitorInfoUpdateRequest.CustomAttributesUpdateMethod customAttrsUpdateMethod = VisitorInfoUpdateRequest.CustomAttributesUpdateMethod.MERGE;
- public String externalId;
-
- public VisitorInfoUpdate() {
- Logger.logDeprecatedClassUse(TAG);
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public void setEmail(String email) {
- this.email = email;
- }
-
- public void setPhone(String phone) {
- this.phone = phone;
- }
-
- public void setNote(String note) {
- this.note = note;
- }
-
- public void setCustomAttributes(Map customAttributes) {
- this.customAttributes = customAttributes;
- }
-
- public void setNoteUpdateMethod(NoteUpdateMethod noteUpdateMethod) {
- this.noteUpdateMethod = noteUpdateMethod;
- }
-
- public void setCustomAttrsUpdateMethod(CustomAttributesUpdateMethod customAttrsUpdateMethod) {
- this.customAttrsUpdateMethod = customAttrsUpdateMethod;
- }
-
- public void setExternalId(String externalId) {
- this.externalId = externalId;
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public String getEmail() {
- return email;
- }
-
- @Override
- public String getPhone() {
- return phone;
- }
-
- @Override
- public String getNote() {
- return note;
- }
-
- @Override
- public NoteUpdateMethod getNoteUpdateMethod() {
- return noteUpdateMethod;
- }
-
- @Override
- public CustomAttributesUpdateMethod getCustomAttrsUpdateMethod() {
- return customAttrsUpdateMethod;
- }
-
- @Override
- public LinkedTreeMap getCustomAttributes() {
- LinkedTreeMap attrs = new LinkedTreeMap<>();
- attrs.putAll(customAttributes);
- return attrs;
- }
-
- @Override
- public Map getCustomAttributesMap() {
- return customAttributes;
- }
-
- @Override
- public String getExternalId() {
- return externalId;
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/di/ControllerFactory.java b/widgetssdk/src/main/java/com/glia/widgets/di/ControllerFactory.java
index e06279666..be45dc4db 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/di/ControllerFactory.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/di/ControllerFactory.java
@@ -12,13 +12,15 @@
import com.glia.widgets.callvisualizer.controller.VisitorCodeController;
import com.glia.widgets.chat.ChatContract;
import com.glia.widgets.chat.controller.ChatController;
-import com.glia.widgets.core.configuration.GliaSdkConfigurationManager;
import com.glia.widgets.core.dialog.DialogContract;
import com.glia.widgets.core.dialog.DialogController;
import com.glia.widgets.engagement.completion.EngagementCompletionContract;
import com.glia.widgets.engagement.completion.EngagementCompletionController;
-import com.glia.widgets.filepreview.ui.FilePreviewContract;
-import com.glia.widgets.filepreview.ui.FilePreviewController;
+import com.glia.widgets.entrywidget.EntryWidgetContract;
+import com.glia.widgets.entrywidget.EntryWidgetController;
+import com.glia.widgets.entrywidget.EntryWidgetHideController;
+import com.glia.widgets.filepreview.ui.ImagePreviewContract;
+import com.glia.widgets.filepreview.ui.ImagePreviewController;
import com.glia.widgets.helper.Logger;
import com.glia.widgets.helper.TimeCounter;
import com.glia.widgets.messagecenter.MessageCenterContract;
@@ -55,9 +57,9 @@ public class ControllerFactory {
private final DialogContract.Controller dialogController;
private final MessagesNotSeenHandler messagesNotSeenHandler;
private final UseCaseFactory useCaseFactory;
- private final GliaSdkConfigurationManager sdkConfigurationManager;
- private final FilePreviewContract.Controller filePreviewController;
+ private final ImagePreviewContract.Controller filePreviewController;
private final ManagerFactory managerFactory;
+ private final GliaCore core;
private EngagementCompletionContract.Controller engagementCompletionController;
private ChatContract.Controller retainedChatController;
private CallContract.Controller retainedCallController;
@@ -65,12 +67,13 @@ public class ControllerFactory {
private CallVisualizerContract.Controller callVisualizerController;
private ActivityWatcherForChatHeadController activityWatcherForChatHeadController;
private ActivityWatcherForLiveObservationController activityWatcherForLiveObservationController;
+ private EntryWidgetHideController entryWidgetHideController;
public ControllerFactory(
RepositoryFactory repositoryFactory,
UseCaseFactory useCaseFactory,
- GliaSdkConfigurationManager sdkConfigurationManager,
- ManagerFactory managerFactory
+ ManagerFactory managerFactory,
+ GliaCore core
) {
this.repositoryFactory = repositoryFactory;
messagesNotSeenHandler = new MessagesNotSeenHandler(
@@ -78,13 +81,13 @@ public ControllerFactory(
);
this.useCaseFactory = useCaseFactory;
+ this.core = core;
this.dialogController = new DialogController();
- this.filePreviewController = new FilePreviewController(
+ this.filePreviewController = new ImagePreviewController(
useCaseFactory.createGetImageFileFromDownloadsUseCase(),
useCaseFactory.createGetImageFileFromCacheUseCase(),
useCaseFactory.createPutImageFileToDownloadsUseCase()
);
- this.sdkConfigurationManager = sdkConfigurationManager;
this.managerFactory = managerFactory;
}
@@ -103,7 +106,6 @@ public ChatContract.Controller getChatController() {
useCaseFactory.getEndEngagementUseCase(),
useCaseFactory.createAddFileToAttachmentAndUploadUseCase(),
useCaseFactory.createAddFileAttachmentsObserverUseCase(),
- useCaseFactory.createRemoveFileAttachmentObserverUseCase(),
useCaseFactory.createGetFileAttachmentsUseCase(),
useCaseFactory.createRemoveFileAttachmentUseCase(),
useCaseFactory.createSupportedFileCountCheckUseCase(),
@@ -113,9 +115,7 @@ public ChatContract.Controller getChatController() {
useCaseFactory.createSiteInfoUseCase(),
useCaseFactory.createIsFromCallScreenUseCase(),
useCaseFactory.createUpdateFromCallScreenUseCase(),
- useCaseFactory.createIsSecureEngagementUseCase(),
- useCaseFactory.createSetEngagementConfigUseCase(),
- useCaseFactory.createSecureMessagingAvailableQueueIdsUseCase(),
+ useCaseFactory.createManageSecureMessagingStatusUseCase(),
useCaseFactory.getIsCurrentEngagementCallVisualizer(),
useCaseFactory.createIsFileReadyForPreviewUseCase(),
useCaseFactory.createDetermineGvaButtonTypeUseCase(),
@@ -137,7 +137,10 @@ public ChatContract.Controller getChatController() {
useCaseFactory.getWithReadWritePermissionsUseCase(),
useCaseFactory.getRequestNotificationPermissionIfPushNotificationsSetUpUseCase(),
useCaseFactory.getReleaseResourcesUseCase(getDialogController()),
- useCaseFactory.createGetUrlFromLinkUseCase()
+ useCaseFactory.createGetUrlFromLinkUseCase(),
+ useCaseFactory.createIsMessagingAvailableUseCase(),
+ useCaseFactory.createSecureConversationTopBannerVisibilityUseCase(),
+ useCaseFactory.createSetLeaveSecureConversationDialogVisibleUseCase()
);
}
@@ -148,7 +151,6 @@ public CallContract.Controller getCallController() {
if (retainedCallController == null) {
Logger.d(TAG, "new call controller");
retainedCallController = new CallController(
- sdkConfigurationManager,
sharedTimer,
new TimeCounter(),
new TimeCounter(),
@@ -227,7 +229,7 @@ public void init() {
getActivityWatcherForChatHeadController().init();
}
- public FilePreviewContract.Controller getImagePreviewController() {
+ public ImagePreviewContract.Controller getImagePreviewController() {
return filePreviewController;
}
@@ -290,14 +292,11 @@ public CallVisualizerContract.Controller getCallVisualizerController() {
public MessageCenterContract.Controller getMessageCenterController() {
return new MessageCenterController(
- serviceChatHeadController,
- useCaseFactory.createSetEngagementConfigUseCase(),
useCaseFactory.createSendSecureMessageUseCase(),
- useCaseFactory.createSecureMessagingAvailableQueueIdsUseCase(),
- useCaseFactory.createAddSecureFileAttachmentsObserverUseCase(),
+ useCaseFactory.createAddFileAttachmentsObserverUseCase(),
useCaseFactory.createAddSecureFileToAttachmentAndUploadUseCase(),
- useCaseFactory.createGetSecureFileAttachmentsUseCase(),
- useCaseFactory.createRemoveSecureFileAttachmentUseCase(),
+ useCaseFactory.createGetFileAttachmentsUseCase(),
+ useCaseFactory.createRemoveFileAttachmentUseCase(),
useCaseFactory.createIsAuthenticatedUseCase(),
useCaseFactory.createSiteInfoUseCase(),
useCaseFactory.createOnNextMessageUseCase(),
@@ -307,7 +306,8 @@ public MessageCenterContract.Controller getMessageCenterController() {
createDialogController(),
useCaseFactory.getTakePictureUseCase(),
useCaseFactory.getUriToFileAttachmentUseCase(),
- useCaseFactory.getRequestNotificationPermissionIfPushNotificationsSetUpUseCase()
+ useCaseFactory.getRequestNotificationPermissionIfPushNotificationsSetUpUseCase(),
+ useCaseFactory.createIsMessagingAvailableUseCase()
);
}
@@ -379,10 +379,27 @@ public OperatorRequestContract.Controller getOperatorRequestController() {
useCaseFactory.getIsCurrentEngagementCallVisualizer(),
useCaseFactory.createSetOverlayPermissionRequestDialogShownUseCase(),
dialogController,
- sdkConfigurationManager,
useCaseFactory.getWithNotificationPermissionUseCase(),
useCaseFactory.getPrepareToScreenSharingUseCase(),
useCaseFactory.getReleaseScreenSharingResourcesUseCase()
);
}
+
+ public EntryWidgetContract.Controller getEntryWidgetController() {
+ return new EntryWidgetController(
+ repositoryFactory.getQueueRepository(),
+ useCaseFactory.createIsAuthenticatedUseCase(),
+ repositoryFactory.getSecureConversationsRepository(),
+ useCaseFactory.getHasPendingSecureConversationsWithTimeoutUseCase(),
+ core,
+ Dependencies.getEngagementLauncher()
+ );
+ }
+
+ public EntryWidgetHideController getEntryWidgetHideController() {
+ if (entryWidgetHideController == null) {
+ entryWidgetHideController = new EntryWidgetHideController();
+ }
+ return entryWidgetHideController;
+ }
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/di/Dependencies.kt b/widgetssdk/src/main/java/com/glia/widgets/di/Dependencies.kt
index 2ecd9303b..9f66a6908 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/di/Dependencies.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/di/Dependencies.kt
@@ -5,7 +5,6 @@ import android.os.Build
import androidx.annotation.VisibleForTesting
import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleOwner
import com.glia.androidsdk.Glia
import com.glia.androidsdk.GliaConfig
import com.glia.widgets.GliaWidgetsConfig
@@ -16,20 +15,26 @@ import com.glia.widgets.core.audio.domain.OnAudioStartedUseCase
import com.glia.widgets.core.authentication.AuthenticationManager
import com.glia.widgets.core.callvisualizer.CallVisualizerManager
import com.glia.widgets.core.chathead.ChatHeadManager
-import com.glia.widgets.core.configuration.GliaSdkConfigurationManager
import com.glia.widgets.core.dialog.PermissionDialogManager
import com.glia.widgets.core.notification.device.INotificationManager
import com.glia.widgets.core.notification.device.NotificationManager
import com.glia.widgets.core.permissions.PermissionManager
import com.glia.widgets.engagement.completion.EngagementCompletionActivityWatcher
+import com.glia.widgets.entrywidget.EntryWidget
+import com.glia.widgets.entrywidget.EntryWidgetImpl
import com.glia.widgets.filepreview.data.source.local.DownloadsFolderDataSource
import com.glia.widgets.helper.ApplicationLifecycleManager
import com.glia.widgets.helper.GliaActivityManagerImpl
-import com.glia.widgets.helper.IntentConfigurationHelper
-import com.glia.widgets.helper.IntentConfigurationHelperImpl
+import com.glia.widgets.helper.IntentHelperImpl
import com.glia.widgets.helper.ResourceProvider
import com.glia.widgets.helper.rx.GliaWidgetsSchedulers
import com.glia.widgets.helper.rx.Schedulers
+import com.glia.widgets.launcher.ActivityLauncher
+import com.glia.widgets.launcher.ActivityLauncherImpl
+import com.glia.widgets.launcher.ConfigurationManager
+import com.glia.widgets.launcher.ConfigurationManagerImpl
+import com.glia.widgets.launcher.EngagementLauncher
+import com.glia.widgets.launcher.EngagementLauncherImpl
import com.glia.widgets.locale.LocaleProvider
import com.glia.widgets.operator.OperatorRequestActivityWatcher
import com.glia.widgets.permissions.ActivityWatcherForPermissionsRequest
@@ -43,19 +48,23 @@ internal object Dependencies {
@JvmStatic
val gliaThemeManager = UnifiedThemeManager()
private val authenticationManagerProvider = AuthenticationManagerProvider()
+
@JvmStatic
lateinit var controllerFactory: ControllerFactory
@VisibleForTesting set
private lateinit var notificationManager: INotificationManager
+
@JvmStatic
lateinit var callVisualizerManager: CallVisualizerManager
private set
+
@JvmStatic
lateinit var useCaseFactory: UseCaseFactory
@VisibleForTesting set
private lateinit var managerFactory: ManagerFactory
var gliaCore: GliaCore = GliaCoreImpl()
@VisibleForTesting set
+
@JvmStatic
lateinit var resourceProvider: ResourceProvider
@VisibleForTesting set
@@ -63,10 +72,28 @@ internal object Dependencies {
@VisibleForTesting set
var schedulers: Schedulers = GliaWidgetsSchedulers()
@VisibleForTesting set
+
@JvmStatic
- var sdkConfigurationManager: GliaSdkConfigurationManager = GliaSdkConfigurationManager()
- @VisibleForTesting set
- val intentConfigurationHelper: IntentConfigurationHelper = IntentConfigurationHelperImpl()
+ val configurationManager: ConfigurationManager by lazy { ConfigurationManagerImpl() }
+
+ val activityLauncher: ActivityLauncher by lazy {
+ ActivityLauncherImpl(IntentHelperImpl(), repositoryFactory.engagementRepository)
+ }
+
+ @JvmStatic
+ val engagementLauncher: EngagementLauncher by lazy {
+ EngagementLauncherImpl(activityLauncher, useCaseFactory.hasPendingSecureConversationsWithTimeoutUseCase, configurationManager)
+ }
+
+ @JvmStatic
+ val entryWidget: EntryWidget
+ get() = EntryWidgetImpl(
+ activityLauncher,
+ gliaThemeManager,
+ controllerFactory.entryWidgetHideController,
+ useCaseFactory.hasPendingSecureConversationsWithTimeoutUseCase
+ )
+
@JvmStatic
lateinit var repositoryFactory: RepositoryFactory
@VisibleForTesting set
@@ -77,7 +104,7 @@ internal object Dependencies {
localeProvider = LocaleProvider(resourceProvider)
notificationManager = NotificationManager(application)
val downloadsFolderDataSource = DownloadsFolderDataSource(application)
- repositoryFactory = RepositoryFactory(gliaCore, downloadsFolderDataSource)
+ repositoryFactory = RepositoryFactory(gliaCore, downloadsFolderDataSource, configurationManager)
val permissionManager = PermissionManager(
application,
@@ -91,7 +118,7 @@ internal object Dependencies {
permissionManager,
PermissionDialogManager(application),
notificationManager,
- sdkConfigurationManager,
+ configurationManager,
ChatHeadManager(application),
audioControlManager,
authenticationManagerProvider,
@@ -107,8 +134,8 @@ internal object Dependencies {
controllerFactory = ControllerFactory(
repositoryFactory,
useCaseFactory,
- sdkConfigurationManager,
- managerFactory
+ managerFactory,
+ gliaCore
)
initApplicationLifecycleObserver(
ApplicationLifecycleManager(),
@@ -119,14 +146,16 @@ internal object Dependencies {
controllerFactory.callVisualizerController,
GliaActivityManagerImpl(),
localeProvider,
- gliaThemeManager
+ gliaThemeManager,
+ activityLauncher
)
application.registerActivityLifecycleCallbacks(callVisualizerActivityWatcher)
val activityWatcherForChatHead = ActivityWatcherForChatHead(
- controllerFactory.activityWatcherForChatHeadController
- )
+ controllerFactory.activityWatcherForChatHeadController,
+ activityLauncher
+ )
application.registerActivityLifecycleCallbacks(activityWatcherForChatHead)
val activityWatcherForLiveObservation = ActivityWatcherForLiveObservation(
@@ -137,8 +166,8 @@ internal object Dependencies {
application.registerActivityLifecycleCallbacks(activityWatcherForLiveObservation)
val activityWatcherForPermissionsRequest = ActivityWatcherForPermissionsRequest(
- controllerFactory.permissionsController
- )
+ controllerFactory.permissionsController
+ )
application.registerActivityLifecycleCallbacks(activityWatcherForPermissionsRequest)
callVisualizerManager = CallVisualizerManager(
@@ -148,13 +177,14 @@ internal object Dependencies {
val engagementCompletionActivityWatcher = EngagementCompletionActivityWatcher(
controllerFactory.endEngagementController,
- GliaActivityManagerImpl()
+ GliaActivityManagerImpl(),
+ activityLauncher
)
application.registerActivityLifecycleCallbacks(engagementCompletionActivityWatcher)
val operatorRequestActivityWatcher = OperatorRequestActivityWatcher(
controllerFactory.operatorRequestController,
- intentConfigurationHelper,
+ activityLauncher,
GliaActivityManagerImpl()
)
application.registerActivityLifecycleCallbacks(operatorRequestActivityWatcher)
@@ -165,8 +195,8 @@ internal object Dependencies {
val gliaConfig = createGliaConfig(gliaWidgetsConfig)
gliaCore.init(gliaConfig)
controllerFactory.init()
- repositoryFactory.engagementRepository.initialize()
- sdkConfigurationManager.fromConfiguration(gliaWidgetsConfig)
+ repositoryFactory.initialize()
+ configurationManager.applyConfiguration(gliaWidgetsConfig)
localeProvider.setCompanyName(gliaWidgetsConfig.companyName)
}
@@ -202,7 +232,7 @@ internal object Dependencies {
lifecycleManager: ApplicationLifecycleManager,
chatBubbleController: ChatHeadContract.Controller
) {
- lifecycleManager.addObserver { source: LifecycleOwner?, event: Lifecycle.Event ->
+ lifecycleManager.addObserver { _, event: Lifecycle.Event ->
if (event == Lifecycle.Event.ON_STOP) {
chatBubbleController.onApplicationStop()
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S && Glia.isInitialized()) {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/di/GliaCore.kt b/widgetssdk/src/main/java/com/glia/widgets/di/GliaCore.kt
index 8723a8322..9b91c31ad 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/di/GliaCore.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/di/GliaCore.kt
@@ -41,11 +41,11 @@ internal interface GliaCore {
fun off(event: OmnicoreEvent, listener: Consumer)
fun off(event: OmnicoreEvent)
fun fetchFile(attachmentFile: AttachmentFile, callback: RequestCallback)
- fun getChatHistory(callback: RequestCallback?>)
- fun getQueues(requestCallback: RequestCallback?>)
+ fun getChatHistory(callback: RequestCallback?>)
+ fun getQueues(requestCallback: RequestCallback?>)
fun queueForEngagement(
- queueIds: Array,
+ queueIds: List,
mediaType: Engagement.MediaType,
visitorContextAssetId: String?,
engagementOptions: EngagementOptions?,
@@ -62,4 +62,13 @@ internal interface GliaCore {
fun getSiteInfo(callback: RequestCallback)
fun getOperator(operatorId: String, callback: RequestCallback)
fun getAuthenticationManager(behavior: Authentication.Behavior): AuthenticationManager
+
+ fun ensureInitialized() {
+ if (!isInitialized) {
+ throw GliaException("Glia SDK is not initialized", GliaException.Cause.INVALID_INPUT)
+ }
+ }
+
+ fun subscribeToQueueStateUpdates(queueIds: List, onError: Consumer, callback: Consumer)
+ fun unsubscribeFromQueueUpdates(onError: Consumer?, callback: Consumer)
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/di/GliaCoreImpl.kt b/widgetssdk/src/main/java/com/glia/widgets/di/GliaCoreImpl.kt
index 9065bb98b..5c6c1510e 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/di/GliaCoreImpl.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/di/GliaCoreImpl.kt
@@ -78,23 +78,23 @@ internal class GliaCoreImpl : GliaCore {
Glia.fetchFile(attachmentFile, callback)
}
- override fun getChatHistory(callback: RequestCallback?>) {
- Glia.getChatHistory(callback)
+ override fun getChatHistory(callback: RequestCallback?>) {
+ Glia.getChatHistory { messages, exception -> callback.onResult(messages?.toList(), exception) }
}
- override fun getQueues(requestCallback: RequestCallback?>) {
- Glia.getQueues(requestCallback)
+ override fun getQueues(requestCallback: RequestCallback?>) {
+ Glia.getQueues { queues, exception -> requestCallback.onResult(queues?.toList(), exception) }
}
override fun queueForEngagement(
- queueIds: Array,
+ queueIds: List,
mediaType: Engagement.MediaType,
visitorContextAssetId: String?,
engagementOptions: EngagementOptions?,
mediaPermissionRequestCode: Int,
callback: Consumer
) {
- Glia.queueForEngagement(queueIds, mediaType, visitorContextAssetId, engagementOptions, mediaPermissionRequestCode, callback)
+ Glia.queueForEngagement(queueIds.toTypedArray(), mediaType, visitorContextAssetId, engagementOptions, mediaPermissionRequestCode, callback)
}
override fun cancelQueueTicket(queueTicketId: String, callback: Consumer) {
@@ -128,4 +128,12 @@ internal class GliaCoreImpl : GliaCore {
override fun getAuthenticationManager(behavior: Authentication.Behavior): AuthenticationManager {
return AuthenticationManager(Glia.getAuthentication(behavior))
}
+
+ override fun subscribeToQueueStateUpdates(queueIds: List, onError: Consumer, callback: Consumer) {
+ Glia.subscribeToQueueStateUpdates(queueIds.toTypedArray(), onError, callback)
+ }
+
+ override fun unsubscribeFromQueueUpdates(onError: Consumer?, callback: Consumer) {
+ Glia.unsubscribeFromQueueUpdates(onError, callback)
+ }
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/di/ManagerFactory.kt b/widgetssdk/src/main/java/com/glia/widgets/di/ManagerFactory.kt
index 7bc36f103..4abce5d23 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/di/ManagerFactory.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/di/ManagerFactory.kt
@@ -15,7 +15,7 @@ internal class ManagerFactory(private val useCaseFactory: UseCaseFactory) {
sendUnsentMessagesUseCase = createSendUnsentMessagesUseCase(),
handleCustomCardClickUseCase = createHandleCustomCardClickUseCase(),
isAuthenticatedUseCase = createIsAuthenticatedUseCase(),
- isQueueingOrEngagementUseCase = isQueueingOrEngagementUseCase
+ isQueueingOrLiveEngagementUseCase = isQueueingOrEngagementUseCase
)
}
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/di/RepositoryFactory.java b/widgetssdk/src/main/java/com/glia/widgets/di/RepositoryFactory.java
index 56d12b343..e1bd407df 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/di/RepositoryFactory.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/di/RepositoryFactory.java
@@ -4,12 +4,12 @@
import com.glia.widgets.chat.data.ChatScreenRepositoryImpl;
import com.glia.widgets.chat.data.GliaChatRepository;
import com.glia.widgets.core.callvisualizer.domain.VisitorCodeRepository;
-import com.glia.widgets.core.engagement.GliaEngagementConfigRepository;
import com.glia.widgets.core.engagement.GliaOperatorRepository;
import com.glia.widgets.core.engagement.GliaOperatorRepositoryImpl;
import com.glia.widgets.core.fileupload.FileAttachmentRepository;
-import com.glia.widgets.core.fileupload.SecureFileAttachmentRepository;
-import com.glia.widgets.core.queue.GliaQueueRepository;
+import com.glia.widgets.core.fileupload.FileAttachmentRepositoryImpl;
+import com.glia.widgets.core.queue.QueueRepository;
+import com.glia.widgets.core.queue.QueueRepositoryImpl;
import com.glia.widgets.core.secureconversations.SecureConversationsRepository;
import com.glia.widgets.core.secureconversations.SendMessageRepository;
import com.glia.widgets.core.survey.GliaSurveyRepository;
@@ -19,6 +19,7 @@
import com.glia.widgets.filepreview.data.GliaFileRepositoryImpl;
import com.glia.widgets.filepreview.data.source.local.DownloadsFolderDataSource;
import com.glia.widgets.filepreview.data.source.local.InAppBitmapCache;
+import com.glia.widgets.launcher.ConfigurationManager;
import com.glia.widgets.permissions.PermissionsRequestRepository;
/**
@@ -26,57 +27,56 @@
*/
public class RepositoryFactory {
private static SecureConversationsRepository secureConversationsRepository;
- private static SecureFileAttachmentRepository secureFileAttachmentRepository;
- private static GliaQueueRepository gliaQueueRepository;
+ private static QueueRepository queueRepository;
private static GliaFileRepository gliaFileRepository;
private static FileAttachmentRepository fileAttachmentRepository;
private static GliaOperatorRepository operatorRepository;
- private static GliaEngagementConfigRepository engagementConfigRepository;
private static SendMessageRepository sendMessageRepository;
private static VisitorCodeRepository visitorCodeRepository;
private static PermissionsRequestRepository permissionsRequestRepository;
private final GliaCore gliaCore;
private final DownloadsFolderDataSource downloadsFolderDataSource;
+ private final ConfigurationManager configurationManager;
private ChatScreenRepository chatScreenRepository;
private EngagementRepository engagementRepository;
public RepositoryFactory(
GliaCore gliaCore,
- DownloadsFolderDataSource downloadsFolderDataSource
+ DownloadsFolderDataSource downloadsFolderDataSource,
+ ConfigurationManager configurationManager
) {
this.downloadsFolderDataSource = downloadsFolderDataSource;
this.gliaCore = gliaCore;
+ this.configurationManager = configurationManager;
+ }
+
+ public void initialize() {
+ getEngagementRepository().initialize();
+ getQueueRepository().initialize();
+ getSecureConversationsRepository().initialize();
}
public GliaChatRepository getGliaMessageRepository() {
return new GliaChatRepository(gliaCore);
}
- public GliaQueueRepository getGliaQueueRepository() {
- if (gliaQueueRepository == null) {
- gliaQueueRepository = new GliaQueueRepository(gliaCore);
+ public QueueRepository getQueueRepository() {
+ if (queueRepository == null) {
+ queueRepository = new QueueRepositoryImpl(gliaCore, configurationManager);
}
- return gliaQueueRepository;
+ return queueRepository;
}
public FileAttachmentRepository getGliaFileAttachmentRepository() {
if (fileAttachmentRepository == null) {
- fileAttachmentRepository = new FileAttachmentRepository(
- gliaCore,
- getEngagementConfigRepository()
- );
+ fileAttachmentRepository = new FileAttachmentRepositoryImpl(gliaCore);
}
return fileAttachmentRepository;
}
public GliaFileRepository getGliaFileRepository() {
if (gliaFileRepository == null) {
- gliaFileRepository = new GliaFileRepositoryImpl(
- InAppBitmapCache.getInstance(),
- downloadsFolderDataSource,
- gliaCore,
- getEngagementConfigRepository()
- );
+ gliaFileRepository = new GliaFileRepositoryImpl(InAppBitmapCache.getInstance(), downloadsFolderDataSource, gliaCore);
}
return gliaFileRepository;
}
@@ -109,25 +109,11 @@ public VisitorCodeRepository getVisitorCodeRepository() {
public SecureConversationsRepository getSecureConversationsRepository() {
if (secureConversationsRepository == null) {
- secureConversationsRepository = new SecureConversationsRepository(gliaCore.getSecureConversations());
+ secureConversationsRepository = new SecureConversationsRepository(gliaCore, getQueueRepository());
}
return secureConversationsRepository;
}
- public SecureFileAttachmentRepository getSecureFileAttachmentRepository() {
- if (secureFileAttachmentRepository == null) {
- secureFileAttachmentRepository = new SecureFileAttachmentRepository(gliaCore);
- }
- return secureFileAttachmentRepository;
- }
-
- public GliaEngagementConfigRepository getEngagementConfigRepository() {
- if (engagementConfigRepository == null) {
- engagementConfigRepository = new GliaEngagementConfigRepository();
- }
- return engagementConfigRepository;
- }
-
public SendMessageRepository getSendMessageRepository() {
if (sendMessageRepository == null) {
sendMessageRepository = new SendMessageRepository();
@@ -144,7 +130,12 @@ public PermissionsRequestRepository getPermissionsRequestRepository() {
public EngagementRepository getEngagementRepository() {
if (engagementRepository == null) {
- engagementRepository = new EngagementRepositoryImpl(gliaCore, getOperatorRepository());
+ engagementRepository = new EngagementRepositoryImpl(
+ gliaCore,
+ getOperatorRepository(),
+ getQueueRepository(),
+ configurationManager
+ );
}
return engagementRepository;
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/di/UseCaseFactory.java b/widgetssdk/src/main/java/com/glia/widgets/di/UseCaseFactory.java
index 1d91c3c77..cd8a7b909 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/di/UseCaseFactory.java
+++ b/widgetssdk/src/main/java/com/glia/widgets/di/UseCaseFactory.java
@@ -66,9 +66,8 @@
import com.glia.widgets.core.callvisualizer.domain.VisitorCodeViewBuilderUseCase;
import com.glia.widgets.core.chathead.ChatHeadManager;
import com.glia.widgets.core.chathead.domain.IsDisplayBubbleInsideAppUseCase;
-import com.glia.widgets.core.chathead.domain.ResolveChatHeadNavigationUseCase;
import com.glia.widgets.core.chathead.domain.IsDisplayBubbleOutsideAppUseCase;
-import com.glia.widgets.core.configuration.GliaSdkConfigurationManager;
+import com.glia.widgets.core.chathead.domain.ResolveChatHeadNavigationUseCase;
import com.glia.widgets.core.dialog.DialogContract;
import com.glia.widgets.core.dialog.PermissionDialogManager;
import com.glia.widgets.core.dialog.domain.ConfirmationDialogLinksUseCase;
@@ -79,13 +78,11 @@
import com.glia.widgets.core.engagement.domain.ConfirmationDialogUseCase;
import com.glia.widgets.core.engagement.domain.GetOperatorUseCase;
import com.glia.widgets.core.engagement.domain.MapOperatorUseCase;
-import com.glia.widgets.core.engagement.domain.SetEngagementConfigUseCase;
import com.glia.widgets.core.engagement.domain.ShouldShowMediaEngagementViewUseCase;
import com.glia.widgets.core.engagement.domain.UpdateOperatorDefaultImageUrlUseCase;
import com.glia.widgets.core.fileupload.domain.AddFileAttachmentsObserverUseCase;
import com.glia.widgets.core.fileupload.domain.AddFileToAttachmentAndUploadUseCase;
import com.glia.widgets.core.fileupload.domain.GetFileAttachmentsUseCase;
-import com.glia.widgets.core.fileupload.domain.RemoveFileAttachmentObserverUseCase;
import com.glia.widgets.core.fileupload.domain.RemoveFileAttachmentUseCase;
import com.glia.widgets.core.fileupload.domain.SupportedFileCountCheckUseCase;
import com.glia.widgets.core.notification.device.INotificationManager;
@@ -103,19 +100,18 @@
import com.glia.widgets.core.permissions.domain.WithNotificationPermissionUseCaseImpl;
import com.glia.widgets.core.permissions.domain.WithReadWritePermissionsUseCase;
import com.glia.widgets.core.permissions.domain.WithReadWritePermissionsUseCaseImpl;
-import com.glia.widgets.core.secureconversations.domain.AddSecureFileAttachmentsObserverUseCase;
import com.glia.widgets.core.secureconversations.domain.AddSecureFileToAttachmentAndUploadUseCase;
-import com.glia.widgets.core.secureconversations.domain.GetSecureFileAttachmentsUseCase;
-import com.glia.widgets.core.secureconversations.domain.GetUnreadMessagesCountWithTimeoutUseCase;
+import com.glia.widgets.core.secureconversations.domain.HasOngoingSecureConversationUseCase;
import com.glia.widgets.core.secureconversations.domain.IsMessagingAvailableUseCase;
-import com.glia.widgets.core.secureconversations.domain.IsSecureEngagementUseCase;
+import com.glia.widgets.core.secureconversations.domain.ManageSecureMessagingStatusUseCase;
import com.glia.widgets.core.secureconversations.domain.MarkMessagesReadWithDelayUseCase;
import com.glia.widgets.core.secureconversations.domain.OnNextMessageUseCase;
-import com.glia.widgets.core.secureconversations.domain.RemoveSecureFileAttachmentUseCase;
import com.glia.widgets.core.secureconversations.domain.ResetMessageCenterUseCase;
-import com.glia.widgets.core.secureconversations.domain.GetAvailableQueueIdsForSecureMessagingUseCase;
+import com.glia.widgets.core.secureconversations.domain.SecureConversationTopBannerVisibilityUseCase;
import com.glia.widgets.core.secureconversations.domain.SendMessageButtonStateUseCase;
import com.glia.widgets.core.secureconversations.domain.SendSecureMessageUseCase;
+import com.glia.widgets.core.secureconversations.domain.SetLeaveSecureConversationDialogVisibleUseCase;
+import com.glia.widgets.core.secureconversations.domain.SetLeaveSecureConversationDialogVisibleUseCaseImpl;
import com.glia.widgets.core.secureconversations.domain.ShowMessageLimitErrorUseCase;
import com.glia.widgets.core.survey.domain.GliaSurveyAnswerUseCase;
import com.glia.widgets.engagement.domain.AcceptMediaUpgradeOfferUseCase;
@@ -148,16 +144,16 @@
import com.glia.widgets.engagement.domain.IsCurrentEngagementCallVisualizerUseCaseImpl;
import com.glia.widgets.engagement.domain.IsOperatorPresentUseCase;
import com.glia.widgets.engagement.domain.IsOperatorPresentUseCaseImpl;
-import com.glia.widgets.engagement.domain.IsQueueingOrEngagementUseCase;
-import com.glia.widgets.engagement.domain.IsQueueingOrEngagementUseCaseImpl;
+import com.glia.widgets.engagement.domain.IsQueueingOrLiveEngagementUseCase;
+import com.glia.widgets.engagement.domain.IsQueueingOrLiveEngagementUseCaseImpl;
+import com.glia.widgets.engagement.domain.OnIncomingEngagementRequestTimeoutUseCase;
+import com.glia.widgets.engagement.domain.OnIncomingEngagementRequestTimeoutUseCaseImpl;
import com.glia.widgets.engagement.domain.OperatorMediaUpgradeOfferUseCase;
import com.glia.widgets.engagement.domain.OperatorMediaUpgradeOfferUseCaseImpl;
import com.glia.widgets.engagement.domain.OperatorMediaUseCase;
import com.glia.widgets.engagement.domain.OperatorMediaUseCaseImpl;
import com.glia.widgets.engagement.domain.OperatorTypingUseCase;
import com.glia.widgets.engagement.domain.OperatorTypingUseCaseImpl;
-import com.glia.widgets.engagement.domain.OnIncomingEngagementRequestTimeoutUseCase;
-import com.glia.widgets.engagement.domain.OnIncomingEngagementRequestTimeoutUseCaseImpl;
import com.glia.widgets.engagement.domain.PrepareToScreenSharingUseCase;
import com.glia.widgets.engagement.domain.PrepareToScreenSharingUseCaseImpl;
import com.glia.widgets.engagement.domain.ReleaseResourcesUseCase;
@@ -185,6 +181,7 @@
import com.glia.widgets.filepreview.domain.usecase.IsFileReadyForPreviewUseCase;
import com.glia.widgets.filepreview.domain.usecase.PutImageFileToDownloadsUseCase;
import com.glia.widgets.helper.rx.Schedulers;
+import com.glia.widgets.launcher.ConfigurationManager;
import com.glia.widgets.locale.LocaleProvider;
import com.glia.widgets.push.notifications.IsPushNotificationsSetUpUseCase;
import com.glia.widgets.push.notifications.IsPushNotificationsSetUpUseCaseImpl;
@@ -208,7 +205,7 @@ public class UseCaseFactory {
private final RepositoryFactory repositoryFactory;
private final PermissionManager permissionManager;
private final PermissionDialogManager permissionDialogManager;
- private final GliaSdkConfigurationManager gliaSdkConfigurationManager;
+ private final ConfigurationManager configurationManager;
private final INotificationManager notificationManager;
private final ChatHeadManager chatHeadManager;
private final AudioControlManager audioControlManager;
@@ -223,7 +220,7 @@ public class UseCaseFactory {
PermissionManager permissionManager,
PermissionDialogManager permissionDialogManager,
INotificationManager notificationManager,
- GliaSdkConfigurationManager gliaSdkConfigurationManager,
+ ConfigurationManager configurationManager,
ChatHeadManager chatHeadManager,
AudioControlManager audioControlManager,
Dependencies.AuthenticationManagerProvider authenticationManagerProvider,
@@ -235,7 +232,7 @@ public class UseCaseFactory {
this.permissionManager = permissionManager;
this.permissionDialogManager = permissionDialogManager;
this.notificationManager = notificationManager;
- this.gliaSdkConfigurationManager = gliaSdkConfigurationManager;
+ this.configurationManager = configurationManager;
this.chatHeadManager = chatHeadManager;
this.audioControlManager = audioControlManager;
this.schedulers = schedulers;
@@ -278,7 +275,7 @@ public IsDisplayBubbleOutsideAppUseCase getToggleChatHeadServiceUseCase() {
getScreenSharingUseCase(),
chatHeadManager,
permissionManager,
- gliaSdkConfigurationManager,
+ configurationManager,
getEngagementTypeUseCase()
);
}
@@ -293,7 +290,7 @@ public IsDisplayBubbleInsideAppUseCase getIsDisplayApplicationChatHeadUseCase()
getIsCurrentEngagementCallVisualizer(),
getScreenSharingUseCase(),
permissionManager,
- gliaSdkConfigurationManager,
+ configurationManager,
getEngagementTypeUseCase()
);
}
@@ -346,9 +343,8 @@ public GliaLoadHistoryUseCase createGliaLoadHistoryUseCase() {
return new GliaLoadHistoryUseCase(
repositoryFactory.getGliaMessageRepository(),
repositoryFactory.getSecureConversationsRepository(),
- createIsSecureEngagementUseCase(),
- getMapOperatorUseCase(),
- createSubscribeToUnreadMessagesCountUseCase()
+ createManageSecureMessagingStatusUseCase(),
+ getMapOperatorUseCase()
);
}
@@ -376,9 +372,8 @@ public GliaSendMessageUseCase createGliaSendMessageUseCase() {
repositoryFactory.getGliaMessageRepository(),
repositoryFactory.getGliaFileAttachmentRepository(),
getIsOperatorPresentUseCase(),
- repositoryFactory.getEngagementConfigRepository(),
repositoryFactory.getSecureConversationsRepository(),
- createIsSecureEngagementUseCase()
+ createManageSecureMessagingStatusUseCase()
);
}
@@ -392,7 +387,10 @@ public ShouldShowMediaEngagementViewUseCase createShouldShowMediaEngagementViewU
@NonNull
public AddFileAttachmentsObserverUseCase createAddFileAttachmentsObserverUseCase() {
- return new AddFileAttachmentsObserverUseCase(repositoryFactory.getGliaFileAttachmentRepository());
+ return new AddFileAttachmentsObserverUseCase(
+ repositoryFactory.getGliaFileAttachmentRepository(),
+ schedulers
+ );
}
@NonNull
@@ -400,7 +398,7 @@ public AddFileToAttachmentAndUploadUseCase createAddFileToAttachmentAndUploadUse
return new AddFileToAttachmentAndUploadUseCase(
getIsQueueingOrEngagementUseCase(),
repositoryFactory.getGliaFileAttachmentRepository(),
- repositoryFactory.getEngagementConfigRepository()
+ createManageSecureMessagingStatusUseCase()
);
}
@@ -409,11 +407,6 @@ public GetFileAttachmentsUseCase createGetFileAttachmentsUseCase() {
return new GetFileAttachmentsUseCase(repositoryFactory.getGliaFileAttachmentRepository());
}
- @NonNull
- public RemoveFileAttachmentObserverUseCase createRemoveFileAttachmentObserverUseCase() {
- return new RemoveFileAttachmentObserverUseCase(repositoryFactory.getGliaFileAttachmentRepository());
- }
-
@NonNull
public RemoveFileAttachmentUseCase createRemoveFileAttachmentUseCase() {
return new RemoveFileAttachmentUseCase(repositoryFactory.getGliaFileAttachmentRepository());
@@ -429,13 +422,13 @@ public IsShowSendButtonUseCase createIsShowSendButtonUseCase() {
return new IsShowSendButtonUseCase(
getIsQueueingOrEngagementUseCase(),
repositoryFactory.getGliaFileAttachmentRepository(),
- createIsSecureEngagementUseCase()
+ createManageSecureMessagingStatusUseCase()
);
}
@NonNull
public IsShowOverlayPermissionRequestDialogUseCase createIsShowOverlayPermissionRequestDialogUseCase() {
- return new IsShowOverlayPermissionRequestDialogUseCaseImpl(permissionManager, permissionDialogManager, gliaSdkConfigurationManager);
+ return new IsShowOverlayPermissionRequestDialogUseCaseImpl(permissionManager, permissionDialogManager, configurationManager);
}
@NonNull
@@ -457,7 +450,8 @@ public GetImageFileFromCacheUseCase createGetImageFileFromCacheUseCase() {
public GetImageFileFromNetworkUseCase createGetImageFileFromNetworkUseCase() {
return new GetImageFileFromNetworkUseCase(
repositoryFactory.getGliaFileRepository(),
- createDecodeSampledBitmapFromInputStreamUseCase()
+ createDecodeSampledBitmapFromInputStreamUseCase(),
+ createManageSecureMessagingStatusUseCase()
);
}
@@ -468,7 +462,7 @@ public PutImageFileToDownloadsUseCase createPutImageFileToDownloadsUseCase() {
@NonNull
public DownloadFileUseCase createDownloadFileUseCase() {
- return new DownloadFileUseCase(repositoryFactory.getGliaFileRepository());
+ return new DownloadFileUseCase(repositoryFactory.getGliaFileRepository(), createManageSecureMessagingStatusUseCase());
}
@NonNull
@@ -480,7 +474,7 @@ public OnNextMessageUseCase createOnNextMessageUseCase() {
public SendMessageButtonStateUseCase createEnableSendMessageButtonUseCase() {
return new SendMessageButtonStateUseCase(
repositoryFactory.getSendMessageRepository(),
- repositoryFactory.getSecureFileAttachmentRepository(),
+ repositoryFactory.getGliaFileAttachmentRepository(),
repositoryFactory.getSecureConversationsRepository(),
createShowMessageLimitErrorUseCase(),
schedulers
@@ -498,7 +492,7 @@ public ShowMessageLimitErrorUseCase createShowMessageLimitErrorUseCase() {
@NonNull
public ResetMessageCenterUseCase createResetMessageCenterUseCase() {
return new ResetMessageCenterUseCase(
- repositoryFactory.getSecureFileAttachmentRepository(),
+ repositoryFactory.getGliaFileAttachmentRepository(),
repositoryFactory.getSendMessageRepository()
);
}
@@ -551,54 +545,22 @@ public IsCallVisualizerScreenSharingUseCase createIsCallVisualizerScreenSharingU
@NonNull
public SendSecureMessageUseCase createSendSecureMessageUseCase() {
return new SendSecureMessageUseCase(
- repositoryFactory.getEngagementConfigRepository(),
repositoryFactory.getSendMessageRepository(),
repositoryFactory.getSecureConversationsRepository(),
- repositoryFactory.getSecureFileAttachmentRepository(),
+ repositoryFactory.getGliaFileAttachmentRepository(),
repositoryFactory.getGliaMessageRepository(),
getIsQueueingOrEngagementUseCase()
);
}
- @NonNull
- public GetAvailableQueueIdsForSecureMessagingUseCase createSecureMessagingAvailableQueueIdsUseCase() {
- return new GetAvailableQueueIdsForSecureMessagingUseCase(
- repositoryFactory.getEngagementConfigRepository(),
- repositoryFactory.getGliaQueueRepository(),
- createIsMessagingAvailableUseCase(),
- schedulers
- );
- }
-
@NonNull
public AddSecureFileToAttachmentAndUploadUseCase createAddSecureFileToAttachmentAndUploadUseCase() {
- return new AddSecureFileToAttachmentAndUploadUseCase(repositoryFactory.getSecureFileAttachmentRepository());
- }
-
- @NonNull
- public AddSecureFileAttachmentsObserverUseCase createAddSecureFileAttachmentsObserverUseCase() {
- return new AddSecureFileAttachmentsObserverUseCase(
- repositoryFactory.getSecureFileAttachmentRepository(),
- schedulers
- );
+ return new AddSecureFileToAttachmentAndUploadUseCase(repositoryFactory.getGliaFileAttachmentRepository());
}
@NonNull
- public GetSecureFileAttachmentsUseCase createGetSecureFileAttachmentsUseCase() {
- return new GetSecureFileAttachmentsUseCase(repositoryFactory.getSecureFileAttachmentRepository());
- }
-
- @NonNull
- public RemoveSecureFileAttachmentUseCase createRemoveSecureFileAttachmentUseCase() {
- return new RemoveSecureFileAttachmentUseCase(repositoryFactory.getSecureFileAttachmentRepository());
- }
-
- @NonNull
- public IsSecureEngagementUseCase createIsSecureEngagementUseCase() {
- return new IsSecureEngagementUseCase(
- repositoryFactory.getEngagementConfigRepository(),
- getIsQueueingOrEngagementUseCase()
- );
+ public ManageSecureMessagingStatusUseCase createManageSecureMessagingStatusUseCase() {
+ return new ManageSecureMessagingStatusUseCase(repositoryFactory.getEngagementRepository());
}
@NonNull
@@ -606,19 +568,14 @@ public IsAuthenticatedUseCase createIsAuthenticatedUseCase() {
return new IsAuthenticatedUseCase(authenticationManagerProvider.authenticationManager);
}
- @NonNull
- public SetEngagementConfigUseCase createSetEngagementConfigUseCase() {
- return new SetEngagementConfigUseCase(repositoryFactory.getEngagementConfigRepository());
- }
-
@NonNull
public IsMessagingAvailableUseCase createIsMessagingAvailableUseCase() {
- return new IsMessagingAvailableUseCase();
+ return new IsMessagingAvailableUseCase(repositoryFactory.getQueueRepository(), repositoryFactory.getEngagementRepository());
}
@NonNull
- public GetUnreadMessagesCountWithTimeoutUseCase createSubscribeToUnreadMessagesCountUseCase() {
- return new GetUnreadMessagesCountWithTimeoutUseCase(repositoryFactory.getSecureConversationsRepository());
+ public SecureConversationTopBannerVisibilityUseCase createSecureConversationTopBannerVisibilityUseCase() {
+ return new SecureConversationTopBannerVisibilityUseCase(repositoryFactory.getQueueRepository(), getHasPendingSecureConversationsWithTimeoutUseCase());
}
@NonNull
@@ -626,6 +583,11 @@ public MarkMessagesReadWithDelayUseCase createMarkMessagesReadUseCase() {
return new MarkMessagesReadWithDelayUseCase(repositoryFactory.getSecureConversationsRepository());
}
+ @NonNull
+ public SetLeaveSecureConversationDialogVisibleUseCase createSetLeaveSecureConversationDialogVisibleUseCase() {
+ return new SetLeaveSecureConversationDialogVisibleUseCaseImpl(repositoryFactory.getSecureConversationsRepository());
+ }
+
@NonNull
public FindNewMessagesDividerIndexUseCase createFindNewMessagesDividerIndexUseCase() {
return new FindNewMessagesDividerIndexUseCase();
@@ -836,8 +798,7 @@ public SendUnsentMessagesUseCase createSendUnsentMessagesUseCase() {
return new SendUnsentMessagesUseCase(
repositoryFactory.getGliaMessageRepository(),
repositoryFactory.getSecureConversationsRepository(),
- repositoryFactory.getEngagementConfigRepository(),
- createIsSecureEngagementUseCase()
+ createManageSecureMessagingStatusUseCase()
);
}
@@ -871,8 +832,8 @@ public EndEngagementUseCase getEndEngagementUseCase() {
}
@NonNull
- public IsQueueingOrEngagementUseCase getIsQueueingOrEngagementUseCase() {
- return new IsQueueingOrEngagementUseCaseImpl(repositoryFactory.getEngagementRepository());
+ public IsQueueingOrLiveEngagementUseCase getIsQueueingOrEngagementUseCase() {
+ return new IsQueueingOrLiveEngagementUseCaseImpl(repositoryFactory.getEngagementRepository());
}
@NonNull
@@ -896,7 +857,6 @@ public ReleaseResourcesUseCase getReleaseResourcesUseCase(DialogContract.Control
getReleaseScreenSharingResourcesUseCase(),
createCallNotificationUseCase(),
repositoryFactory.getGliaFileAttachmentRepository(),
- repositoryFactory.getEngagementConfigRepository(),
createUpdateFromCallScreenUseCase(),
dialogController
);
@@ -975,7 +935,7 @@ public EnqueueForEngagementUseCase getQueueForEngagementUseCase() {
@NonNull
public ScreenSharingUseCase getScreenSharingUseCase() {
- return new ScreenSharingUseCaseImpl(repositoryFactory.getEngagementRepository(), getReleaseScreenSharingResourcesUseCase());
+ return new ScreenSharingUseCaseImpl(repositoryFactory.getEngagementRepository(), getReleaseScreenSharingResourcesUseCase(), configurationManager);
}
@NonNull
@@ -1100,4 +1060,11 @@ public FlipCameraButtonStateUseCase getFlipCameraButtonStateUseCase() {
);
}
+ @NonNull
+ public HasOngoingSecureConversationUseCase getHasPendingSecureConversationsWithTimeoutUseCase() {
+ return new HasOngoingSecureConversationUseCase(
+ repositoryFactory.getSecureConversationsRepository(),
+ getEngagementStateUseCase()
+ );
+ }
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/engagement/EngagementRepository.kt b/widgetssdk/src/main/java/com/glia/widgets/engagement/EngagementRepository.kt
index cc59fbbe9..1fdeb835f 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/engagement/EngagementRepository.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/engagement/EngagementRepository.kt
@@ -31,13 +31,15 @@ internal interface EngagementRepository {
val visitorCameraState: Flowable
val currentOperatorValue: Operator?
- val isQueueingOrEngagement: Boolean
- val hasOngoingEngagement: Boolean
+ val isQueueingOrLiveEngagement: Boolean
+ val hasOngoingLiveEngagement: Boolean
+ val isTransferredSecureConversation: Boolean
val isQueueing: Boolean
val isQueueingForMedia: Boolean
val isCallVisualizerEngagement: Boolean
val isOperatorPresent: Boolean
val isSharingScreen: Boolean
+ val isSecureMessagingRequested: Boolean
val cameras: List?
val currentVisitorCamera: VisitorCamera
@@ -45,7 +47,7 @@ internal interface EngagementRepository {
fun reset()
fun resetQueueing()
fun endEngagement(silently: Boolean)
- fun queueForEngagement(queueIds: List, mediaType: MediaType, visitorContextAssetId: String?)
+ fun queueForEngagement(mediaType: MediaType)
fun cancelQueuing()
fun acceptCurrentEngagementRequest(visitorContextAssetId: String)
fun declineCurrentEngagementRequest()
@@ -62,4 +64,5 @@ internal interface EngagementRepository {
fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?)
fun onActivityResultSkipScreenSharingPermissionRequest(resultCode: Int, intent: Intent?)
fun onReadyToShareScreen()
+ fun updateIsSecureMessagingRequested(isSecureMessagingRequested: Boolean)
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/engagement/EngagementRepositoryImpl.kt b/widgetssdk/src/main/java/com/glia/widgets/engagement/EngagementRepositoryImpl.kt
index 43dac4332..ed4bc453d 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/engagement/EngagementRepositoryImpl.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/engagement/EngagementRepositoryImpl.kt
@@ -30,14 +30,17 @@ import com.glia.androidsdk.screensharing.ScreenSharing
import com.glia.androidsdk.screensharing.ScreenSharingRequest
import com.glia.androidsdk.screensharing.VisitorScreenSharingState
import com.glia.widgets.core.engagement.GliaOperatorRepository
+import com.glia.widgets.core.queue.QueueRepository
import com.glia.widgets.di.GliaCore
import com.glia.widgets.helper.Data
import com.glia.widgets.helper.Logger
import com.glia.widgets.helper.isAudioOrVideo
import com.glia.widgets.helper.isQueueUnavailable
import com.glia.widgets.helper.unSafeSubscribe
+import com.glia.widgets.launcher.ConfigurationManager
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.core.Flowable
+import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.processors.BehaviorProcessor
import io.reactivex.rxjava3.processors.PublishProcessor
import java.util.function.Consumer
@@ -50,7 +53,12 @@ internal const val MEDIA_PERMISSION_REQUEST_CODE = 0x3E9
@VisibleForTesting
internal const val SKIP_ASKING_SCREEN_SHARING_PERMISSION_RESULT_CODE = 0x1995
-internal class EngagementRepositoryImpl(private val core: GliaCore, private val operatorRepository: GliaOperatorRepository) : EngagementRepository {
+internal class EngagementRepositoryImpl(
+ private val core: GliaCore,
+ private val operatorRepository: GliaOperatorRepository,
+ private val queueRepository: QueueRepository,
+ private val configurationManager: ConfigurationManager
+) : EngagementRepository {
private val engagementRequestCallback = Consumer(::handleIncomingEngagementRequest)
private val engagementOutcomeCallback = Consumer(::handleEngagementOutcome)
@@ -70,13 +78,13 @@ internal class EngagementRepositoryImpl(private val core: GliaCore, private val
private val queueTicketCallback = Consumer(::handleQueueTicket)
private val _engagementState: BehaviorProcessor = BehaviorProcessor.createDefault(State.NoEngagement)
- override val engagementState: Flowable = _engagementState.onBackpressureBuffer()
+ override val engagementState: Flowable = _engagementState.onBackpressureBuffer().distinctUntilChanged()
private val _survey: PublishProcessor = PublishProcessor.create()
override val survey: Flowable = _survey
private val _currentOperator: BehaviorProcessor> = BehaviorProcessor.createDefault(Data.Empty)
- override val currentOperator: Flowable> = _currentOperator.onBackpressureLatest()
+ override val currentOperator: Flowable> = _currentOperator.onBackpressureLatest().distinctUntilChanged()
override val currentOperatorValue: Operator? get() = with(_currentOperator.value as? Data.Value) { this?.result }
override val isOperatorPresent: Boolean get() = currentOperatorValue != null
private val currentState: State? get() = _engagementState.value
@@ -123,30 +131,39 @@ internal class EngagementRepositoryImpl(private val core: GliaCore, private val
private val _screenSharingState: BehaviorProcessor = BehaviorProcessor.create()
override val screenSharingState: Flowable = _screenSharingState.onBackpressureLatest()
-
private val readyToShareScreenProcessor: PublishProcessor = PublishProcessor.create()
private val mediaProjectionActivityResultProcessor: PublishProcessor> = PublishProcessor.create()
private var currentScreenSharingRequest: ScreenSharingRequest? = null
private var currentScreenSharingScreen: LocalScreen? = null
- override val hasOngoingEngagement: Boolean
- get() = currentEngagement != null
+ private val queueIngDisposable = CompositeDisposable()
+
+ override val hasOngoingLiveEngagement: Boolean
+ get() = currentEngagement != null && currentState?.isLiveEngagement == true
+
+ override val isTransferredSecureConversation: Boolean
+ get() = currentState is State.TransferredToSecureConversation
override val isQueueing: Boolean
- get() = currentState is State.Queuing || currentState is State.PreQueuing
+ get() = currentState?.isQueueing == true
+
override val isQueueingForMedia: Boolean
get() = currentState?.queueingMediaType?.isAudioOrVideo() == true
override val isCallVisualizerEngagement: Boolean
get() = currentEngagement is OmnibrowseEngagement
- override val isQueueingOrEngagement: Boolean
- get() = isQueueing || hasOngoingEngagement
+ override val isQueueingOrLiveEngagement: Boolean
+ get() = isQueueing || hasOngoingLiveEngagement
override val isSharingScreen: Boolean
get() = currentEngagement != null && _screenSharingState.run { value == ScreenSharingState.Started || value == ScreenSharingState.RequestAccepted }
+ private var _isSecureMessagingRequested: Boolean = false
+ override val isSecureMessagingRequested: Boolean
+ get() = _isSecureMessagingRequested
+
override val cameras: List?
get() = currentEngagement?.media?.cameraDevices
@@ -165,6 +182,7 @@ internal class EngagementRepositoryImpl(private val core: GliaCore, private val
}
override fun reset() {
+ _isSecureMessagingRequested = false
currentEngagement?.let { endEngagement(true) } ?: cancelQueuing()
}
@@ -173,6 +191,7 @@ internal class EngagementRepositoryImpl(private val core: GliaCore, private val
}
private fun resetState() {
+ _isSecureMessagingRequested = false
_operatorMediaState.onNext(Data.Empty)
_visitorMediaState.onNext(Data.Empty)
_currentOperator.onNext(Data.Empty)
@@ -196,17 +215,34 @@ internal class EngagementRepositoryImpl(private val core: GliaCore, private val
}
}
- override fun queueForEngagement(queueIds: List, mediaType: MediaType, visitorContextAssetId: String?) {
- if (isQueueingOrEngagement) return
+ override fun queueForEngagement(mediaType: MediaType) {
+ if (isQueueingOrLiveEngagement) return
+
+ _engagementState.onNext(State.PreQueuing(mediaType))
Logger.i(TAG, "Start queueing for media engagement")
- core.queueForEngagement(queueIds.toTypedArray(), mediaType, visitorContextAssetId, null, MEDIA_PERMISSION_REQUEST_CODE) {
- handleQueueingResponse(it, queueIds, mediaType)
- }
+
+ queueIngDisposable.add(
+ queueRepository.relevantQueueIds.subscribe { ids ->
+ if (ids.isNotEmpty()) {
+ core.queueForEngagement(
+ ids,
+ mediaType,
+ configurationManager.visitorContextAssetId,
+ null,
+ MEDIA_PERMISSION_REQUEST_CODE) {
+ handleQueueingResponse(it)
+ }
+ } else {
+ handleQueueingResponse(GliaException("relevant queues are empty", GliaException.Cause.INVALID_INPUT))
+ }
+ }
+ )
}
override fun cancelQueuing() {
(_engagementState.value as? State.PreQueuing)?.apply {
+ queueIngDisposable.clear()
_engagementState.onNext(State.QueueingCanceled)
return
}
@@ -320,7 +356,7 @@ internal class EngagementRepositoryImpl(private val core: GliaCore, private val
}
if (currentState is State.PreQueuing) {
- _engagementState.onNext(State.Queuing(currentState.queueIds, ticket.id, currentState.mediaType))
+ _engagementState.onNext(State.Queuing(ticket.id, currentState.mediaType))
trackQueueTicketUpdates(ticket)
}
}
@@ -334,10 +370,9 @@ internal class EngagementRepositoryImpl(private val core: GliaCore, private val
}
}
- private fun handleQueueingResponse(exception: GliaException?, queueIds: List, mediaType: MediaType) {
+ private fun handleQueueingResponse(exception: GliaException?) {
when {
- exception == null || exception.cause == GliaException.Cause.ALREADY_QUEUED ->
- _engagementState.onNext(State.PreQueuing(queueIds, mediaType))
+ exception == null || exception.cause == GliaException.Cause.ALREADY_QUEUED -> return
exception.isQueueUnavailable -> {
_engagementState.onNext(State.QueueUnstaffed)
@@ -354,14 +389,22 @@ internal class EngagementRepositoryImpl(private val core: GliaCore, private val
private fun handleOmniCoreEngagement(engagement: OmnicoreEngagement) {
Logger.i(TAG, "Omnicore Engagement started")
- currentEngagement?.also {
- unsubscribeFromEvents(it)
- currentEngagement = null
- resetState()
- } ?: _engagementState.onNext(State.StartedOmniCore)
+ when {
+ currentEngagement != null -> {
+ unsubscribeFromEvents(currentEngagement!!)
+ currentEngagement = null
+ resetState()
+ }
+
+ engagement.state.visitorStatus == EngagementState.VisitorStatus.TRANSFERRING && engagement.state.capabilities.isText -> {
+ _engagementState.onNext(State.TransferredToSecureConversation)
+ }
+
+ else -> _engagementState.onNext(State.StartedOmniCore)
+ }
currentEngagement = engagement
- operatorRepository.emit(engagement.state.operator)
+ handleEngagementState(engagement.state)
subscribeToEngagementEvents(engagement)
subscribeToEngagementMediaEvents(engagement.media)
@@ -380,7 +423,7 @@ internal class EngagementRepositoryImpl(private val core: GliaCore, private val
} ?: _engagementState.onNext(State.StartedCallVisualizer)
currentEngagement = engagement
- operatorRepository.emit(engagement.state.operator)
+ handleEngagementState(engagement.state)
subscribeToEngagementEvents(engagement)
subscribeToEngagementMediaEvents(engagement.media)
@@ -443,29 +486,36 @@ internal class EngagementRepositoryImpl(private val core: GliaCore, private val
}
private fun handleEngagementState(state: EngagementState) {
+ // keeping the current operator value, to use inside this function,
+ // because in some cases we need to globally have up to date operator before emitting new state
+ val currentOperator: Operator? = currentOperatorValue
+
operatorRepository.emit(state.operator)
+ _currentOperator.onNext(Data.Value(state.operator))
+
+ when {
+ state.visitorStatus == EngagementState.VisitorStatus.TRANSFERRING && state.capabilities.isText -> {
+ Logger.i(TAG, "Transfer to Secure Conversation")
+ _engagementState.onNext(State.TransferredToSecureConversation)
+ }
- val updateState = when {
state.visitorStatus == EngagementState.VisitorStatus.TRANSFERRING -> {
Logger.i(TAG, "Transfer engagement")
- EngagementUpdateState.Transferring
+ _engagementState.onNext(State.Update(state, EngagementUpdateState.Transferring))
}
- !isOperatorPresent -> {
+ currentOperator == null -> {
Logger.i(TAG, "Operator connected")
- EngagementUpdateState.OperatorConnected(state.operator)
+ _engagementState.onNext(State.Update(state, EngagementUpdateState.OperatorConnected(state.operator)))
}
- currentOperatorValue?.id != state.operator.id -> {
+ currentOperator.id != state.operator.id -> {
Logger.i(TAG, "Operator changed")
- EngagementUpdateState.OperatorChanged(state.operator)
+ _engagementState.onNext(State.Update(state, EngagementUpdateState.OperatorChanged(state.operator)))
}
- else -> EngagementUpdateState.Ongoing(state.operator)
+ currentOperator != state.operator -> _engagementState.onNext(State.Update(state, EngagementUpdateState.Ongoing(state.operator)))
}
- _currentOperator.onNext(Data.Value(state.operator))
-
- _engagementState.onNext(State.Update(state, updateState))
}
private fun handleEngagementEnd() {
@@ -638,4 +688,8 @@ internal class EngagementRepositoryImpl(private val core: GliaCore, private val
override fun onReadyToShareScreen() {
readyToShareScreenProcessor.onNext(Unit)
}
+
+ override fun updateIsSecureMessagingRequested(isSecureMessagingRequested: Boolean) {
+ _isSecureMessagingRequested = isSecureMessagingRequested
+ }
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/engagement/States.kt b/widgetssdk/src/main/java/com/glia/widgets/engagement/States.kt
index 4703bb9e6..e3d27ee4a 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/engagement/States.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/engagement/States.kt
@@ -6,18 +6,28 @@ import com.glia.androidsdk.engagement.EngagementState
import com.glia.androidsdk.engagement.Survey
internal sealed interface State {
- object NoEngagement : State
- data class PreQueuing(val queueIds: List, val mediaType: MediaType) : State
- data class Queuing(val queueIds: List, val queueTicketId: String, val mediaType: MediaType) : State
- object QueueUnstaffed : State
- object UnexpectedErrorHappened : State
- object QueueingCanceled : State
- object StartedOmniCore : State
- object StartedCallVisualizer : State
- object FinishedOmniCore : State
- object FinishedCallVisualizer : State
+ data object NoEngagement : State
+ data class PreQueuing(val mediaType: MediaType) : State
+ data class Queuing(val queueTicketId: String, val mediaType: MediaType) : State
+ data object QueueUnstaffed : State
+ data object UnexpectedErrorHappened : State
+ data object QueueingCanceled : State
+ data object StartedOmniCore : State
+ data object StartedCallVisualizer : State
+ data object FinishedOmniCore : State
+ data object FinishedCallVisualizer : State
+ data object TransferredToSecureConversation : State
data class Update(val state: EngagementState, val updateState: EngagementUpdateState) : State
+ val isQueueing: Boolean
+ get() = this is Queuing || this is PreQueuing
+
+ val isLiveEngagement: Boolean
+ get() = when (this) {
+ is Update, StartedOmniCore, StartedCallVisualizer -> true
+ else -> false
+ }
+
val queueingMediaType: MediaType?
get() = when (this) {
is PreQueuing -> mediaType
@@ -27,23 +37,23 @@ internal sealed interface State {
}
internal sealed interface EngagementUpdateState {
- object Transferring : EngagementUpdateState
+ data object Transferring : EngagementUpdateState
data class Ongoing(val operator: Operator) : EngagementUpdateState
data class OperatorConnected(val operator: Operator) : EngagementUpdateState
data class OperatorChanged(val operator: Operator) : EngagementUpdateState
}
internal sealed interface SurveyState {
- object Empty : SurveyState
- object EmptyFromOperatorRequest : SurveyState
+ data object Empty : SurveyState
+ data object EmptyFromOperatorRequest : SurveyState
data class Value(val survey: Survey) : SurveyState
}
internal sealed interface ScreenSharingState {
- object Requested : ScreenSharingState
- object Started : ScreenSharingState
- object RequestAccepted : ScreenSharingState
- object RequestDeclined : ScreenSharingState
+ data object Requested : ScreenSharingState
+ data object Started : ScreenSharingState
+ data object RequestAccepted : ScreenSharingState
+ data object RequestDeclined : ScreenSharingState
data class FailedToAcceptRequest(val message: String) : ScreenSharingState
- object Ended : ScreenSharingState
+ data object Ended : ScreenSharingState
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/engagement/completion/EngagementCompletionActivityWatcher.kt b/widgetssdk/src/main/java/com/glia/widgets/engagement/completion/EngagementCompletionActivityWatcher.kt
index 2fa0a0666..6edb4591e 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/engagement/completion/EngagementCompletionActivityWatcher.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/engagement/completion/EngagementCompletionActivityWatcher.kt
@@ -1,24 +1,22 @@
package com.glia.widgets.engagement.completion
import android.app.Activity
-import android.content.Intent
-import android.os.Parcelable
import com.glia.androidsdk.engagement.Survey
-import com.glia.widgets.GliaWidgets
-import com.glia.widgets.R
import com.glia.widgets.base.BaseSingleActivityWatcher
import com.glia.widgets.helper.GliaActivityManager
import com.glia.widgets.helper.Logger
import com.glia.widgets.helper.OneTimeEvent
import com.glia.widgets.helper.TAG
-import com.glia.widgets.helper.withRuntimeTheme
-import com.glia.widgets.survey.SurveyActivity
+import com.glia.widgets.launcher.ActivityLauncher
import com.glia.widgets.view.Dialogs
import io.reactivex.rxjava3.core.Flowable
import java.lang.ref.WeakReference
-internal class EngagementCompletionActivityWatcher(controller: EngagementCompletionContract.Controller, gliaActivityManager: GliaActivityManager) :
- BaseSingleActivityWatcher(gliaActivityManager) {
+internal class EngagementCompletionActivityWatcher(
+ controller: EngagementCompletionContract.Controller,
+ gliaActivityManager: GliaActivityManager,
+ private val activityLauncher: ActivityLauncher
+) : BaseSingleActivityWatcher(gliaActivityManager) {
init {
Flowable.combineLatest(resumedActivity, controller.state, ::handleState).subscribe()
@@ -44,16 +42,7 @@ internal class EngagementCompletionActivityWatcher(controller: EngagementComplet
}
}
- private fun showSurvey(activity: Activity, survey: Survey) {
- activity.withRuntimeTheme { _, uiTheme ->
- val newIntent: Intent = Intent(activity, SurveyActivity::class.java)
- .putExtra(GliaWidgets.UI_THEME, uiTheme)
- .putExtra(GliaWidgets.SURVEY, survey as Parcelable)
-
- activity.overridePendingTransition(R.anim.slide_up, 0)
- activity.startActivity(newIntent)
- }
- }
+ private fun showSurvey(activity: Activity, survey: Survey) = activityLauncher.launchSurvey(activity, survey)
private fun showOperatorEndedEngagementDialog(activity: Activity, consumeCallback: () -> Unit) {
showAlertDialogWithStyledContext(activity) { context, theme ->
diff --git a/widgetssdk/src/main/java/com/glia/widgets/engagement/completion/EngagementCompletionController.kt b/widgetssdk/src/main/java/com/glia/widgets/engagement/completion/EngagementCompletionController.kt
index 30441bccb..452cbf701 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/engagement/completion/EngagementCompletionController.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/engagement/completion/EngagementCompletionController.kt
@@ -1,6 +1,9 @@
package com.glia.widgets.engagement.completion
import com.glia.widgets.engagement.State
+import com.glia.widgets.engagement.State.StartedCallVisualizer
+import com.glia.widgets.engagement.State.StartedOmniCore
+import com.glia.widgets.engagement.State.Update
import com.glia.widgets.engagement.SurveyState
import com.glia.widgets.engagement.domain.EngagementStateUseCase
import com.glia.widgets.engagement.domain.ReleaseResourcesUseCase
diff --git a/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/EngagementTypeUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/EngagementTypeUseCase.kt
index adbac140f..161267415 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/EngagementTypeUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/EngagementTypeUseCase.kt
@@ -7,13 +7,13 @@ internal interface EngagementTypeUseCase {
}
internal class EngagementTypeUseCaseImpl(
- private val isQueueingOrEngagementUseCase: IsQueueingOrEngagementUseCase,
+ private val isQueueingOrLiveEngagementUseCase: IsQueueingOrLiveEngagementUseCase,
private val isCurrentEngagementCallVisualizerUseCase: IsCurrentEngagementCallVisualizerUseCase,
private val operatorMediaUseCase: OperatorMediaUseCase,
private val visitorMediaUseCase: VisitorMediaUseCase,
private val isOperatorPresentUseCase: IsOperatorPresentUseCase
) : EngagementTypeUseCase {
- private val hasOngoingEngagement get() = isQueueingOrEngagementUseCase.hasOngoingEngagement
+ private val hasOngoingEngagement get() = isQueueingOrLiveEngagementUseCase.hasOngoingLiveEngagement
private val hasAnyMedia: Boolean get() = visitorMediaUseCase.hasMedia || operatorMediaUseCase.hasMedia
override val isMediaEngagement: Boolean get() = hasOngoingEngagement && isOperatorPresentUseCase() && hasAnyMedia
override val isChatEngagement: Boolean get() = hasOngoingEngagement && !isCurrentEngagementCallVisualizerUseCase() && isOperatorPresentUseCase() && !hasAnyMedia
diff --git a/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/EnqueueForEngagementUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/EnqueueForEngagementUseCase.kt
index 9779fa67d..74e69d1bd 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/EnqueueForEngagementUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/EnqueueForEngagementUseCase.kt
@@ -4,12 +4,9 @@ import com.glia.androidsdk.Engagement
import com.glia.widgets.engagement.EngagementRepository
internal interface EnqueueForEngagementUseCase {
- operator fun invoke(queueIds: List?, mediaType: Engagement.MediaType? = null, visitorContextAssetId: String? = null)
+ operator fun invoke(mediaType: Engagement.MediaType = Engagement.MediaType.TEXT)
}
internal class EnqueueForEngagementUseCaseImpl(private val engagementRepository: EngagementRepository) : EnqueueForEngagementUseCase {
- override fun invoke(queueIds: List?, mediaType: Engagement.MediaType?, visitorContextAssetId: String?) {
- val type = mediaType ?: Engagement.MediaType.TEXT
- engagementRepository.queueForEngagement(queueIds ?: emptyList(), type, visitorContextAssetId)
- }
+ override fun invoke(mediaType: Engagement.MediaType) = engagementRepository.queueForEngagement(mediaType)
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/IsQueueingOrEngagementUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/IsQueueingOrEngagementUseCase.kt
deleted file mode 100644
index 0954604f2..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/IsQueueingOrEngagementUseCase.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.glia.widgets.engagement.domain
-
-import com.glia.widgets.engagement.EngagementRepository
-
-internal interface IsQueueingOrEngagementUseCase {
- val hasOngoingEngagement: Boolean
- val isQueueingForMedia: Boolean
- val isQueueingForChat: Boolean
- operator fun invoke(): Boolean
-}
-
-internal class IsQueueingOrEngagementUseCaseImpl(private val engagementRepository: EngagementRepository) : IsQueueingOrEngagementUseCase {
- override val hasOngoingEngagement: Boolean get() = engagementRepository.hasOngoingEngagement
- override val isQueueingForMedia: Boolean get() = engagementRepository.isQueueingForMedia
- override val isQueueingForChat: Boolean get() = engagementRepository.isQueueing && !isQueueingForMedia
- override fun invoke(): Boolean = engagementRepository.isQueueingOrEngagement
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/IsQueueingOrLiveEngagementUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/IsQueueingOrLiveEngagementUseCase.kt
new file mode 100644
index 000000000..61f017685
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/IsQueueingOrLiveEngagementUseCase.kt
@@ -0,0 +1,17 @@
+package com.glia.widgets.engagement.domain
+
+import com.glia.widgets.engagement.EngagementRepository
+
+internal interface IsQueueingOrLiveEngagementUseCase {
+ val hasOngoingLiveEngagement: Boolean
+ val isQueueingForMedia: Boolean
+ val isQueueingForLiveChat: Boolean
+ operator fun invoke(): Boolean
+}
+
+internal class IsQueueingOrLiveEngagementUseCaseImpl(private val engagementRepository: EngagementRepository) : IsQueueingOrLiveEngagementUseCase {
+ override val hasOngoingLiveEngagement: Boolean get() = engagementRepository.hasOngoingLiveEngagement
+ override val isQueueingForMedia: Boolean get() = engagementRepository.isQueueingForMedia
+ override val isQueueingForLiveChat: Boolean get() = engagementRepository.isQueueing && !isQueueingForMedia
+ override fun invoke(): Boolean = engagementRepository.isQueueingOrLiveEngagement
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/ReleaseResourcesUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/ReleaseResourcesUseCase.kt
index 6e6a7e59a..f6857f343 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/ReleaseResourcesUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/ReleaseResourcesUseCase.kt
@@ -2,7 +2,6 @@ package com.glia.widgets.engagement.domain
import com.glia.widgets.chat.domain.UpdateFromCallScreenUseCase
import com.glia.widgets.core.dialog.DialogContract
-import com.glia.widgets.core.engagement.GliaEngagementConfigRepository
import com.glia.widgets.core.fileupload.FileAttachmentRepository
import com.glia.widgets.core.notification.domain.CallNotificationUseCase
import com.glia.widgets.di.Dependencies
@@ -15,17 +14,14 @@ internal class ReleaseResourcesUseCaseImpl(
private val releaseScreenSharingResourcesUseCase: ReleaseScreenSharingResourcesUseCase,
private val callNotificationUseCase: CallNotificationUseCase,
private val fileAttachmentRepository: FileAttachmentRepository,
- private val gliaEngagementConfigRepository: GliaEngagementConfigRepository,
private val updateFromCallScreenUseCase: UpdateFromCallScreenUseCase,
private val dialogController: DialogContract.Controller
) : ReleaseResourcesUseCase {
override fun invoke() {
dialogController.dismissDialogs()
- fileAttachmentRepository.clearObservers()
fileAttachmentRepository.detachAllFiles()
releaseScreenSharingResourcesUseCase()
callNotificationUseCase.removeAllNotifications()
- gliaEngagementConfigRepository.reset()
updateFromCallScreenUseCase(false)
Dependencies.destroyControllers()
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/ScreenSharingUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/ScreenSharingUseCase.kt
index b07ae147d..bc32a6270 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/ScreenSharingUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/engagement/domain/ScreenSharingUseCase.kt
@@ -2,9 +2,9 @@ package com.glia.widgets.engagement.domain
import android.app.Activity
import android.content.Intent
-import com.glia.androidsdk.screensharing.ScreenSharing
import com.glia.widgets.engagement.EngagementRepository
import com.glia.widgets.engagement.ScreenSharingState
+import com.glia.widgets.launcher.ConfigurationManager
import io.reactivex.rxjava3.core.Flowable
internal interface ScreenSharingUseCase {
@@ -12,13 +12,14 @@ internal interface ScreenSharingUseCase {
operator fun invoke(): Flowable
fun end()
fun declineRequest()
- fun acceptRequestWithAskedPermission(activity: Activity, mode: ScreenSharing.Mode)
+ fun acceptRequestWithAskedPermission(activity: Activity)
fun onActivityResultSkipPermissionRequest(resultCode: Int, intent: Intent?)
}
internal class ScreenSharingUseCaseImpl(
private val engagementRepository: EngagementRepository,
- private val releaseScreenSharingResourcesUseCase: ReleaseScreenSharingResourcesUseCase
+ private val releaseScreenSharingResourcesUseCase: ReleaseScreenSharingResourcesUseCase,
+ private val configurationManager: ConfigurationManager
) : ScreenSharingUseCase {
override val isSharing: Boolean get() = engagementRepository.isSharingScreen
override fun invoke(): Flowable = engagementRepository.screenSharingState
@@ -28,8 +29,8 @@ internal class ScreenSharingUseCaseImpl(
}
override fun declineRequest() = engagementRepository.declineScreenSharingRequest()
- override fun acceptRequestWithAskedPermission(activity: Activity, mode: ScreenSharing.Mode) =
- engagementRepository.acceptScreenSharingWithAskedPermission(activity, mode)
+ override fun acceptRequestWithAskedPermission(activity: Activity) =
+ engagementRepository.acceptScreenSharingWithAskedPermission(activity, configurationManager.screenSharingMode)
override fun onActivityResultSkipPermissionRequest(resultCode: Int, intent: Intent?) =
engagementRepository.onActivityResultSkipScreenSharingPermissionRequest(resultCode, intent)
diff --git a/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidget.kt b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidget.kt
new file mode 100644
index 000000000..f85a3afa9
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidget.kt
@@ -0,0 +1,74 @@
+package com.glia.widgets.entrywidget
+
+import android.app.Activity
+import android.content.Context
+import android.view.View
+import com.glia.widgets.chat.Intention
+import com.glia.widgets.core.secureconversations.domain.HasOngoingSecureConversationUseCase
+import com.glia.widgets.entrywidget.adapter.EntryWidgetAdapter
+import com.glia.widgets.helper.wrapWithTheme
+import com.glia.widgets.launcher.ActivityLauncher
+import com.glia.widgets.view.unifiedui.theme.UnifiedThemeManager
+
+/**
+ * An interface for interacting with the entry widget(pre-built UI for engagement type selection).
+ */
+interface EntryWidget {
+ /**
+ * Shows the entry widget.
+ *
+ * @param activity The Activity used to show the entry widget
+ */
+ fun show(activity: Activity)
+
+ /**
+ * Creates the entry widget view that might be easily embedded in a custom UI.
+ *
+ * @return New instance of an entry widget view.
+ */
+ fun getView(context: Context): View
+
+ /**
+ * Hides the entry widget.
+ */
+ fun hide()
+}
+
+internal class EntryWidgetImpl(
+ private val activityLauncher: ActivityLauncher,
+ private val themeManager: UnifiedThemeManager,
+ private val entryWidgetHideController: EntryWidgetHideController,
+ private val hasOngoingSecureConversationUseCase: HasOngoingSecureConversationUseCase
+) : EntryWidget {
+
+ override fun show(activity: Activity) {
+ hasOngoingSecureConversationUseCase {
+ handleShowWithPendingSecureConversations(it, activity)
+ }
+ }
+
+ private fun handleShowWithPendingSecureConversations(hasPendingSecureConversations: Boolean, activity: Activity) {
+ if (hasPendingSecureConversations) {
+ activityLauncher.launchChat(activity, Intention.SC_CHAT)
+ } else {
+ activityLauncher.launchEntryWidget(activity)
+ }
+ }
+
+ override fun getView(context: Context): View {
+ val entryWidgetTheme = themeManager.theme?.entryWidgetTheme
+ val adapter = EntryWidgetAdapter(
+ EntryWidgetContract.ViewType.EMBEDDED_VIEW,
+ entryWidgetTheme
+ )
+ //Wrapping with Glia theme to make the Theme available for embedded view
+ return EntryWidgetView(context.wrapWithTheme()).apply {
+ setAdapter(adapter)
+ setEntryWidgetTheme(entryWidgetTheme)
+ }
+ }
+
+ override fun hide() {
+ entryWidgetHideController.hide()
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetActivity.kt b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetActivity.kt
new file mode 100644
index 000000000..2a21c8512
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetActivity.kt
@@ -0,0 +1,37 @@
+package com.glia.widgets.entrywidget
+
+import android.os.Bundle
+import com.glia.widgets.base.FadeTransitionActivity
+import com.glia.widgets.di.Dependencies
+import io.reactivex.rxjava3.disposables.CompositeDisposable
+
+/**
+ * EntryWidgetActivity provides a way to display the EntryWidget bottom sheet.
+ */
+internal class EntryWidgetActivity : FadeTransitionActivity(), EntryWidgetFragment.OnDismissListener {
+
+ private var disposable: CompositeDisposable = CompositeDisposable()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ if (savedInstanceState == null) {
+ EntryWidgetFragment().show(supportFragmentManager)
+ }
+
+ disposable.add(Dependencies.controllerFactory.entryWidgetHideController.onHide.subscribe {
+ finish()
+ })
+ }
+
+ override fun onEntryWidgetDismiss() {
+ if (!isChangingConfigurations) {
+ finish()
+ }
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ disposable.dispose()
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetContract.kt b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetContract.kt
new file mode 100644
index 000000000..bda469242
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetContract.kt
@@ -0,0 +1,45 @@
+package com.glia.widgets.entrywidget
+
+import android.app.Activity
+import com.glia.widgets.base.BaseController
+import com.glia.widgets.base.BaseView
+
+private const val LOWEST_ORDER = Int.MAX_VALUE
+private const val HIGHEST_ORDER = Int.MIN_VALUE
+
+internal interface EntryWidgetContract {
+
+ enum class ViewType {
+ BOTTOM_SHEET,
+ EMBEDDED_VIEW,
+ MESSAGING_LIVE_SUPPORT
+ }
+
+ sealed class ItemType(private val order: Int = HIGHEST_ORDER) : Comparable {
+ data object VideoCall : ItemType(order = 0)
+ data object AudioCall : ItemType(order = 1)
+ data object Chat : ItemType(order = 2)
+ data class Messaging(val value: Int) : ItemType(order = 3)
+ data object LoadingState : ItemType()
+ data object EmptyState : ItemType()
+ data object SdkNotInitializedState : ItemType()
+ data object ErrorState : ItemType()
+ data object PoweredBy : ItemType(order = LOWEST_ORDER)
+
+ override fun compareTo(other: ItemType): Int {
+ return this.order.compareTo(other.order)
+ }
+ }
+
+ interface Controller : BaseController {
+ fun setView(view: View, type: ViewType)
+ fun onItemClicked(itemType: ItemType, activity: Activity)
+ }
+
+ interface View : BaseView {
+ val whiteLabel: Boolean
+ fun showItems(items: List)
+ fun dismiss()
+ fun getActivity(): Activity
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetController.kt b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetController.kt
new file mode 100644
index 000000000..c195e1d0f
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetController.kt
@@ -0,0 +1,177 @@
+package com.glia.widgets.entrywidget
+
+import android.app.Activity
+import com.glia.androidsdk.Engagement
+import com.glia.androidsdk.queuing.Queue
+import com.glia.widgets.chat.domain.IsAuthenticatedUseCase
+import com.glia.widgets.core.queue.QueueRepository
+import com.glia.widgets.core.queue.QueuesState
+import com.glia.widgets.core.secureconversations.SecureConversationsRepository
+import com.glia.widgets.core.secureconversations.domain.HasOngoingSecureConversationUseCase
+import com.glia.widgets.di.GliaCore
+import com.glia.widgets.helper.Logger
+import com.glia.widgets.helper.TAG
+import com.glia.widgets.helper.mediaTypes
+import com.glia.widgets.launcher.EngagementLauncher
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
+import io.reactivex.rxjava3.core.Flowable
+import io.reactivex.rxjava3.disposables.CompositeDisposable
+
+internal class EntryWidgetController @JvmOverloads constructor(
+ private val queueRepository: QueueRepository,
+ private val isAuthenticatedUseCase: IsAuthenticatedUseCase,
+ private val secureConversationsRepository: SecureConversationsRepository,
+ private val hasOngoingSecureConversationUseCase: HasOngoingSecureConversationUseCase,
+ private val core: GliaCore,
+ private val engagementLauncher: EngagementLauncher,
+ private val compositeDisposable: CompositeDisposable = CompositeDisposable()
+) : EntryWidgetContract.Controller {
+ private val queueStateObservable by lazy { queueRepository.queuesState }
+ private val unreadMessagesCountObservable by lazy { secureConversationsRepository.unreadMessagesCountObservable }
+ private val hasOngoingSCObservable by lazy { hasOngoingSecureConversationUseCase() }
+
+ private val loadingState: List by lazy {
+ listOf(
+ EntryWidgetContract.ItemType.LoadingState,
+ EntryWidgetContract.ItemType.LoadingState,
+ EntryWidgetContract.ItemType.LoadingState,
+ EntryWidgetContract.ItemType.LoadingState
+ )
+ }
+
+ private val emptyState: List by lazy {
+ listOf(EntryWidgetContract.ItemType.EmptyState)
+ }
+
+ private val errorState: List by lazy {
+ listOf(EntryWidgetContract.ItemType.EmptyState)
+ }
+
+ private val messagingChatObservableItemType: Flowable>
+ get() = queueStateObservable.map(::mapMessagingQueueState)
+
+ private val entryWidgetObservableItemType: Flowable>
+ get() = Flowable.combineLatest(queueStateObservable, unreadMessagesCountObservable, hasOngoingSCObservable, ::mapEntryWidgetQueueState)
+
+ private lateinit var view: EntryWidgetContract.View
+
+ override fun setView(view: EntryWidgetContract.View, type: EntryWidgetContract.ViewType) {
+ this.view = view
+
+ if (!core.isInitialized) {
+ showSdkNotInitializedState(type == EntryWidgetContract.ViewType.MESSAGING_LIVE_SUPPORT)
+ }
+
+ itemsObservableBasedOnType(type)
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(view::showItems)
+ .let(compositeDisposable::add)
+ }
+
+ private fun itemsObservableBasedOnType(type: EntryWidgetContract.ViewType) = when (type) {
+ EntryWidgetContract.ViewType.MESSAGING_LIVE_SUPPORT -> messagingChatObservableItemType
+ else -> entryWidgetObservableItemType
+ }
+
+ private fun showSdkNotInitializedState(isMessaging: Boolean) {
+ val items = if (view.whiteLabel || isMessaging) {
+ listOf(EntryWidgetContract.ItemType.SdkNotInitializedState)
+ } else {
+ listOf(EntryWidgetContract.ItemType.SdkNotInitializedState, EntryWidgetContract.ItemType.PoweredBy)
+ }
+ view.showItems(items)
+ }
+
+ private fun mapEntryWidgetQueueState(
+ queuesState: QueuesState,
+ unreadMessagesCount: Int,
+ hasOngoingSC: Boolean
+ ): List {
+
+ val messagingOrDefault: (default: List) -> List = { default ->
+ /* This check may be unreliable due to the asynchronous nature of authentication.
+ * We lack a callback for authentication completion, making it difficult to track when authentication has finished.
+ * Sometimes(when we authenticate with opened Entry Widget embedded view),
+ * we receive updates for ongoing secure conversations before the authentication result is saved, causing this check to return false. */
+ if (hasOngoingSC && isAuthenticatedUseCase())
+ listOf(EntryWidgetContract.ItemType.Messaging(unreadMessagesCount))
+ else
+ default
+ }
+
+ val items = when (queuesState) {
+ QueuesState.Empty -> messagingOrDefault(emptyState)
+ QueuesState.Loading -> messagingOrDefault(loadingState)
+ is QueuesState.Error -> messagingOrDefault(errorState)
+ is QueuesState.Queues -> mapEntryWidgetQueues(queuesState.queues, unreadMessagesCount, hasOngoingSC)
+ }.toMutableList()
+
+ if (!view.whiteLabel) {
+ items.add(EntryWidgetContract.ItemType.PoweredBy)
+ }
+
+ return items.apply { sort() }
+ }
+
+ private fun mapEntryWidgetQueues(queues: List, unreadMessagesCount: Int, hasOngoingSC: Boolean): List {
+ val messaging = EntryWidgetContract.ItemType.Messaging(unreadMessagesCount)
+
+ val items = queues.mediaTypes.mapNotNull {
+ when {
+ it == Engagement.MediaType.VIDEO -> EntryWidgetContract.ItemType.VideoCall
+ it == Engagement.MediaType.AUDIO -> EntryWidgetContract.ItemType.AudioCall
+ it == Engagement.MediaType.TEXT -> EntryWidgetContract.ItemType.Chat
+ it == Engagement.MediaType.MESSAGING && isAuthenticatedUseCase() -> messaging
+
+ else -> null
+ }
+ }
+
+ return when {
+ hasOngoingSC && !items.contains(messaging) && isAuthenticatedUseCase() -> items + messaging
+ items.isEmpty() -> emptyState
+ else -> items
+ }
+
+ }
+
+ private fun mapMessagingQueueState(queuesState: QueuesState): List = when (queuesState) {
+ QueuesState.Empty -> emptyState
+ QueuesState.Loading -> loadingState
+ is QueuesState.Error -> errorState
+ is QueuesState.Queues -> queuesState.queues.mediaTypes.mapNotNull { mediaType ->
+ when (mediaType) {
+ Engagement.MediaType.VIDEO -> EntryWidgetContract.ItemType.VideoCall
+ Engagement.MediaType.AUDIO -> EntryWidgetContract.ItemType.AudioCall
+ Engagement.MediaType.TEXT -> EntryWidgetContract.ItemType.Chat
+ else -> null
+ }
+ }.takeIf { it.isNotEmpty() }?.sorted() ?: emptyState
+ }
+
+ override fun onItemClicked(itemType: EntryWidgetContract.ItemType, activity: Activity) {
+ Logger.d(TAG, "Item clicked: $itemType")
+
+ when (itemType) {
+ EntryWidgetContract.ItemType.Chat -> engagementLauncher.startChat(activity)
+ EntryWidgetContract.ItemType.AudioCall -> engagementLauncher.startAudioCall(activity)
+ EntryWidgetContract.ItemType.VideoCall -> engagementLauncher.startVideoCall(activity)
+ is EntryWidgetContract.ItemType.Messaging -> engagementLauncher.startSecureMessaging(activity)
+ EntryWidgetContract.ItemType.ErrorState -> onRetryButtonClicked()
+ else -> {}
+ }
+
+ if (itemType != EntryWidgetContract.ItemType.ErrorState) {
+ // Dismiss the widget only if the clicked item is not a retry button
+ view.dismiss()
+ }
+ }
+
+ private fun onRetryButtonClicked() {
+ queueRepository.fetchQueues()
+ }
+
+ override fun onDestroy() {
+ compositeDisposable.dispose()
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetFragment.kt b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetFragment.kt
new file mode 100644
index 000000000..7af210f50
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetFragment.kt
@@ -0,0 +1,102 @@
+package com.glia.widgets.entrywidget
+
+import android.app.Dialog
+import android.content.Context
+import android.content.DialogInterface
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.VisibleForTesting
+import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.FragmentManager
+import com.glia.widgets.R
+import com.glia.widgets.databinding.EntryWidgetFragmentBinding
+import com.glia.widgets.di.Dependencies
+import com.glia.widgets.entrywidget.adapter.EntryWidgetAdapter
+import com.glia.widgets.helper.wrapWithMaterialThemeOverlay
+import com.glia.widgets.view.unifiedui.applyLayerTheme
+import com.glia.widgets.view.unifiedui.theme.entrywidget.EntryWidgetTheme
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.bottomsheet.BottomSheetDialog
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment
+
+/**
+ * EntryWidgetFragment (bottom sheet) provides a way to display the entry points for the user to start a chat, audio call, video call, or secure messaging.
+ */
+internal class EntryWidgetFragment : BottomSheetDialogFragment() {
+
+ interface OnDismissListener {
+ fun onEntryWidgetDismiss()
+ }
+
+ private var onDismissListener: (() -> Unit)? = null
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ val layoutInflater = LayoutInflater.from(requireContext().wrapWithMaterialThemeOverlay())
+ val binding = EntryWidgetFragmentBinding.inflate(layoutInflater, container, false)
+ val entryWidgetsTheme = Dependencies.gliaThemeManager.theme?.entryWidgetTheme
+
+ setupView(
+ requireContext(),
+ binding,
+ entryWidgetsTheme
+ )
+
+ return binding.root
+ }
+
+ @VisibleForTesting
+ fun setupView(
+ context: Context,
+ binding: EntryWidgetFragmentBinding,
+ entryWidgetsTheme: EntryWidgetTheme?
+ ) {
+ val entryWidgetAdapter = EntryWidgetAdapter(
+ EntryWidgetContract.ViewType.BOTTOM_SHEET,
+ entryWidgetsTheme
+ )
+
+ EntryWidgetView(context).apply {
+ setAdapter(entryWidgetAdapter)
+ setEntryWidgetTheme(entryWidgetsTheme)
+ binding.container.addView(this)
+ onDismissListener = {
+ this@EntryWidgetFragment.dismiss()
+ }
+ }
+
+ entryWidgetsTheme?.background?.let {
+ binding.root.applyLayerTheme(it)
+ }
+ }
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ setStyle(DialogFragment.STYLE_NO_FRAME, R.style.ThemeOverlay_Glia_BottomSheetDialog)
+ val dialog = super.onCreateDialog(savedInstanceState)
+ (dialog as? BottomSheetDialog)?.behavior?.state = BottomSheetBehavior.STATE_EXPANDED
+ return dialog
+ }
+
+ override fun onAttach(context: Context) {
+ super.onAttach(context)
+
+ (activity as? OnDismissListener)?.let {
+ onDismissListener = it::onEntryWidgetDismiss
+ }
+ }
+
+ override fun onDismiss(dialog: DialogInterface) {
+ super.onDismiss(dialog)
+
+ onDismissListener?.invoke()
+ }
+
+ fun show(parentFragmentManager: FragmentManager) {
+ show(parentFragmentManager, tag)
+ }
+
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetHideController.kt b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetHideController.kt
new file mode 100644
index 000000000..3ec6b3d84
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetHideController.kt
@@ -0,0 +1,12 @@
+package com.glia.widgets.entrywidget
+
+import io.reactivex.rxjava3.processors.PublishProcessor
+
+internal class EntryWidgetHideController {
+ private val _onHide: PublishProcessor = PublishProcessor.create()
+
+ val onHide = _onHide
+ fun hide() {
+ onHide.onNext(Unit)
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetView.kt b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetView.kt
new file mode 100644
index 000000000..fd21cd132
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetView.kt
@@ -0,0 +1,148 @@
+package com.glia.widgets.entrywidget
+
+import android.app.Activity
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.InsetDrawable
+import android.util.AttributeSet
+import android.util.TypedValue
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.glia.widgets.R
+import com.glia.widgets.di.Dependencies
+import com.glia.widgets.entrywidget.adapter.EntryWidgetAdapter
+import com.glia.widgets.entrywidget.adapter.EntryWidgetItemDivider
+import com.glia.widgets.helper.getDrawableCompat
+import com.glia.widgets.helper.requireActivity
+import com.glia.widgets.helper.wrapWithMaterialThemeOverlay
+import com.glia.widgets.view.unifiedui.applyLayerTheme
+import com.glia.widgets.view.unifiedui.theme.base.ColorTheme
+import com.glia.widgets.view.unifiedui.theme.base.LayerTheme
+import com.glia.widgets.view.unifiedui.theme.entrywidget.EntryWidgetTheme
+import com.glia.widgets.view.unifiedui.theme.entrywidget.MediaTypeItemsTheme
+import com.glia.widgets.view.unifiedui.theme.securemessaging.SecureMessagingTheme
+
+/**
+ * EntryWidgetView provides a way to display the entry points for the user to start a chat, audio call, video call, or secure messaging.
+ */
+internal class EntryWidgetView : RecyclerView, EntryWidgetContract.View {
+
+ var onDismissListener: (() -> Unit)? = null
+
+ private var controller: EntryWidgetContract.Controller
+ private var _viewAdapter: EntryWidgetAdapter? = null
+ private val viewAdapter: EntryWidgetAdapter
+ get() = _viewAdapter ?: throw IllegalStateException("Make sure adapter is set up before attempting to show any items")
+ private var dividerView: EntryWidgetItemDivider? = null
+
+ override val whiteLabel by lazy {
+ TypedValue().run {
+ context.theme.resolveAttribute(R.attr.whiteLabel, this, true)
+ data != 0
+ }
+ }
+
+ init {
+ controller = Dependencies.controllerFactory.entryWidgetController
+ itemAnimator = null
+ setupDefaultViewAppearance()
+ }
+
+ constructor(context: Context) : super(context.wrapWithMaterialThemeOverlay())
+
+ constructor(context: Context, attrs: AttributeSet) : super(context.wrapWithMaterialThemeOverlay(), attrs)
+
+ constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context.wrapWithMaterialThemeOverlay(), attrs, defStyle)
+
+
+ private fun setupDefaultViewAppearance() {
+ val value = TypedValue()
+ context.theme.resolveAttribute(R.attr.gliaBaseLightColor, value, true)
+ setBackgroundColor(value.data)
+ }
+
+ override fun setController(controller: EntryWidgetContract.Controller) {
+ this.controller = controller
+ }
+
+ fun setAdapter(viewAdapter: EntryWidgetAdapter) {
+ this._viewAdapter?.release()
+ this._viewAdapter = viewAdapter
+
+ super.setAdapter(viewAdapter)
+
+ //For Chat screen we need to handle Item click inside ChatController
+ if (viewAdapter.viewType != EntryWidgetContract.ViewType.MESSAGING_LIVE_SUPPORT) {
+ viewAdapter.onItemClickListener = {
+ controller.onItemClicked(it, context.requireActivity())
+ }
+ }
+
+ val isBottomSheet = viewAdapter.viewType == EntryWidgetContract.ViewType.BOTTOM_SHEET
+ // Entry Widget might be embedded in scrolling views, disabling the scroll for
+ // itself would prevent unintended scrolling behavior in such cases.
+ // Scrolling is only applicable for bottom sheet view.
+ enableScrolling(isBottomSheet)
+
+ applyTheme(null, null)
+ controller.setView(this, viewAdapter.viewType)
+ }
+
+ fun setEntryWidgetTheme(entryWidgetTheme: EntryWidgetTheme?) {
+ val backgroundTheme = entryWidgetTheme?.background
+ val mediaTypeItemsTheme = entryWidgetTheme?.mediaTypeItems
+ applyTheme(backgroundTheme, mediaTypeItemsTheme)
+ }
+
+ fun setSecureMessagingTheme(secureMessagingTheme: SecureMessagingTheme?) {
+ val backgroundTheme = secureMessagingTheme?.topBannerBackground
+ val mediaTypeItemsTheme = secureMessagingTheme?.mediaTypeItems
+ applyTheme(backgroundTheme, mediaTypeItemsTheme)
+ }
+
+ override fun showItems(items: List) {
+ viewAdapter.submitList(items)
+ }
+
+ override fun dismiss() {
+ onDismissListener?.invoke()
+ }
+
+ override fun getActivity(): Activity {
+ return context.requireActivity()
+ }
+
+ private fun enableScrolling(enable: Boolean) {
+ if (!enable) {
+ // setNestedScrollingEnabled() broke scrolling in some cases (bottom sheet).
+ // By default, it is enabled. So we can only disable it if needed.
+ isNestedScrollingEnabled = false
+ }
+ layoutManager = object : LinearLayoutManager(context) {
+ override fun canScrollVertically() = enable
+ }
+ }
+
+ private fun applyTheme(backgroundTheme: LayerTheme? = null, mediaTypeItemsTheme: MediaTypeItemsTheme? = null) {
+ backgroundTheme?.let { applyLayerTheme(it) }
+ dividerView?.let(::removeItemDecoration)
+ dividerView = createDividerDrawable(mediaTypeItemsTheme?.dividerColor)?.let(::EntryWidgetItemDivider)
+ dividerView?.let(::addItemDecoration)
+
+ }
+
+ private fun createDividerDrawable(dividerColor: ColorTheme?): Drawable? {
+ return getDrawableCompat(R.drawable.bg_entry_widget_divider)
+ ?.apply {
+ dividerColor?.primaryColor?.let { setTint(it) }
+ }
+ ?.let {
+ if (viewAdapter.viewType == EntryWidgetContract.ViewType.MESSAGING_LIVE_SUPPORT) {
+ it
+ } else {
+ val padding = resources.getDimension(R.dimen.glia_large).toInt()
+ InsetDrawable(it, padding, 0, padding, 0)
+ }
+ }
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetAdapter.kt b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetAdapter.kt
new file mode 100644
index 000000000..583561a0f
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetAdapter.kt
@@ -0,0 +1,149 @@
+package com.glia.widgets.entrywidget.adapter
+
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.glia.widgets.databinding.EntryWidgetErrorItemBinding
+import com.glia.widgets.databinding.EntryWidgetLiveItemBinding
+import com.glia.widgets.databinding.EntryWidgetMessagingItemBinding
+import com.glia.widgets.databinding.EntryWidgetPoweredByItemBinding
+import com.glia.widgets.entrywidget.EntryWidgetContract
+import com.glia.widgets.helper.Logger
+import com.glia.widgets.helper.TAG
+import com.glia.widgets.helper.layoutInflater
+import com.glia.widgets.view.unifiedui.theme.base.ButtonTheme
+import com.glia.widgets.view.unifiedui.theme.base.TextTheme
+import com.glia.widgets.view.unifiedui.theme.entrywidget.EntryWidgetTheme
+import com.glia.widgets.view.unifiedui.theme.entrywidget.MediaTypeItemsTheme
+import com.glia.widgets.view.unifiedui.theme.securemessaging.SecureMessagingTheme
+
+internal class EntryWidgetAdapter(
+ val viewType: EntryWidgetContract.ViewType,
+ private val mediaTypeItemsTheme: MediaTypeItemsTheme? = null,
+ private val errorTitleTheme: TextTheme? = null,
+ private val errorMessageTheme: TextTheme? = null,
+ private val errorButtonTheme: ButtonTheme? = null
+) : ListAdapter(DIFF_CALLBACK) {
+
+ constructor(
+ viewType: EntryWidgetContract.ViewType,
+ entryWidgetTheme: EntryWidgetTheme?
+ ) : this(
+ viewType,
+ entryWidgetTheme?.mediaTypeItems,
+ entryWidgetTheme?.errorTitle,
+ entryWidgetTheme?.errorMessage,
+ entryWidgetTheme?.errorButton
+ )
+
+ constructor(
+ viewType: EntryWidgetContract.ViewType,
+ secureMessagingTheme: SecureMessagingTheme?
+ ) : this(
+ viewType,
+ secureMessagingTheme?.mediaTypeItems,
+ null,
+ null,
+ null
+ )
+
+ var onItemClickListener: ((EntryWidgetContract.ItemType) -> Unit)? = null
+
+ init {
+ when (viewType) {
+ EntryWidgetContract.ViewType.BOTTOM_SHEET,
+ EntryWidgetContract.ViewType.EMBEDDED_VIEW -> Logger.d(TAG, "Creating an EntryWidget for $viewType")
+
+ else -> { /* No need to send a log */
+ }
+ }
+ }
+
+ companion object {
+ private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(
+ oldItem: EntryWidgetContract.ItemType,
+ newItem: EntryWidgetContract.ItemType
+ ): Boolean {
+ // Whether items are the same
+ return oldItem == newItem
+ }
+
+ override fun areContentsTheSame(
+ oldItem: EntryWidgetContract.ItemType,
+ newItem: EntryWidgetContract.ItemType
+ ): Boolean {
+ // Whether content is the same
+ return oldItem == newItem
+ }
+ }
+ }
+
+ enum class ViewType {
+ LIVE_MEDIA_TYPE_ITEMS,
+ MESSAGING_MEDIA_TYPE_ITEM,
+ ERROR_ITEM,
+ PROVIDED_BY_ITEM
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ val isInsideSecureConversation = this.viewType == EntryWidgetContract.ViewType.MESSAGING_LIVE_SUPPORT
+ return when (viewType) {
+ ViewType.ERROR_ITEM.ordinal -> EntryWidgetErrorStateViewHolder(
+ EntryWidgetErrorItemBinding.inflate(parent.layoutInflater, parent, false),
+ viewType = this.viewType,
+ errorTitleTheme = errorTitleTheme,
+ errorMessageTheme = errorMessageTheme,
+ errorButtonTheme = errorButtonTheme
+ )
+ ViewType.PROVIDED_BY_ITEM.ordinal -> EntryWidgetPoweredByViewHolder(
+ EntryWidgetPoweredByItemBinding.inflate(parent.layoutInflater, parent, false)
+ )
+ ViewType.MESSAGING_MEDIA_TYPE_ITEM.ordinal -> EntryWidgetMessagingItemViewHolder(
+ EntryWidgetMessagingItemBinding.inflate(parent.layoutInflater, parent, false),
+ itemTheme = mediaTypeItemsTheme?.mediaTypeItem
+ )
+ else -> EntryWidgetLiveItemViewHolder(
+ EntryWidgetLiveItemBinding.inflate(parent.layoutInflater, parent, false),
+ itemTheme = mediaTypeItemsTheme?.mediaTypeItem,
+ isInsideSecureConversation
+ )
+ }
+ }
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ getItem(position)?.let { item ->
+ if (item is EntryWidgetContract.ItemType.Messaging && holder is EntryWidgetMessagingItemViewHolder) {
+ holder.bind(item, item.value) {
+ onItemClickListener?.invoke(item)
+ }
+ } else {
+ holder.bind(item) {
+ onItemClickListener?.invoke(item)
+ }
+ }
+ }
+ }
+
+ override fun getItemViewType(position: Int): Int {
+ return when (getItem(position)) {
+ EntryWidgetContract.ItemType.EmptyState,
+ EntryWidgetContract.ItemType.SdkNotInitializedState,
+ EntryWidgetContract.ItemType.ErrorState -> ViewType.ERROR_ITEM.ordinal
+ EntryWidgetContract.ItemType.PoweredBy -> ViewType.PROVIDED_BY_ITEM.ordinal
+ is EntryWidgetContract.ItemType.Messaging -> ViewType.MESSAGING_MEDIA_TYPE_ITEM.ordinal
+ else -> ViewType.LIVE_MEDIA_TYPE_ITEMS.ordinal
+ }
+ }
+
+ abstract class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ open fun bind(itemType: EntryWidgetContract.ItemType, onClickListener: View.OnClickListener) {}
+ }
+
+ fun release() {
+ onItemClickListener = null
+ }
+
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetErrorStateViewHolder.kt b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetErrorStateViewHolder.kt
new file mode 100644
index 000000000..424ad5193
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetErrorStateViewHolder.kt
@@ -0,0 +1,58 @@
+package com.glia.widgets.entrywidget.adapter
+
+import android.view.View
+import androidx.core.view.isVisible
+import com.glia.widgets.R
+import com.glia.widgets.databinding.EntryWidgetErrorItemBinding
+import com.glia.widgets.entrywidget.EntryWidgetContract
+import com.glia.widgets.helper.setLocaleText
+import com.glia.widgets.view.unifiedui.applyButtonTheme
+import com.glia.widgets.view.unifiedui.applyTextTheme
+import com.glia.widgets.view.unifiedui.theme.base.ButtonTheme
+import com.glia.widgets.view.unifiedui.theme.base.TextTheme
+
+internal class EntryWidgetErrorStateViewHolder(
+ private val binding: EntryWidgetErrorItemBinding,
+ private val viewType: EntryWidgetContract.ViewType,
+ errorTitleTheme: TextTheme? = null,
+ errorMessageTheme: TextTheme? = null,
+ errorButtonTheme: ButtonTheme? = null
+) : EntryWidgetAdapter.ViewHolder(binding.root) {
+
+ init {
+ errorTitleTheme?.let { binding.title.applyTextTheme(it) }
+ errorMessageTheme?.let { binding.description.applyTextTheme(it) }
+ errorButtonTheme?.let { binding.button.applyButtonTheme(it) }
+ }
+
+ override fun bind(itemType: EntryWidgetContract.ItemType, onClickListener: View.OnClickListener) {
+ when (itemType) {
+ EntryWidgetContract.ItemType.EmptyState -> {
+ binding.title.setLocaleText(R.string.entry_widget_empty_state_title)
+ binding.description.setLocaleText(R.string.entry_widget_empty_state_description)
+ binding.button.isVisible = false
+ }
+ EntryWidgetContract.ItemType.ErrorState -> {
+ binding.title.setLocaleText(R.string.entry_widget_error_state_title)
+ binding.description.setLocaleText(R.string.entry_widget_error_state_description)
+ binding.button.setLocaleText(R.string.entry_widget_error_state_try_again_button_label)
+ binding.button.setOnClickListener(onClickListener)
+ binding.button.isVisible = true
+ }
+ EntryWidgetContract.ItemType.SdkNotInitializedState -> {
+ binding.title.setLocaleText(R.string.entry_widget_error_state_title)
+ binding.description.setLocaleText(R.string.entry_widget_error_state_description)
+ binding.button.setLocaleText(R.string.entry_widget_error_state_try_again_button_label)
+ binding.button.setOnClickListener(onClickListener)
+ binding.button.isVisible = false
+ }
+ else -> {
+ // Do nothing
+ }
+ }
+
+ if (viewType == EntryWidgetContract.ViewType.BOTTOM_SHEET) {
+ binding.root.minHeight = binding.root.resources.getDimensionPixelSize(R.dimen.entry_widget_error_item_min_height)
+ }
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetItemDivider.kt b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetItemDivider.kt
new file mode 100644
index 000000000..22eeef354
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetItemDivider.kt
@@ -0,0 +1,63 @@
+package com.glia.widgets.entrywidget.adapter
+
+import android.graphics.Canvas
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.view.View
+import androidx.recyclerview.widget.RecyclerView
+
+internal class EntryWidgetItemDivider(
+ private val divider: Drawable
+) : RecyclerView.ItemDecoration() {
+
+ override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
+ super.onDraw(canvas, parent, state)
+
+ val left = parent.paddingLeft
+ val right = parent.width - parent.paddingRight
+ val lastIndex = parent.childCount - 1
+
+ for (i in 0 until lastIndex) {
+ val child = parent.getChildAt(i)
+ val position = parent.getChildAdapterPosition(child)
+ if (position == RecyclerView.NO_POSITION) {
+ continue
+ }
+
+ val viewType = parent.adapter?.getItemViewType(position)
+
+ if (isContactItem(viewType)) {
+ val params = child.layoutParams as RecyclerView.LayoutParams
+
+ val top = child.bottom + params.bottomMargin
+ val bottom = top + divider.intrinsicHeight
+
+ divider.setBounds(left, top, right, bottom)
+ divider.draw(canvas)
+ }
+ }
+ }
+
+ override fun getItemOffsets(
+ outRect: Rect,
+ view: View,
+ parent: RecyclerView,
+ state: RecyclerView.State
+ ) {
+ val position = parent.getChildAdapterPosition(view)
+ if (position == RecyclerView.NO_POSITION) {
+ return
+ }
+ val viewType = parent.adapter?.getItemViewType(position)
+ if (isContactItem(viewType)) {
+ outRect.bottom = divider.intrinsicHeight
+ } else {
+ outRect.bottom = 0
+ }
+ }
+
+ private fun isContactItem(viewType: Int?): Boolean {
+ return viewType == EntryWidgetAdapter.ViewType.LIVE_MEDIA_TYPE_ITEMS.ordinal ||
+ viewType == EntryWidgetAdapter.ViewType.MESSAGING_MEDIA_TYPE_ITEM.ordinal
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetLiveItemViewHolder.kt b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetLiveItemViewHolder.kt
new file mode 100644
index 000000000..1e16e4ec7
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetLiveItemViewHolder.kt
@@ -0,0 +1,79 @@
+package com.glia.widgets.entrywidget.adapter
+
+import android.view.View
+import androidx.core.view.isVisible
+import com.glia.widgets.R
+import com.glia.widgets.databinding.EntryWidgetLiveItemBinding
+import com.glia.widgets.entrywidget.EntryWidgetContract
+import com.glia.widgets.helper.setLocaleContentDescription
+import com.glia.widgets.helper.setLocaleHint
+import com.glia.widgets.helper.setLocaleText
+import com.glia.widgets.view.unifiedui.applyImageColorTheme
+import com.glia.widgets.view.unifiedui.applyLayerTheme
+import com.glia.widgets.view.unifiedui.applyTextTheme
+import com.glia.widgets.view.unifiedui.theme.entrywidget.MediaTypeItemTheme
+
+internal class EntryWidgetLiveItemViewHolder(
+ private val binding: EntryWidgetLiveItemBinding,
+ itemTheme: MediaTypeItemTheme?,
+ isInsideSecureConversation: Boolean
+) : EntryWidgetAdapter.ViewHolder(binding.root) {
+
+ init {
+ itemTheme?.let {
+ binding.root.applyLayerTheme(it.background)
+ binding.title.applyTextTheme(it.title)
+ binding.description.applyTextTheme(it.message)
+ binding.icon.applyImageColorTheme(it.iconColor)
+ it.loadingTintColor?.primaryColorStateList?.let { tintList ->
+ binding.iconLoading.backgroundTintList = tintList
+ binding.titleLoading.backgroundTintList = tintList
+ binding.descriptionLoading.backgroundTintList = tintList
+ }
+ }
+
+
+ if (isInsideSecureConversation) {
+ val mediumPadding = binding.root.resources.getDimension(R.dimen.glia_medium).toInt()
+ binding.root.setPadding(0, mediumPadding, 0, mediumPadding)
+ }
+ }
+
+ override fun bind(itemType: EntryWidgetContract.ItemType, onClickListener: View.OnClickListener) {
+ binding.root.setOnClickListener(onClickListener)
+ binding.root.contentDescription = null
+ when (itemType) {
+ EntryWidgetContract.ItemType.VideoCall -> {
+ binding.icon.setImageResource(R.drawable.ic_video)
+ binding.title.setLocaleText(R.string.entry_widget_video_button_label)
+ binding.description.setLocaleText(R.string.entry_widget_video_button_description)
+ binding.description.setLocaleHint(R.string.entry_widget_video_button_accessibility_hint)
+ }
+
+ EntryWidgetContract.ItemType.AudioCall -> {
+ binding.icon.setImageResource(R.drawable.ic_audio)
+ binding.title.setLocaleText(R.string.entry_widget_audio_button_label)
+ binding.description.setLocaleText(R.string.entry_widget_audio_button_description)
+ binding.description.setLocaleHint(R.string.entry_widget_audio_button_accessibility_hint)
+ }
+
+ EntryWidgetContract.ItemType.Chat -> {
+ binding.icon.setImageResource(R.drawable.ic_chat)
+ binding.title.setLocaleText(R.string.entry_widget_live_chat_button_label)
+ binding.description.setLocaleText(R.string.entry_widget_live_chat_button_description)
+ binding.description.setLocaleHint(R.string.entry_widget_live_chat_button_accessibility_hint)
+ }
+
+ else -> {
+ binding.icon.setImageResource(0)
+ binding.title.text = null
+ binding.description.text = null
+ binding.description.hint = null
+ binding.root.setLocaleContentDescription(R.string.entry_widget_loading_accessibility_label)
+ binding.root.setOnClickListener(null)
+ }
+ }
+
+ binding.loadingGroup.isVisible = itemType == EntryWidgetContract.ItemType.LoadingState
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetMessagingItemViewHolder.kt b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetMessagingItemViewHolder.kt
new file mode 100644
index 000000000..99636e740
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetMessagingItemViewHolder.kt
@@ -0,0 +1,60 @@
+package com.glia.widgets.entrywidget.adapter
+
+import android.view.View
+import androidx.core.view.isVisible
+import com.glia.widgets.R
+import com.glia.widgets.databinding.EntryWidgetMessagingItemBinding
+import com.glia.widgets.entrywidget.EntryWidgetContract
+import com.glia.widgets.helper.setLocaleHint
+import com.glia.widgets.helper.setLocaleText
+import com.glia.widgets.view.unifiedui.applyImageColorTheme
+import com.glia.widgets.view.unifiedui.applyLayerTheme
+import com.glia.widgets.view.unifiedui.applyTextTheme
+import com.glia.widgets.view.unifiedui.theme.entrywidget.MediaTypeItemTheme
+import java.util.Locale
+
+internal class EntryWidgetMessagingItemViewHolder(
+ private val binding: EntryWidgetMessagingItemBinding,
+ itemTheme: MediaTypeItemTheme?,
+) : EntryWidgetAdapter.ViewHolder(binding.root) {
+
+ init {
+ itemTheme?.let {
+ binding.root.applyLayerTheme(it.background)
+ binding.title.applyTextTheme(it.title)
+ binding.description.applyTextTheme(it.message)
+ binding.icon.applyImageColorTheme(it.iconColor)
+ it.loadingTintColor?.primaryColorStateList?.let { tintList ->
+ binding.iconLoading.backgroundTintList = tintList
+ binding.titleLoading.backgroundTintList = tintList
+ binding.descriptionLoading.backgroundTintList = tintList
+ }
+ it.badge?.also(binding.unreadMessagesBadge::applyBadgeTheme)
+ }
+ }
+
+ fun bind(itemType: EntryWidgetContract.ItemType, unreadMessageCount: Int, onClickListener: View.OnClickListener) {
+ bind(itemType, onClickListener)
+ bindBadge(unreadMessageCount)
+ }
+
+ private fun bindBadge(unreadMessageCount: Int) {
+ if (unreadMessageCount > 0) {
+ binding.unreadMessagesBadge.text = String.format(Locale.getDefault(), "%d", unreadMessageCount)
+ binding.unreadMessagesBadge.visibility = View.VISIBLE
+ } else {
+ binding.unreadMessagesBadge.text = ""
+ binding.unreadMessagesBadge.visibility = View.GONE
+ }
+ }
+
+ override fun bind(itemType: EntryWidgetContract.ItemType, onClickListener: View.OnClickListener) {
+ binding.root.setOnClickListener(onClickListener)
+ binding.root.contentDescription = null
+ binding.icon.setImageResource(R.drawable.ic_secure_message)
+ binding.title.setLocaleText(R.string.entry_widget_secure_messaging_button_label)
+ binding.description.setLocaleText(R.string.entry_widget_secure_messaging_button_description)
+ binding.description.setLocaleHint(R.string.entry_widget_secure_messaging_button_accessibility_hint)
+ binding.loadingGroup.isVisible = itemType == EntryWidgetContract.ItemType.LoadingState
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetPoweredByViewHolder.kt b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetPoweredByViewHolder.kt
new file mode 100644
index 000000000..9f47fa105
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetPoweredByViewHolder.kt
@@ -0,0 +1,12 @@
+package com.glia.widgets.entrywidget.adapter
+
+import com.glia.widgets.R
+import com.glia.widgets.databinding.EntryWidgetPoweredByItemBinding
+import com.glia.widgets.helper.setLocaleText
+
+internal class EntryWidgetPoweredByViewHolder(binding: EntryWidgetPoweredByItemBinding) : EntryWidgetAdapter.ViewHolder(binding.root) {
+
+ init {
+ binding.title.setLocaleText(R.string.general_powered)
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/filepreview/data/GliaFileRepository.kt b/widgetssdk/src/main/java/com/glia/widgets/filepreview/data/GliaFileRepository.kt
index a10f3dd0d..8c6a500b2 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/filepreview/data/GliaFileRepository.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/filepreview/data/GliaFileRepository.kt
@@ -12,6 +12,6 @@ internal interface GliaFileRepository {
fun putImageToCache(fileName: String, bitmap: Bitmap)
fun loadImageFromDownloads(fileName: String): Maybe
fun putImageToDownloads(fileName: String, bitmap: Bitmap): Completable
- fun loadImageFileFromNetwork(attachmentFile: AttachmentFile): Maybe
- fun downloadFileFromNetwork(attachmentFile: AttachmentFile): Completable
+ fun loadImageFileFromNetwork(shouldUseSecureMessagingEndpoints: Boolean, attachmentFile: AttachmentFile): Maybe
+ fun downloadFileFromNetwork(shouldUseSecureMessagingEndpoints: Boolean, attachmentFile: AttachmentFile): Completable
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/filepreview/data/GliaFileRepositoryImpl.kt b/widgetssdk/src/main/java/com/glia/widgets/filepreview/data/GliaFileRepositoryImpl.kt
index 08e191faf..4eb70de07 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/filepreview/data/GliaFileRepositoryImpl.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/filepreview/data/GliaFileRepositoryImpl.kt
@@ -4,8 +4,6 @@ import android.graphics.Bitmap
import com.glia.androidsdk.RequestCallback
import com.glia.androidsdk.chat.AttachmentFile
import com.glia.androidsdk.secureconversations.SecureConversations
-import com.glia.widgets.chat.ChatType
-import com.glia.widgets.core.engagement.GliaEngagementConfigRepository
import com.glia.widgets.di.GliaCore
import com.glia.widgets.filepreview.data.source.local.DownloadsFolderDataSource
import com.glia.widgets.filepreview.data.source.local.InAppBitmapCache
@@ -14,13 +12,11 @@ import com.glia.widgets.helper.fileName
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.core.Maybe
import java.io.InputStream
-import kotlin.jvm.optionals.getOrNull
internal class GliaFileRepositoryImpl(
private val bitmapCache: InAppBitmapCache,
private val downloadsFolderDataSource: DownloadsFolderDataSource,
- private val gliaCore: GliaCore,
- private val engagementConfigRepository: GliaEngagementConfigRepository
+ private val gliaCore: GliaCore
) : GliaFileRepository {
private val secureConversations: SecureConversations by lazy {
gliaCore.secureConversations
@@ -46,12 +42,12 @@ internal class GliaFileRepositoryImpl(
override fun putImageToDownloads(fileName: String, bitmap: Bitmap): Completable =
downloadsFolderDataSource.putImageToDownloads(fileName, bitmap)
- override fun loadImageFileFromNetwork(attachmentFile: AttachmentFile): Maybe {
- return fetchFileMaybe(attachmentFile)
+ override fun loadImageFileFromNetwork(shouldUseSecureMessagingEndpoints: Boolean, attachmentFile: AttachmentFile): Maybe {
+ return fetchFileMaybe(shouldUseSecureMessagingEndpoints, attachmentFile)
}
- override fun downloadFileFromNetwork(attachmentFile: AttachmentFile): Completable {
- return fetchFileMaybe(attachmentFile).flatMapCompletable {
+ override fun downloadFileFromNetwork(shouldUseSecureMessagingEndpoints: Boolean, attachmentFile: AttachmentFile): Completable {
+ return fetchFileMaybe(shouldUseSecureMessagingEndpoints, attachmentFile).flatMapCompletable {
downloadsFolderDataSource.downloadFileToDownloads(
attachmentFile.fileName,
attachmentFile.contentType,
@@ -60,9 +56,9 @@ internal class GliaFileRepositoryImpl(
}
}
- private fun fetchFileMaybe(attachmentFile: AttachmentFile): Maybe =
+ private fun fetchFileMaybe(shouldUseSecureMessagingEndpoints: Boolean, attachmentFile: AttachmentFile): Maybe =
Maybe.create { emitter ->
- fetchFile(attachmentFile) { response, exception ->
+ fetchFile(shouldUseSecureMessagingEndpoints, attachmentFile) { response, exception ->
when {
exception != null -> emitter.onError(exception)
response != null -> emitter.onSuccess(response)
@@ -71,9 +67,8 @@ internal class GliaFileRepositoryImpl(
}
}
- private fun fetchFile(attachmentFile: AttachmentFile, callback: RequestCallback) {
- val engagement = gliaCore.currentEngagement.getOrNull()
- if (engagement == null && engagementConfigRepository.chatType == ChatType.SECURE_MESSAGING) {
+ private fun fetchFile(shouldUseSecureMessagingEndpoints: Boolean, attachmentFile: AttachmentFile, callback: RequestCallback) {
+ if (shouldUseSecureMessagingEndpoints) {
secureConversations.fetchFile(attachmentFile.id, callback)
} else {
gliaCore.fetchFile(attachmentFile, callback)
diff --git a/widgetssdk/src/main/java/com/glia/widgets/filepreview/domain/usecase/DownloadFileUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/filepreview/domain/usecase/DownloadFileUseCase.kt
index f2e49d5b1..fada0ad3f 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/filepreview/domain/usecase/DownloadFileUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/filepreview/domain/usecase/DownloadFileUseCase.kt
@@ -1,15 +1,19 @@
package com.glia.widgets.filepreview.domain.usecase
import com.glia.androidsdk.chat.AttachmentFile
+import com.glia.widgets.core.secureconversations.domain.ManageSecureMessagingStatusUseCase
import com.glia.widgets.filepreview.data.GliaFileRepository
import com.glia.widgets.filepreview.domain.exception.FileNameMissingException
import com.glia.widgets.filepreview.domain.exception.RemoteFileIsDeletedException
import io.reactivex.rxjava3.core.Completable
-internal class DownloadFileUseCase(private val fileRepository: GliaFileRepository) {
+internal class DownloadFileUseCase(
+ private val fileRepository: GliaFileRepository,
+ private val manageSecureMessagingStatusUseCase: ManageSecureMessagingStatusUseCase
+) {
operator fun invoke(file: AttachmentFile): Completable = when {
file.name.isEmpty() -> Completable.error(FileNameMissingException())
file.isDeleted -> Completable.error(RemoteFileIsDeletedException())
- else -> fileRepository.downloadFileFromNetwork(file)
+ else -> fileRepository.downloadFileFromNetwork(manageSecureMessagingStatusUseCase.shouldUseSecureMessagingEndpoints, file)
}
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/filepreview/domain/usecase/GetImageFileFromNetworkUseCase.kt b/widgetssdk/src/main/java/com/glia/widgets/filepreview/domain/usecase/GetImageFileFromNetworkUseCase.kt
index c249be068..6a661f29d 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/filepreview/domain/usecase/GetImageFileFromNetworkUseCase.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/filepreview/domain/usecase/GetImageFileFromNetworkUseCase.kt
@@ -3,6 +3,7 @@ package com.glia.widgets.filepreview.domain.usecase
import android.graphics.Bitmap
import com.glia.androidsdk.chat.AttachmentFile
import com.glia.widgets.chat.domain.DecodeSampledBitmapFromInputStreamUseCase
+import com.glia.widgets.core.secureconversations.domain.ManageSecureMessagingStatusUseCase
import com.glia.widgets.filepreview.data.GliaFileRepository
import com.glia.widgets.filepreview.domain.exception.FileNameMissingException
import com.glia.widgets.filepreview.domain.exception.RemoteFileIsDeletedException
@@ -11,12 +12,13 @@ import io.reactivex.rxjava3.core.Maybe
internal class GetImageFileFromNetworkUseCase(
private val gliaFileRepository: GliaFileRepository,
- private val decodeSampledBitmapFromInputStreamUseCase: DecodeSampledBitmapFromInputStreamUseCase
+ private val decodeSampledBitmapFromInputStreamUseCase: DecodeSampledBitmapFromInputStreamUseCase,
+ private val manageSecureMessagingStatusUseCase: ManageSecureMessagingStatusUseCase
) {
operator fun invoke(file: AttachmentFile?): Maybe = when {
file?.name.isNullOrBlank() -> Maybe.error(FileNameMissingException())
file!!.isDeleted -> Maybe.error(RemoteFileIsDeletedException())
- else -> gliaFileRepository.loadImageFileFromNetwork(file)
+ else -> gliaFileRepository.loadImageFileFromNetwork(manageSecureMessagingStatusUseCase.shouldUseSecureMessagingEndpoints, file)
.flatMap { decodeSampledBitmapFromInputStreamUseCase(it) }
.doOnSuccess { gliaFileRepository.putImageToCache(file.fileName, it) }
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/FilePreviewActivity.kt b/widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/ImagePreviewActivity.kt
similarity index 65%
rename from widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/FilePreviewActivity.kt
rename to widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/ImagePreviewActivity.kt
index 650b0fa19..1f2f072d7 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/FilePreviewActivity.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/ImagePreviewActivity.kt
@@ -8,30 +8,28 @@ import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.os.Bundle
-import android.os.Environment
import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
-import androidx.core.content.FileProvider
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
-import com.glia.androidsdk.chat.AttachmentFile
import com.glia.widgets.R
-import com.glia.widgets.core.fileupload.model.LocalAttachment
-import com.glia.widgets.databinding.FilePreviewActivityBinding
+import com.glia.widgets.databinding.ImagePreviewActivityBinding
import com.glia.widgets.di.Dependencies
+import com.glia.widgets.helper.ExtraKeys
import com.glia.widgets.helper.Logger
import com.glia.widgets.helper.SimpleWindowInsetsAndAnimationHandler
import com.glia.widgets.helper.TAG
-import com.glia.widgets.helper.fileProviderAuthority
import com.glia.widgets.helper.getParcelable
import com.glia.widgets.helper.setLocaleContentDescription
import com.glia.widgets.helper.showToast
-import java.io.File
+import com.glia.widgets.launcher.ActivityLauncher
+
+private const val WRITE_PERMISSION_REQUEST_CODE = 110011
/**
* Glia internal class.
@@ -40,12 +38,11 @@ import java.io.File
*
* This activity is used to preview images shared in chat in full-screen.
*/
-internal class FilePreviewActivity : AppCompatActivity(), FilePreviewContract.View {
+internal class ImagePreviewActivity : AppCompatActivity(), ImagePreviewContract.View {
+ private val activityLauncher: ActivityLauncher by lazy { Dependencies.activityLauncher }
private val localeProvider = Dependencies.localeProvider
- private val binding: FilePreviewActivityBinding by lazy {
- FilePreviewActivityBinding.inflate(layoutInflater)
- }
+ private val binding: ImagePreviewActivityBinding by lazy { ImagePreviewActivityBinding.inflate(layoutInflater) }
private val hasExternalStoragePermission: Boolean
get() = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
@@ -53,7 +50,7 @@ internal class FilePreviewActivity : AppCompatActivity(), FilePreviewContract.Vi
private var showDownloadIcon = false
private var showShareIcon = false
- private var filePreviewController: FilePreviewContract.Controller? = null
+ private var imagePreviewController: ImagePreviewContract.Controller? = null
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(newBase)
@@ -65,7 +62,7 @@ internal class FilePreviewActivity : AppCompatActivity(), FilePreviewContract.Vi
enableEdgeToEdge()
super.onCreate(savedInstanceState)
SimpleWindowInsetsAndAnimationHandler(binding.root, binding.toolbar)
- Logger.i(TAG, "Create File Preview screen")
+ Logger.i(TAG, "Create Image Preview screen")
setContentView(binding.root)
title = localeProvider.getString(R.string.android_preview_title)
@@ -78,13 +75,13 @@ internal class FilePreviewActivity : AppCompatActivity(), FilePreviewContract.Vi
}
private fun onImageDataReceived(intent: Intent) {
- if (intent.hasExtra(LOCAL_IMAGE_URI)) {
- filePreviewController?.onLocalImageReceived(intent.getParcelable(LOCAL_IMAGE_URI) ?: return)
+ if (intent.hasExtra(ExtraKeys.IMAGE_PREVIEW_LOCAL_IMAGE_URI)) {
+ imagePreviewController?.onLocalImageReceived(intent.getParcelable(ExtraKeys.IMAGE_PREVIEW_LOCAL_IMAGE_URI) ?: return)
} else {
- val bitmapId = intent.getStringExtra(IMAGE_ID_KEY).orEmpty()
- val bitmapName = intent.getStringExtra(IMAGE_ID_NAME).orEmpty()
- filePreviewController?.onImageDataReceived(bitmapId, bitmapName)
- filePreviewController?.onImageRequested()
+ val bitmapId = intent.getStringExtra(ExtraKeys.IMAGE_PREVIEW_IMAGE_ID).orEmpty()
+ val bitmapName = intent.getStringExtra(ExtraKeys.IMAGE_PREVIEW_IMAGE_NAME).orEmpty()
+ imagePreviewController?.onImageDataReceived(bitmapId, bitmapName)
+ imagePreviewController?.onImageRequested()
}
}
@@ -132,8 +129,8 @@ internal class FilePreviewActivity : AppCompatActivity(), FilePreviewContract.Vi
}
override fun onDestroy() {
- Logger.i(TAG, "Destroy File Preview screen")
- filePreviewController?.onDestroy()
+ Logger.i(TAG, "Destroy Image Preview screen")
+ imagePreviewController?.onDestroy()
super.onDestroy()
}
@@ -144,18 +141,18 @@ internal class FilePreviewActivity : AppCompatActivity(), FilePreviewContract.Vi
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == WRITE_PERMISSION_REQUEST_CODE && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- filePreviewController?.onDownloadPressed()
+ imagePreviewController?.onDownloadPressed()
}
}
private fun onShareClicked() {
- filePreviewController?.onSharePressed()
+ imagePreviewController?.onSharePressed()
}
private fun onDownloadClicked() {
when {
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> filePreviewController?.onDownloadPressed()
- hasExternalStoragePermission -> filePreviewController?.onDownloadPressed()
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> imagePreviewController?.onDownloadPressed()
+ hasExternalStoragePermission -> imagePreviewController?.onDownloadPressed()
else -> requestStoragePermission()
}
}
@@ -166,15 +163,15 @@ internal class FilePreviewActivity : AppCompatActivity(), FilePreviewContract.Vi
}
private fun setImageBitmap(loadedImage: Bitmap) {
- binding.filePreviewView.setImageBitmap(loadedImage)
+ binding.previewView.setImageBitmap(loadedImage)
}
private fun setImageUri(uri: Uri) {
- binding.filePreviewView.setImageURI(uri)
+ binding.previewView.setImageURI(uri)
}
- override fun setController(controller: FilePreviewContract.Controller) {
- filePreviewController = controller
+ override fun setController(controller: ImagePreviewContract.Controller) {
+ imagePreviewController = controller
controller.setView(this)
}
@@ -187,12 +184,7 @@ internal class FilePreviewActivity : AppCompatActivity(), FilePreviewContract.Vi
}
override fun shareImageFile(fileName: String) {
- val file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString(), fileName)
- val contentUri = FileProvider.getUriForFile(this, this.fileProviderAuthority, file)
- val shareIntent = Intent(Intent.ACTION_SEND)
- shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri)
- shareIntent.type = "image/jpeg"
- startActivity(shareIntent)
+ activityLauncher.launchShareImage(this, fileName)
}
override fun shareImageFile(uri: Uri) {
@@ -217,24 +209,4 @@ internal class FilePreviewActivity : AppCompatActivity(), FilePreviewContract.Vi
override fun engagementEnded() {
finishAfterTransition()
}
-
- companion object {
- private const val WRITE_PERMISSION_REQUEST_CODE = 110011
- private const val IMAGE_ID_KEY = "image_id"
- private const val IMAGE_ID_NAME = "image_name"
- private const val LOCAL_IMAGE_URI = "local_image_uri"
-
- fun intent(context: Context, attachment: AttachmentFile): Intent {
- return Intent(context, FilePreviewActivity::class.java)
- .putExtra(IMAGE_ID_KEY, attachment.id)
- .putExtra(IMAGE_ID_NAME, attachment.name)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
- }
-
- fun intent(context: Context, attachment: LocalAttachment): Intent {
- return Intent(context, FilePreviewActivity::class.java)
- .putExtra(LOCAL_IMAGE_URI, attachment.uri)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
- }
- }
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/FilePreviewContract.kt b/widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/ImagePreviewContract.kt
similarity index 94%
rename from widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/FilePreviewContract.kt
rename to widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/ImagePreviewContract.kt
index ffc0ba329..12ad6fd41 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/FilePreviewContract.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/ImagePreviewContract.kt
@@ -4,7 +4,7 @@ import android.net.Uri
import com.glia.widgets.base.BaseController
import com.glia.widgets.base.BaseView
-internal interface FilePreviewContract {
+internal interface ImagePreviewContract {
interface Controller : BaseController {
fun onSharePressed()
fun onDownloadPressed()
diff --git a/widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/FilePreviewController.kt b/widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/ImagePreviewController.kt
similarity index 92%
rename from widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/FilePreviewController.kt
rename to widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/ImagePreviewController.kt
index 8ca29e897..b899cde79 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/FilePreviewController.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/ImagePreviewController.kt
@@ -6,16 +6,16 @@ import com.glia.widgets.filepreview.domain.usecase.GetImageFileFromDownloadsUseC
import com.glia.widgets.filepreview.domain.usecase.PutImageFileToDownloadsUseCase
import io.reactivex.rxjava3.disposables.CompositeDisposable
-internal class FilePreviewController @JvmOverloads constructor(
+internal class ImagePreviewController @JvmOverloads constructor(
private val getImageFileFromDownloadsUseCase: GetImageFileFromDownloadsUseCase,
private val getImageFileFromCacheUseCase: GetImageFileFromCacheUseCase,
private val putImageFileToDownloadsUseCase: PutImageFileToDownloadsUseCase,
private val disposables: CompositeDisposable = CompositeDisposable()
-) : FilePreviewContract.Controller {
- private var view: FilePreviewContract.View? = null
+) : ImagePreviewContract.Controller {
+ private var view: ImagePreviewContract.View? = null
private var state: State = State()
- override fun setView(view: FilePreviewContract.View) {
+ override fun setView(view: ImagePreviewContract.View) {
this.view = view
state = State()
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/FilePreviewView.kt b/widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/ImagePreviewView.kt
similarity index 91%
rename from widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/FilePreviewView.kt
rename to widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/ImagePreviewView.kt
index 9dd9a62b0..1eda106f9 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/FilePreviewView.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/ImagePreviewView.kt
@@ -6,7 +6,7 @@ import androidx.appcompat.widget.AppCompatImageView
import com.glia.widgets.R
import com.glia.widgets.di.Dependencies
-internal class FilePreviewView @JvmOverloads constructor(
+internal class ImagePreviewView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : AppCompatImageView(context, attrs) {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/helper/CommonExtensions.kt b/widgetssdk/src/main/java/com/glia/widgets/helper/CommonExtensions.kt
index 0a9580a10..00eec9fd6 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/helper/CommonExtensions.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/helper/CommonExtensions.kt
@@ -21,8 +21,8 @@ import com.glia.androidsdk.comms.MediaDirection
import com.glia.androidsdk.comms.MediaState
import com.glia.androidsdk.comms.MediaUpgradeOffer
import com.glia.androidsdk.queuing.Queue
+import com.glia.androidsdk.queuing.QueueState
import com.glia.widgets.UiTheme
-import com.glia.widgets.di.Dependencies
import com.glia.widgets.view.unifiedui.merge
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Completable
@@ -42,6 +42,18 @@ internal fun String.separateStringWithSymbol(symbol: String): String = asSequenc
internal fun Queue.supportMessaging() = state.medias.contains(MediaType.MESSAGING)
+internal fun Queue.supportedMediaTypes(): List? = state.run {
+ when {
+ status == QueueState.Status.OPEN -> medias.filterNot { it == MediaType.PHONE || it == MediaType.UNKNOWN }.takeIf { it.isNotEmpty() }
+ status == QueueState.Status.FULL && medias.contains(MediaType.MESSAGING) -> listOf(MediaType.MESSAGING)
+ status == QueueState.Status.UNSTAFFED && medias.contains(MediaType.MESSAGING) -> listOf(MediaType.MESSAGING)
+ else -> null
+ }
+}
+
+internal val List.mediaTypes: List
+ get() = this.mapNotNull { it.supportedMediaTypes() }.flatten().distinct()
+
internal fun MediaType.isAudioOrVideo() = this == MediaType.AUDIO || this == MediaType.VIDEO
internal fun formatElapsedTime(elapsedMilliseconds: Long) = DateUtils.formatElapsedTime(elapsedMilliseconds / DateUtils.SECOND_IN_MILLIS)
@@ -58,9 +70,6 @@ internal fun UiTheme?.isAlertDialogButtonUseVerticalAlignment(): Boolean = this?
internal fun UiTheme?.getFullHybridTheme(newTheme: UiTheme?): UiTheme = merge(newTheme) ?: UiTheme.UiThemeBuilder().build()
-internal val UiTheme?.withConfigurationTheme: UiTheme
- get() = getFullHybridTheme(Dependencies.sdkConfigurationManager.uiTheme)
-
/**
* Returns styled text from the provided HTML string. Replaces \n to regardless of the operating system where the string was created.
*/
diff --git a/widgetssdk/src/main/java/com/glia/widgets/helper/ContextExtensions.kt b/widgetssdk/src/main/java/com/glia/widgets/helper/ContextExtensions.kt
index 06756c054..664005cb9 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/helper/ContextExtensions.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/helper/ContextExtensions.kt
@@ -1,9 +1,15 @@
+@file:JvmName("ContextExtensions")
+
package com.glia.widgets.helper
import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
+import android.content.Intent
+import android.os.Build
+import android.os.Parcelable
import android.util.AttributeSet
+import android.util.TypedValue
import android.view.View
import android.widget.Toast
import androidx.annotation.AttrRes
@@ -11,12 +17,14 @@ import androidx.annotation.DimenRes
import androidx.annotation.IntRange
import androidx.annotation.StyleRes
import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.content.withStyledAttributes
+import androidx.core.util.TypedValueCompat
import com.glia.widgets.BuildConfig
-import com.glia.widgets.GliaWidgets
import com.glia.widgets.R
import com.glia.widgets.UiTheme
import com.google.android.material.theme.overlay.MaterialThemeOverlay
+import java.io.Serializable
internal fun Context.asActivity(): Activity? = (this as? ContextWrapper)?.let {
it as? Activity ?: it.baseContext.asActivity()
@@ -25,7 +33,7 @@ internal fun Context.asActivity(): Activity? = (this as? ContextWrapper)?.let {
internal fun Context.requireActivity(): Activity =
asActivity() ?: throw IllegalStateException("Context $this is not an Activity.")
-internal fun Context.pxToSp(pixels: Float): Float = pixels / resources.displayMetrics.scaledDensity
+internal fun Context.pxToSp(pixels: Float): Float = TypedValueCompat.deriveDimension(TypedValue.COMPLEX_UNIT_SP, pixels, resources.displayMetrics)
internal fun Context.getDimenRes(@DimenRes dimenId: Int): Float = resources.getDimension(dimenId)
internal fun Context.getDimenResPx(@DimenRes dimenId: Int): Int =
resources.getDimensionPixelSize(dimenId)
@@ -43,6 +51,9 @@ internal fun Context.wrapWithMaterialThemeOverlay(
)
}
+internal fun Context.wrapWithTheme(@StyleRes themeResId: Int = R.style.Application_Glia_Activity_Style): Context =
+ ContextThemeWrapper(this, themeResId)
+
internal fun Context.showToast(
message: String,
@IntRange(from = 0, to = 1) duration: Int = Toast.LENGTH_LONG
@@ -55,11 +66,8 @@ internal val Activity.rootView: View
internal fun Activity.withRuntimeTheme(callback: (themedContext: Context, uiTheme: UiTheme) -> Unit) {
val themedContext = wrapWithMaterialThemeOverlay()
-
- intent.getParcelableExtra(GliaWidgets.UI_THEME)?.also {
- callback(themedContext, it.withConfigurationTheme)
- } ?: themedContext.withStyledAttributes(R.style.Application_Glia_Chat, R.styleable.GliaView) {
- callback(themedContext, Utils.getThemeFromTypedArray(this, themedContext).withConfigurationTheme)
+ themedContext.withStyledAttributes(R.style.Application_Glia_Chat, R.styleable.GliaView) {
+ callback(themedContext, Utils.getThemeFromTypedArray(this, themedContext))
}
}
@@ -70,3 +78,35 @@ internal val Activity.isGlia: Boolean
get() = qualifiedName.startsWith(BuildConfig.LIBRARY_PACKAGE_NAME + ".")
internal val AlertDialog.parentActivity: Activity? get() = context.asActivity()
+
+internal fun Context.safeStartActivity(intent: Intent, onFailure: () -> Unit, onSuccess: () -> Unit = {}) {
+ if (intent.resolveActivity(packageManager) != null) {
+ startActivity(intent)
+ onSuccess()
+ } else {
+ onFailure()
+ }
+}
+
+internal fun Intent.setSafeFlags(context: Context): Intent = context.asActivity()?.let { this } ?: addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+
+internal inline fun Intent.getSerializableExtraCompat(key: String): T? =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ getSerializableExtra(key, T::class.java)
+ } else {
+ @Suppress("DEPRECATION")
+ getSerializableExtra(key) as? T
+ }
+
+internal inline fun Intent.getParcelableExtraCompat(key: String): T? =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ getParcelableExtra(key, T::class.java)
+ } else {
+ @Suppress("DEPRECATION")
+ getParcelableExtra(key) as? T
+ }
+
+internal inline fun > Intent.putEnumExtra(key: String, value: T?): Intent = putExtra(key, value?.ordinal)
+
+internal inline fun > Intent.getEnumExtra(key: String): T? = getIntExtra(key, -1).takeIf { it != -1 }
+ ?.let { T::class.java.enumConstants?.getOrNull(it) }
diff --git a/widgetssdk/src/main/java/com/glia/widgets/helper/Data.kt b/widgetssdk/src/main/java/com/glia/widgets/helper/Data.kt
index 9ee4260ac..95ef18b7d 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/helper/Data.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/helper/Data.kt
@@ -2,7 +2,15 @@ package com.glia.widgets.helper
internal sealed class Data {
val hasValue: Boolean get() = this is Value
+ val valueOrNull: T? get() = (this as? Value)?.result
data class Value(val result: T) : Data()
- object Empty : Data()
+ data object Empty : Data()
+
+ companion object
+}
+
+internal fun Data.Companion.from(value: T?): Data = when (value) {
+ null -> Data.Empty
+ else -> Data.Value(value)
}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/helper/IntentConfigurationHelper.kt b/widgetssdk/src/main/java/com/glia/widgets/helper/IntentConfigurationHelper.kt
deleted file mode 100644
index a1b6ef1a2..000000000
--- a/widgetssdk/src/main/java/com/glia/widgets/helper/IntentConfigurationHelper.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.glia.widgets.helper
-
-import android.content.Context
-import android.content.Intent
-import android.provider.Settings
-import androidx.core.net.toUri
-import com.glia.androidsdk.Engagement.MediaType
-import com.glia.widgets.call.CallActivity
-import com.glia.widgets.call.CallConfiguration
-import com.glia.widgets.di.Dependencies
-
-internal interface IntentConfigurationHelper {
- fun createForCall(context: Context, mediaType: MediaType, upgradeToCall: Boolean = true): Intent
-
- fun createForOverlayPermissionScreen(context: Context): Intent
-}
-
-internal class IntentConfigurationHelperImpl : IntentConfigurationHelper {
- private val defaultBuilder: CallConfiguration.Builder
- get() = Dependencies.sdkConfigurationManager
- .buildEngagementConfiguration()
- .let(CallConfiguration.Builder()::setEngagementConfiguration)
-
- override fun createForCall(context: Context, mediaType: MediaType, upgradeToCall: Boolean): Intent = defaultBuilder
- .setMediaType(mediaType)
- .setIsUpgradeToCall(upgradeToCall)
- .run { CallActivity.getIntent(context, build()) }
-
- override fun createForOverlayPermissionScreen(context: Context): Intent = context.run {
- Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, "package:${packageName}".toUri()).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- }
-}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/helper/IntentHelper.kt b/widgetssdk/src/main/java/com/glia/widgets/helper/IntentHelper.kt
new file mode 100644
index 000000000..dfff3d023
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/helper/IntentHelper.kt
@@ -0,0 +1,151 @@
+package com.glia.widgets.helper
+
+import android.app.Activity
+import android.content.ClipData
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.os.Environment
+import android.os.Parcelable
+import android.provider.Settings
+import androidx.core.content.FileProvider
+import androidx.core.net.toUri
+import com.glia.androidsdk.Engagement.MediaType
+import com.glia.androidsdk.chat.AttachmentFile
+import com.glia.androidsdk.engagement.Survey
+import com.glia.widgets.call.CallActivity
+import com.glia.widgets.callvisualizer.EndScreenSharingActivity
+import com.glia.widgets.chat.ChatActivity
+import com.glia.widgets.chat.Intention
+import com.glia.widgets.core.fileupload.model.LocalAttachment
+import com.glia.widgets.entrywidget.EntryWidgetActivity
+import com.glia.widgets.filepreview.ui.ImagePreviewActivity
+import com.glia.widgets.locale.LocaleString
+import com.glia.widgets.messagecenter.MessageCenterActivity
+import com.glia.widgets.survey.SurveyActivity
+import com.glia.widgets.webbrowser.WebBrowserActivity
+import java.io.File
+
+internal object ExtraKeys {
+ const val WEB_BROWSER_TITLE = "web_browser_title"
+ const val WEB_BROWSER_URL = "web_browser_url"
+
+ const val IMAGE_PREVIEW_IMAGE_ID = "image_preview_image_id"
+ const val IMAGE_PREVIEW_IMAGE_NAME = "image_preview_image_name"
+ const val IMAGE_PREVIEW_LOCAL_IMAGE_URI = "image_preview_local_image_uri"
+
+ const val OPEN_CHAT_INTENTION = "open_chat_intention"
+
+ const val MEDIA_TYPE = "media_type"
+ const val SURVEY = "survey"
+ const val IS_UPGRADE_TO_CALL = "call_screen_is_upgrade_to_call"
+}
+
+internal interface IntentHelper {
+ fun chatIntent(context: Context, intention: Intention): Intent
+
+ fun secureMessagingWelcomeScreenIntent(context: Context): Intent
+
+ fun callIntent(context: Context, mediaType: MediaType?, upgradeToCall: Boolean): Intent
+
+ fun imagePreviewIntent(context: Context, attachment: AttachmentFile): Intent
+
+ fun imagePreviewIntent(context: Context, attachment: LocalAttachment): Intent
+
+ fun shareImageIntent(context: Context, fileName: String): Intent
+
+ fun surveyIntent(context: Context, survey: Survey): Intent
+
+ fun endScreenSharingIntent(context: Context): Intent
+
+ fun webBrowserIntent(context: Context, title: LocaleString, url: String): Intent
+
+ fun overlayPermissionIntent(context: Context): Intent
+
+ fun openEmailIntent(uri: Uri): Intent
+
+ fun dialerIntent(uri: Uri): Intent
+
+ fun openUriIntent(uri: Uri): Intent
+
+ fun openFileIntent(contentUri: Uri, fileContentType: String?): Intent
+ fun entryWidgetIntent(activity: Activity): Intent
+}
+
+internal class IntentHelperImpl : IntentHelper {
+
+ override fun chatIntent(context: Context, intention: Intention): Intent = Intent(context, ChatActivity::class.java).apply {
+ putEnumExtra(ExtraKeys.OPEN_CHAT_INTENTION, intention)
+ setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ setSafeFlags(context)
+ }
+
+ override fun secureMessagingWelcomeScreenIntent(context: Context): Intent = Intent(context, MessageCenterActivity::class.java)
+ .setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ .setSafeFlags(context)
+
+ override fun callIntent(context: Context, mediaType: MediaType?, upgradeToCall: Boolean): Intent = Intent(context, CallActivity::class.java)
+ .putEnumExtra(ExtraKeys.MEDIA_TYPE, mediaType)
+ .putExtra(ExtraKeys.IS_UPGRADE_TO_CALL, upgradeToCall)
+ .setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ .setSafeFlags(context)
+
+ override fun imagePreviewIntent(context: Context, attachment: AttachmentFile): Intent {
+ return Intent(context, ImagePreviewActivity::class.java)
+ .putExtra(ExtraKeys.IMAGE_PREVIEW_IMAGE_ID, attachment.id)
+ .putExtra(ExtraKeys.IMAGE_PREVIEW_IMAGE_NAME, attachment.name)
+ .setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ }
+
+ override fun imagePreviewIntent(context: Context, attachment: LocalAttachment): Intent {
+ return Intent(context, ImagePreviewActivity::class.java)
+ .putExtra(ExtraKeys.IMAGE_PREVIEW_LOCAL_IMAGE_URI, attachment.uri)
+ .setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ }
+
+ override fun shareImageIntent(context: Context, fileName: String): Intent {
+ val file = File(
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
+ .toString(),
+ fileName
+ )
+ val contentUri = FileProvider.getUriForFile(context, context.fileProviderAuthority, file)
+ return Intent(Intent.ACTION_SEND)
+ .putExtra(Intent.EXTRA_STREAM, contentUri)
+ .setType("image/jpeg")
+ }
+
+ override fun surveyIntent(context: Context, survey: Survey): Intent = Intent(context, SurveyActivity::class.java)
+ .putExtra(ExtraKeys.SURVEY, survey as Parcelable)
+ .setSafeFlags(context)
+
+ override fun endScreenSharingIntent(context: Context): Intent = Intent(context, EndScreenSharingActivity::class.java)
+ .setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ .setSafeFlags(context)
+
+ override fun webBrowserIntent(context: Context, title: LocaleString, url: String): Intent = Intent(context, WebBrowserActivity::class.java)
+ .putExtra(ExtraKeys.WEB_BROWSER_TITLE, title)
+ .putExtra(ExtraKeys.WEB_BROWSER_URL, url)
+ .setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
+
+ override fun overlayPermissionIntent(context: Context): Intent = context.run {
+ Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, "package:${packageName}".toUri()).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK).setSafeFlags(this)
+ }
+
+ override fun openEmailIntent(uri: Uri): Intent = Intent(Intent.ACTION_SENDTO)
+ .setData(Uri.parse("mailto:")) // This step makes sure that only email apps handle this.
+ .putExtra(Intent.EXTRA_EMAIL, arrayOf(uri.schemeSpecificPart))
+
+ override fun dialerIntent(uri: Uri): Intent = Intent(Intent.ACTION_DIAL).setData(uri)
+
+ override fun openUriIntent(uri: Uri): Intent = Intent(Intent.ACTION_VIEW, uri).addCategory(Intent.CATEGORY_BROWSABLE)
+
+ override fun openFileIntent(contentUri: Uri, fileContentType: String?): Intent = with(Intent(Intent.ACTION_VIEW)) {
+ clipData = ClipData.newRawUri("", contentUri)
+ addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+ setDataAndType(contentUri, fileContentType)
+ }
+
+ override fun entryWidgetIntent(activity: Activity): Intent = Intent(activity, EntryWidgetActivity::class.java)
+ .setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/helper/TimeCounter.kt b/widgetssdk/src/main/java/com/glia/widgets/helper/TimeCounter.kt
index 30b097852..4df607449 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/helper/TimeCounter.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/helper/TimeCounter.kt
@@ -18,10 +18,11 @@ internal class TimeCounter {
fun stop() {
Logger.d(TAG, "stop")
- timerTask?.cancel()
- timerTask = null
timer?.cancel()
timer = null
+
+ timerTask?.cancel()
+ timerTask = null
}
fun clear() {
diff --git a/widgetssdk/src/main/java/com/glia/widgets/helper/Utils.kt b/widgetssdk/src/main/java/com/glia/widgets/helper/Utils.kt
index a9cc027d8..9796491b9 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/helper/Utils.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/helper/Utils.kt
@@ -7,26 +7,14 @@ import android.util.TypedValue
import androidx.annotation.AttrRes
import androidx.annotation.StyleableRes
import androidx.core.content.res.ResourcesCompat
-import com.glia.androidsdk.Engagement
-import com.glia.widgets.GliaWidgets
import com.glia.widgets.R
import com.glia.widgets.UiTheme
import com.glia.widgets.UiTheme.UiThemeBuilder
-import java.security.InvalidParameterException
internal object Utils {
- @JvmStatic
- fun toMediaType(mediaType: String): Engagement.MediaType {
- return when (mediaType) {
- GliaWidgets.MEDIA_TYPE_VIDEO -> Engagement.MediaType.VIDEO
- GliaWidgets.MEDIA_TYPE_AUDIO -> Engagement.MediaType.AUDIO
- else -> throw InvalidParameterException("Invalid Media Type")
- }
- }
fun getThemeFromTypedArray(typedArray: TypedArray, context: Context): UiTheme {
val defaultThemeBuilder = UiThemeBuilder().apply {
- setAppBarTitle(getAppBarTitleValue(typedArray))
setBrandPrimaryColor(
getTypedArrayIntegerValue(
typedArray,
@@ -424,14 +412,6 @@ internal object Utils {
return typedArray.hasValue(index) && typedArray.getBoolean(index, false)
}
- private fun getAppBarTitleValue(typedArray: TypedArray): String? {
- return if (typedArray.hasValue(R.styleable.GliaView_appBarTitle)) {
- typedArray.getString(R.styleable.GliaView_appBarTitle)
- } else {
- null
- }
- }
-
fun getTypedArrayStringValue(
typedArray: TypedArray,
@StyleableRes index: Int
diff --git a/widgetssdk/src/main/java/com/glia/widgets/helper/ViewExtensions.kt b/widgetssdk/src/main/java/com/glia/widgets/helper/ViewExtensions.kt
index 0f608e491..163bc3393 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/helper/ViewExtensions.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/helper/ViewExtensions.kt
@@ -35,6 +35,7 @@ import com.airbnb.lottie.LottieAnimationView
import com.airbnb.lottie.LottieProperty
import com.airbnb.lottie.model.KeyPath
import com.glia.widgets.di.Dependencies
+import com.glia.widgets.locale.LocaleProvider
import com.glia.widgets.locale.LocaleString
import com.glia.widgets.locale.StringKeyPair
import com.google.android.material.button.MaterialButton
@@ -213,7 +214,7 @@ internal fun View.removeAccessibilityClickAction() {
}
private fun View.registerLocaleListener(@StringRes stringKey: Int, vararg values: StringKeyPair, listener: (String) -> Unit) {
- val localeManager = Dependencies.localeProvider
+ val localeManager = if (isInEditMode) LocaleProvider(ResourceProvider(context)) else Dependencies.localeProvider
val disposable = localeManager.getLocaleObservable()
.startWithItem("stub")
.map { localeManager.getString(stringKey, values.toList()) }
diff --git a/widgetssdk/src/main/java/com/glia/widgets/launcher/ActivityLauncher.kt b/widgetssdk/src/main/java/com/glia/widgets/launcher/ActivityLauncher.kt
new file mode 100644
index 000000000..9fd8398f4
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/launcher/ActivityLauncher.kt
@@ -0,0 +1,86 @@
+package com.glia.widgets.launcher
+
+import android.app.Activity
+import android.content.Context
+import android.net.Uri
+import android.os.Bundle
+import com.glia.androidsdk.Engagement.MediaType
+import com.glia.androidsdk.chat.AttachmentFile
+import com.glia.androidsdk.engagement.Survey
+import com.glia.widgets.chat.Intention
+import com.glia.widgets.core.fileupload.model.LocalAttachment
+import com.glia.widgets.engagement.EngagementRepository
+import com.glia.widgets.helper.IntentHelper
+import com.glia.widgets.helper.safeStartActivity
+import com.glia.widgets.locale.LocaleString
+
+
+internal interface ActivityLauncher {
+ fun launchChat(context: Context, intention: Intention)
+ fun launchCall(context: Context, mediaType: MediaType?, upgradeToCall: Boolean)
+ fun launchSecureMessagingWelcomeScreen(context: Context)
+ fun launchEndScreenSharing(context: Context)
+ fun launchWebBrowser(context: Context, title: LocaleString, url: String)
+ fun launchOverlayPermission(context: Context, onSuccess: () -> Unit = {}, onFailure: () -> Unit = {})
+ fun launchImagePreview(context: Context, attachment: AttachmentFile, options: Bundle? = null)
+ fun launchImagePreview(context: Context, attachment: LocalAttachment, options: Bundle? = null)
+ fun launchEmailClient(context: Context, uri: Uri, onFailure: () -> Unit)
+ fun launchDialer(context: Context, uri: Uri, onFailure: () -> Unit)
+ fun launchUri(context: Context, uri: Uri, onFailure: () -> Unit)
+ fun launchFileReader(context: Context, contentUri: Uri, fileContentType: String?, onFailure: () -> Unit)
+ fun launchShareImage(activity: Activity, fileName: String)
+ fun launchEntryWidget(activity: Activity)
+ fun launchSurvey(activity: Activity, survey: Survey)
+}
+
+internal class ActivityLauncherImpl(
+ private val intentHelper: IntentHelper,
+ private val engagementRepository: EngagementRepository
+) : ActivityLauncher {
+
+ override fun launchChat(context: Context, intention: Intention) {
+ engagementRepository.updateIsSecureMessagingRequested(intention.isSecureConversation)
+ context.startActivity(intentHelper.chatIntent(context, intention))
+ }
+
+ override fun launchCall(context: Context, mediaType: MediaType?, upgradeToCall: Boolean) {
+ engagementRepository.updateIsSecureMessagingRequested(false)
+ context.startActivity(intentHelper.callIntent(context, mediaType, upgradeToCall))
+ }
+
+ override fun launchSecureMessagingWelcomeScreen(context: Context) {
+ engagementRepository.updateIsSecureMessagingRequested(true)
+ context.startActivity(intentHelper.secureMessagingWelcomeScreenIntent(context))
+ }
+
+ override fun launchEndScreenSharing(context: Context) = context.startActivity(intentHelper.endScreenSharingIntent(context))
+
+ override fun launchWebBrowser(context: Context, title: LocaleString, url: String) =
+ context.startActivity(intentHelper.webBrowserIntent(context, title, url))
+
+ override fun launchOverlayPermission(context: Context, onSuccess: () -> Unit, onFailure: () -> Unit) =
+ context.safeStartActivity(intentHelper.overlayPermissionIntent(context), onFailure, onSuccess)
+
+ override fun launchImagePreview(context: Context, attachment: AttachmentFile, options: Bundle?) =
+ context.startActivity(intentHelper.imagePreviewIntent(context, attachment), options)
+
+ override fun launchImagePreview(context: Context, attachment: LocalAttachment, options: Bundle?) =
+ context.startActivity(intentHelper.imagePreviewIntent(context, attachment), options)
+
+ override fun launchEmailClient(context: Context, uri: Uri, onFailure: () -> Unit) =
+ context.safeStartActivity(intentHelper.openEmailIntent(uri), onFailure)
+
+ override fun launchDialer(context: Context, uri: Uri, onFailure: () -> Unit) =
+ context.safeStartActivity(intentHelper.dialerIntent(uri), onFailure)
+
+ override fun launchUri(context: Context, uri: Uri, onFailure: () -> Unit) = context.safeStartActivity(intentHelper.openUriIntent(uri), onFailure)
+
+ override fun launchFileReader(context: Context, contentUri: Uri, fileContentType: String?, onFailure: () -> Unit) =
+ context.safeStartActivity(intentHelper.openFileIntent(contentUri, fileContentType), onFailure)
+
+ override fun launchShareImage(activity: Activity, fileName: String) = activity.startActivity(intentHelper.shareImageIntent(activity, fileName))
+
+ override fun launchEntryWidget(activity: Activity) = activity.startActivity(intentHelper.entryWidgetIntent(activity))
+
+ override fun launchSurvey(activity: Activity, survey: Survey) = activity.startActivity(intentHelper.surveyIntent(activity, survey))
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/launcher/ConfigurationManager.kt b/widgetssdk/src/main/java/com/glia/widgets/launcher/ConfigurationManager.kt
new file mode 100644
index 000000000..3e604e68b
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/launcher/ConfigurationManager.kt
@@ -0,0 +1,72 @@
+package com.glia.widgets.launcher
+
+import com.glia.androidsdk.screensharing.ScreenSharing
+import com.glia.widgets.GliaWidgets
+import com.glia.widgets.GliaWidgetsConfig
+import io.reactivex.rxjava3.core.Flowable
+import io.reactivex.rxjava3.processors.BehaviorProcessor
+
+internal interface ConfigurationManager {
+ val screenSharingMode: ScreenSharing.Mode
+
+ val enableBubbleOutsideApp: Boolean
+ val enableBubbleInsideApp: Boolean
+
+ val queueIdsObservable: Flowable>
+
+ val visitorContextAssetId: String?
+
+ /**
+ * Applies [GliaWidgetsConfig] from the [GliaWidgets.init] configuration step
+ */
+ fun applyConfiguration(config: GliaWidgetsConfig)
+
+ /**
+ * Sets queue IDs
+ * [queueIds] A list of queue IDs to be used for the engagement launcher.
+ * When empty or invalid, the default queues will be used.
+ */
+ fun setQueueIds(queueIds: List)
+
+ /**
+ * Sets the visitor context for the engagement.
+ *
+ * @param visitorContextAssetId a visitor context id from Glia Hub.
+ */
+ fun setVisitorContextAssetId(visitorContextAssetId: String)
+}
+
+internal class ConfigurationManagerImpl : ConfigurationManager {
+ private var _screenSharingMode: ScreenSharing.Mode = ScreenSharing.Mode.APP_BOUNDED
+ override val screenSharingMode: ScreenSharing.Mode
+ get() = _screenSharingMode
+
+ private var _enableBubbleOutsideApp: Boolean = true
+ override val enableBubbleOutsideApp: Boolean
+ get() = _enableBubbleOutsideApp
+
+ private var _enableBubbleInsideApp: Boolean = true
+ override val enableBubbleInsideApp: Boolean
+ get() = _enableBubbleInsideApp
+
+ private var _queueIdsObservable: BehaviorProcessor> = BehaviorProcessor.create()
+ override val queueIdsObservable: Flowable> = _queueIdsObservable.onBackpressureLatest()
+
+ private var _visitorContextAssetId: String? = null
+ override val visitorContextAssetId: String?
+ get() = _visitorContextAssetId
+
+ override fun applyConfiguration(config: GliaWidgetsConfig) {
+ config.screenSharingMode?.also { _screenSharingMode = it }
+ config.enableBubbleInsideApp?.also { _enableBubbleInsideApp = it }
+ config.enableBubbleOutsideApp?.also { _enableBubbleOutsideApp = it }
+ }
+
+ override fun setQueueIds(queueIds: List) {
+ _queueIdsObservable.onNext(queueIds)
+ }
+
+ override fun setVisitorContextAssetId(visitorContextAssetId: String) {
+ _visitorContextAssetId = visitorContextAssetId
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/launcher/EngagementLauncher.kt b/widgetssdk/src/main/java/com/glia/widgets/launcher/EngagementLauncher.kt
new file mode 100644
index 000000000..2afeaefbb
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/launcher/EngagementLauncher.kt
@@ -0,0 +1,95 @@
+package com.glia.widgets.launcher
+
+import android.content.Context
+import com.glia.androidsdk.Engagement
+import com.glia.widgets.chat.Intention
+import com.glia.widgets.core.secureconversations.domain.HasOngoingSecureConversationUseCase
+
+/**
+ * An interface for launching different types of engagements, such as chat,
+ * audio calls, video calls, and secure messaging
+ */
+interface EngagementLauncher {
+ /**
+ * Starts a chat engagement.
+ *
+ * @param context Activity or Context used to launch the chat screen
+ * @param visitorContextAssetId Optional visitor context id from Glia Hub
+ */
+ fun startChat(context: Context, visitorContextAssetId: String? = null)
+
+ /**
+ * Starts an audio engagement.
+ *
+ * @param context Activity or Context used to launch the call screen
+ * @param visitorContextAssetId Optional visitor context id from Glia Hub
+ */
+ fun startAudioCall(context: Context, visitorContextAssetId: String? = null)
+
+ /**
+ * Starts a video engagement.
+ *
+ * @param context Activity or Context used to launch the call screen
+ * @param visitorContextAssetId Optional visitor context id from Glia Hub
+ */
+ fun startVideoCall(context: Context, visitorContextAssetId: String? = null)
+
+ /**
+ * Starts a secure messaging.
+ *
+ * @param context Activity or Context used to launch the secure messaging welcome or chat screen
+ * @param visitorContextAssetId Optional visitor context id from Glia Hub
+ */
+ fun startSecureMessaging(context: Context, visitorContextAssetId: String? = null)
+}
+
+internal class EngagementLauncherImpl(
+ private val activityLauncher: ActivityLauncher,
+ private val hasOngoingSecureConversationUseCase: HasOngoingSecureConversationUseCase,
+ private val configurationManager: ConfigurationManager
+) : EngagementLauncher {
+
+ override fun startChat(context: Context, visitorContextAssetId: String?) {
+ visitorContextAssetId?.let { configurationManager.setVisitorContextAssetId(it) }
+ hasOngoingSecureConversationUseCase {
+ if (it) {
+ activityLauncher.launchChat(context, Intention.SC_DIALOG_ENQUEUE_FOR_TEXT)
+ } else {
+ activityLauncher.launchChat(context, Intention.LIVE_CHAT)
+ }
+ }
+ }
+
+ override fun startAudioCall(context: Context, visitorContextAssetId: String?) {
+ visitorContextAssetId?.let { configurationManager.setVisitorContextAssetId(it) }
+ hasOngoingSecureConversationUseCase {
+ if (it) {
+ activityLauncher.launchChat(context, Intention.SC_DIALOG_START_AUDIO)
+ } else {
+ activityLauncher.launchCall(context, Engagement.MediaType.AUDIO, false)
+ }
+ }
+ }
+
+ override fun startVideoCall(context: Context, visitorContextAssetId: String?) {
+ visitorContextAssetId?.let { configurationManager.setVisitorContextAssetId(it) }
+ hasOngoingSecureConversationUseCase {
+ if (it) {
+ activityLauncher.launchChat(context, Intention.SC_DIALOG_START_VIDEO)
+ } else {
+ activityLauncher.launchCall(context, Engagement.MediaType.VIDEO, false)
+ }
+ }
+ }
+
+ override fun startSecureMessaging(context: Context, visitorContextAssetId: String?) {
+ visitorContextAssetId?.let { configurationManager.setVisitorContextAssetId(it) }
+ hasOngoingSecureConversationUseCase {
+ if (it) {
+ activityLauncher.launchChat(context, Intention.SC_CHAT)
+ } else {
+ activityLauncher.launchSecureMessagingWelcomeScreen(context)
+ }
+ }
+ }
+}
diff --git a/widgetssdk/src/main/java/com/glia/widgets/locale/LocaleProvider.kt b/widgetssdk/src/main/java/com/glia/widgets/locale/LocaleProvider.kt
index 8448a897c..04637a4b9 100644
--- a/widgetssdk/src/main/java/com/glia/widgets/locale/LocaleProvider.kt
+++ b/widgetssdk/src/main/java/com/glia/widgets/locale/LocaleProvider.kt
@@ -6,6 +6,7 @@ import androidx.annotation.StringRes
import com.glia.androidsdk.Glia
import com.glia.androidsdk.GliaException
import com.glia.androidsdk.locale.LocaleManager
+import com.glia.widgets.BuildConfig
import com.glia.widgets.R
import com.glia.widgets.StringProvider
import com.glia.widgets.helper.IResourceProvider
@@ -17,7 +18,6 @@ import io.reactivex.rxjava3.core.Observer
import io.reactivex.rxjava3.schedulers.Schedulers
import io.reactivex.rxjava3.subjects.PublishSubject
import kotlinx.parcelize.Parcelize
-import java.lang.Exception
/**
* @hide
@@ -26,7 +26,7 @@ import java.lang.Exception
data class LocaleString(
@StringRes val stringKey: Int,
val values: List = emptyList()
-): Parcelable {
+) : Parcelable {
constructor(@StringRes stringKey: Int, vararg values: StringKeyPair) : this(stringKey, values.toList())
@@ -43,6 +43,7 @@ internal open class LocaleProvider @JvmOverloads constructor(
private val localeObservable: Observable
private val localeEmitter: Observer