From d92cb8cf7969a1375523e83ae0bdd3255d775cc0 Mon Sep 17 00:00:00 2001 From: Davit Dolmazyan Date: Mon, 16 Sep 2024 22:42:16 +0300 Subject: [PATCH 001/116] Refactor configurations - Migrate Call and Chat Activities to Kotlin - Refactor CallConfiguration - Refactor EngagementConfiguration MOB 3587 --- .../com/glia/widgets/call/CallActivity.java | 197 --------------- .../com/glia/widgets/call/CallActivity.kt | 162 ++++++++++++ .../widgets/call/CallActivityIntentHelper.kt | 25 +- .../glia/widgets/call/CallConfiguration.kt | 47 +--- .../com/glia/widgets/chat/ChatActivity.java | 238 ------------------ .../com/glia/widgets/chat/ChatActivity.kt | 185 ++++++++++++++ .../java/com/glia/widgets/chat/ChatView.kt | 2 +- .../configuration/EngagementConfiguration.kt | 137 ++-------- .../GliaSdkConfigurationManager.java | 17 +- .../glia/widgets/helper/ContextExtensions.kt | 26 +- .../helper/IntentConfigurationHelper.kt | 12 +- .../messagecenter/MessageCenterActivity.kt | 6 +- .../view/head/ActivityWatcherForChatHead.kt | 13 +- .../glia/widgets/view/head/ChatHeadView.kt | 15 +- 14 files changed, 440 insertions(+), 642 deletions(-) delete mode 100644 widgetssdk/src/main/java/com/glia/widgets/call/CallActivity.java create mode 100644 widgetssdk/src/main/java/com/glia/widgets/call/CallActivity.kt delete mode 100644 widgetssdk/src/main/java/com/glia/widgets/chat/ChatActivity.java create mode 100644 widgetssdk/src/main/java/com/glia/widgets/chat/ChatActivity.kt 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}. - *

- * Code example: - *

- * Intent intent = new Intent(requireContext(), CallActivity.class);
- * intent.putExtra(GliaWidgets.QUEUE_IDS, new ArrayList<>(List.of("AUDIO_QUEUE_ID")));
- * intent.putExtra(GliaWidgets.MEDIA_TYPE, Engagement.MediaType.VIDEO);
- * startActivity(intent);
- * 
- */ -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..4b049cb1c --- /dev/null +++ b/widgetssdk/src/main/java/com/glia/widgets/call/CallActivity.kt @@ -0,0 +1,162 @@ +package com.glia.widgets.call + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import com.glia.widgets.GliaWidgets +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.ChatActivity +import com.glia.widgets.di.Dependencies +import com.glia.widgets.helper.Logger +import com.glia.widgets.helper.TAG +import com.glia.widgets.locale.LocaleString +import com.glia.widgets.webbrowser.WebBrowserActivity.Companion.intent +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. + * + * + * Data that can be passed together with the Activity intent: + * - [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 [GliaWidgets]. + * + * + * Code example: + *
+ * Intent intent = new Intent(requireContext(), CallActivity.class);
+ * intent.putExtra(GliaWidgets.QUEUE_IDS, new ArrayList<>(List.of("AUDIO_QUEUE_ID")));
+ * intent.putExtra(GliaWidgets.MEDIA_TYPE, Engagement.MediaType.VIDEO);
+ * startActivity(intent);
+
* + */ +class CallActivity : FadeTransitionActivity() { + private var callConfiguration: CallConfiguration by Delegates.notNull() + 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) + + // Legacy company name support + Dependencies.sdkConfigurationManager.setLegacyCompanyName(intent.getStringExtra(GliaWidgets.COMPANY_NAME)) + + callConfiguration = CallActivityIntentHelper.readConfiguration(this) + if (this.intent.hasExtra(GliaWidgets.USE_OVERLAY)) { + // Integrator has passed a deprecated GliaWidgets.USE_OVERLAY parameter with Intent + // Override bubble configuration with USE_OVERLAY value + val useOverlay = this.intent.getBooleanExtra(GliaWidgets.USE_OVERLAY, true) + Dependencies.sdkConfigurationManager.setLegacyUseOverlay(useOverlay) + } + + if (!callView.shouldShowMediaEngagementView(callConfiguration.isUpgradeToCall)) { + finishAndRemoveTask() + return + } + + callView.setOnTitleUpdatedListener(this::setTitle) + callView.setEngagementConfiguration(callConfiguration.engagementConfiguration) + callView.setUiTheme(callConfiguration.engagementConfiguration?.runTimeTheme) + onBackClickedListener?.also(callView::setOnBackClickedListener) + + // 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() } + onNavigateToChatListener?.also(callView::setOnNavigateToChatListener) + callView.setOnNavigateToWebBrowserListener(onNavigateToWebBrowserListener) + + if (savedInstanceState == null) { + startCall() + } + } + + 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() { + val engagementConfiguration = callConfiguration.engagementConfiguration!! + callView.startCall( + engagementConfiguration.companyName!!, + engagementConfiguration.queueIds, + engagementConfiguration.contextAssetId, + engagementConfiguration.screenSharingMode!!, + callConfiguration.isUpgradeToCall, + callConfiguration.mediaType + ) + } + + private fun navigateToChat() { + Logger.d(TAG, "navigateToChat") + val engagementConfiguration = callConfiguration.engagementConfiguration + val queueIds = engagementConfiguration?.queueIds?.let { ArrayList(it) } + val newIntent = Intent(applicationContext, ChatActivity::class.java) + .putExtra(GliaWidgets.QUEUE_IDS, queueIds) + .putExtra(GliaWidgets.CONTEXT_ASSET_ID, engagementConfiguration?.contextAssetId) + .putExtra(GliaWidgets.UI_THEME, engagementConfiguration?.runTimeTheme) + .putExtra(GliaWidgets.SCREEN_SHARING_MODE, engagementConfiguration?.screenSharingMode) + startActivity(newIntent) + } + + private fun navigateToWebBrowser(title: LocaleString, url: String) { + val newIntent = intent(this, title, url) + startActivity(newIntent) + } + + internal companion object { + + /** + * Creates and fills out Intent for starting CallActivity + * @param context - Context object + * @param callConfiguration - CallActivity configuration + * @return - Intent for Starting CallActivity + */ + fun getIntent(context: Context, callConfiguration: CallConfiguration): Intent { + return CallActivityIntentHelper.createIntent(context, callConfiguration) + } + } +} diff --git a/widgetssdk/src/main/java/com/glia/widgets/call/CallActivityIntentHelper.kt b/widgetssdk/src/main/java/com/glia/widgets/call/CallActivityIntentHelper.kt index 1125d8e81..e3bce3eab 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/call/CallActivityIntentHelper.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/call/CallActivityIntentHelper.kt @@ -6,33 +6,28 @@ 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 +import com.glia.widgets.helper.getSerializableExtraCompat internal object CallActivityIntentHelper { - @JvmStatic fun createIntent(context: Context, callConfiguration: CallConfiguration): Intent { - val sdkConfiguration = callConfiguration.engagementConfiguration ?: throw NullPointerException("WidgetsSdk Configuration can't be null") + val engagementConfiguration = + 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.QUEUE_IDS, engagementConfiguration.queueIds?.let(::ArrayList)) + .putExtra(GliaWidgets.CONTEXT_ASSET_ID, engagementConfiguration.contextAssetId) + .putExtra(GliaWidgets.UI_THEME, engagementConfiguration.runTimeTheme) + .putExtra(GliaWidgets.SCREEN_SHARING_MODE, engagementConfiguration.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 engagementConfiguration = EngagementConfiguration(intent) + val mediaType = intent.getSerializableExtraCompat(GliaWidgets.MEDIA_TYPE) val isUpgradeToCall = intent.getBooleanExtra(GliaWidgets.IS_UPGRADE_TO_CALL, false) - return CallConfiguration.Builder() - .setEngagementConfiguration(engagementConfiguration) - .setMediaType(mediaType) - .setIsUpgradeToCall(isUpgradeToCall) - .build() + return CallConfiguration(engagementConfiguration, mediaType, isUpgradeToCall) } } diff --git a/widgetssdk/src/main/java/com/glia/widgets/call/CallConfiguration.kt b/widgetssdk/src/main/java/com/glia/widgets/call/CallConfiguration.kt index df8052301..176ae6672 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/call/CallConfiguration.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/call/CallConfiguration.kt @@ -2,49 +2,12 @@ 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) { +internal data class CallConfiguration @JvmOverloads constructor( @JvmField - val engagementConfiguration: EngagementConfiguration? + val engagementConfiguration: EngagementConfiguration?, @JvmField - val mediaType: Engagement.MediaType? + val mediaType: Engagement.MediaType? = null, @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) - } - } -} + val isUpgradeToCall: Boolean = false +) 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..a7904616f
--- /dev/null
+++ b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatActivity.kt
@@ -0,0 +1,185 @@
+package com.glia.widgets.chat
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.os.Parcelable
+import androidx.activity.OnBackPressedCallback
+import androidx.activity.enableEdgeToEdge
+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.sdkConfigurationManager
+import com.glia.widgets.helper.Logger
+import com.glia.widgets.helper.TAG
+import com.glia.widgets.helper.Utils
+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.
+ *
+ *
+ * Data that can be passed together with the Activity intent:
+ * - [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 [GliaWidgets].
+ *
+ *
+ * Code example:
+ * 
+ * Intent intent = new Intent(requireContext(), ChatActivity.class);
+ * intent.putExtra(GliaWidgets.QUEUE_IDS, new ArrayList<>(List.of("CHAT_QUEUE_ID")));
+ * startActivity(intent);
+ * 

+
*/ +class ChatActivity : FadeTransitionActivity() { + private var chatView: ChatView by Delegates.notNull() + private var engagementConfiguration: EngagementConfiguration by Delegates.notNull() + + private val defaultCallConfiguration: CallConfiguration + get() = CallConfiguration(engagementConfiguration) + + 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() + } + }) + + // Legacy company name support + sdkConfigurationManager.setLegacyCompanyName(intent.getStringExtra(GliaWidgets.COMPANY_NAME)) + + chatView.setOnTitleUpdatedListener(this::setTitle) + engagementConfiguration = createEngagementConfiguration(intent) + if (intent.hasExtra(GliaWidgets.USE_OVERLAY)) { + // Integrator has passed a deprecated GliaWidgets.USE_OVERLAY parameter with Intent + // Override bubble configuration with USE_OVERLAY value + val useOverlay = intent.getBooleanExtra(GliaWidgets.USE_OVERLAY, true) + sdkConfigurationManager.setLegacyUseOverlay(useOverlay) + } + + if (!chatView.shouldShow()) { + finishAndRemoveTask() + return + } + + chatView.setConfiguration(engagementConfiguration) + chatView.setUiTheme(engagementConfiguration.runTimeTheme) + 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) + chatView.setOnNavigateToCallListener(this::startCallScreen) + chatView.startChat( + engagementConfiguration.companyName, + engagementConfiguration.queueIds, + engagementConfiguration.contextAssetId, + engagementConfiguration.screenSharingMode, + engagementConfiguration.chatType ?: ChatType.LIVE_CHAT + ) + } + + 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 createEngagementConfiguration(intent: Intent): EngagementConfiguration = EngagementConfiguration(intent) + + //TODO: Check why theme attribute is not anymore used + private fun startCallScreen(theme: UiTheme, mediaType: String) { + startActivity( + CallActivity.getIntent( + applicationContext, + defaultCallConfiguration.copy(mediaType = Utils.toMediaType(mediaType), isUpgradeToCall = true) + ) + ) + finish() + } + + private fun backToCallScreen() { + startActivity(CallActivity.getIntent(applicationContext, defaultCallConfiguration)) + finish() + } + + /** + * Just commenting to avoid lint error, this should be removed in next tasks + */ + companion object { + /** + * 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 + */ + fun getIntent( + context: Context, + contextId: String?, + queueIds: List + ): Intent { + return Intent(context, ChatActivity::class.java) + .putExtra(GliaWidgets.CONTEXT_ASSET_ID, contextId) + .putStringArrayListExtra(GliaWidgets.QUEUE_IDS, ArrayList(queueIds)) + } + + /** + * 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 + */ + fun getIntent( + context: Context, + contextId: String?, + queueIds: List, + chatType: ChatType? + ): Intent { + return Intent(context, ChatActivity::class.java) + .putExtra(GliaWidgets.CONTEXT_ASSET_ID, contextId) + .putExtra(GliaWidgets.QUEUE_IDS, ArrayList(queueIds)) + .putExtra(GliaWidgets.COMPANY_NAME, "Legacy company") + .putExtra(GliaWidgets.CHAT_TYPE, chatType as Parcelable?) + } + } +} 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..a19a4cef9 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/chat/ChatView.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatView.kt @@ -912,7 +912,7 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In * @param theme Used to pass the finalized [UiTheme] * to the activity which is being navigated to. */ - fun call(theme: UiTheme?, mediaType: String?) + fun call(theme: UiTheme, mediaType: String) } fun interface OnBackToCallListener { 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 index e797c6451..63d08031c 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/core/configuration/EngagementConfiguration.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/core/configuration/EngagementConfiguration.kt @@ -6,121 +6,30 @@ 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 +import com.glia.widgets.helper.getParcelableExtraCompat +import com.glia.widgets.helper.getSerializableExtraCompat + +internal data class EngagementConfiguration( + val companyName: String? = null, + val queueIds: List? = null, + val contextAssetId: String? = null, + val chatType: ChatType? = null, + private val _runTimeTheme: UiTheme? = null, + private val _screenSharingMode: ScreenSharing.Mode? = null +) { + constructor(intent: Intent) : this( + companyName = Dependencies.sdkConfigurationManager.companyName, + queueIds = intent.getStringArrayListExtra(GliaWidgets.QUEUE_IDS) ?: intent.getStringExtra(GliaWidgets.QUEUE_ID)?.let(::listOf), + contextAssetId = intent.getStringExtra(GliaWidgets.CONTEXT_ASSET_ID), + chatType = intent.getParcelableExtraCompat(GliaWidgets.CHAT_TYPE), + _runTimeTheme = intent.getParcelableExtraCompat(GliaWidgets.UI_THEME) ?: Dependencies.sdkConfigurationManager.uiTheme, + _screenSharingMode = intent.getSerializableExtraCompat(GliaWidgets.SCREEN_SHARING_MODE) + ?: Dependencies.sdkConfigurationManager.screenSharingMode + ) -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 - } + get() = _runTimeTheme ?: Dependencies.sdkConfigurationManager.uiTheme - 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 - } + val screenSharingMode: ScreenSharing.Mode? + get() = _screenSharingMode ?: Dependencies.sdkConfigurationManager.screenSharingMode } 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 index 766791e4d..c304e93b9 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/core/configuration/GliaSdkConfigurationManager.java +++ b/widgetssdk/src/main/java/com/glia/widgets/core/configuration/GliaSdkConfigurationManager.java @@ -67,8 +67,8 @@ public void setLegacyCompanyName(String companyName) { public String getCompanyName() { if (companyName == null && legacyCompanyName != null) { - // Legacy company name configuration method used before local default - companyName = legacyCompanyName; + // Legacy company name configuration method used before local default + companyName = legacyCompanyName; } return companyName; } @@ -100,10 +100,13 @@ public void setScreenSharingMode(ScreenSharing.Mode screenSharingMode) { @Nullable public EngagementConfiguration buildEngagementConfiguration() { - return new EngagementConfiguration.Builder() - .companyName(companyName) - .screenSharingMode(screenSharingMode) - .runTimeTheme(uiTheme) - .build(); + return new EngagementConfiguration( + companyName, + null, + null, + null, + uiTheme, + screenSharingMode + ); } } 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..51dc4631b 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/helper/ContextExtensions.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/helper/ContextExtensions.kt @@ -3,7 +3,11 @@ 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 @@ -12,11 +16,13 @@ import androidx.annotation.IntRange import androidx.annotation.StyleRes import androidx.appcompat.app.AlertDialog 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 +31,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) @@ -56,7 +62,7 @@ internal val Activity.rootView: View internal fun Activity.withRuntimeTheme(callback: (themedContext: Context, uiTheme: UiTheme) -> Unit) { val themedContext = wrapWithMaterialThemeOverlay() - intent.getParcelableExtra(GliaWidgets.UI_THEME)?.also { + intent.getParcelableExtraCompat(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) @@ -70,3 +76,19 @@ internal val Activity.isGlia: Boolean get() = qualifiedName.startsWith(BuildConfig.LIBRARY_PACKAGE_NAME + ".") internal val AlertDialog.parentActivity: Activity? get() = context.asActivity() + +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 + } diff --git a/widgetssdk/src/main/java/com/glia/widgets/helper/IntentConfigurationHelper.kt b/widgetssdk/src/main/java/com/glia/widgets/helper/IntentConfigurationHelper.kt index a1b6ef1a2..fc254f675 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/helper/IntentConfigurationHelper.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/helper/IntentConfigurationHelper.kt @@ -16,15 +16,15 @@ internal interface IntentConfigurationHelper { } internal class IntentConfigurationHelperImpl : IntentConfigurationHelper { - private val defaultBuilder: CallConfiguration.Builder + private val defaultConfiguration: CallConfiguration get() = Dependencies.sdkConfigurationManager .buildEngagementConfiguration() - .let(CallConfiguration.Builder()::setEngagementConfiguration) + .let(::CallConfiguration) - override fun createForCall(context: Context, mediaType: MediaType, upgradeToCall: Boolean): Intent = defaultBuilder - .setMediaType(mediaType) - .setIsUpgradeToCall(upgradeToCall) - .run { CallActivity.getIntent(context, build()) } + override fun createForCall(context: Context, mediaType: MediaType, upgradeToCall: Boolean): Intent = CallActivity.getIntent( + context, + defaultConfiguration.copy(mediaType = mediaType, isUpgradeToCall = upgradeToCall) + ) 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/messagecenter/MessageCenterActivity.kt b/widgetssdk/src/main/java/com/glia/widgets/messagecenter/MessageCenterActivity.kt index 78f5a013c..dc3c2d60a 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/messagecenter/MessageCenterActivity.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/messagecenter/MessageCenterActivity.kt @@ -137,9 +137,5 @@ class MessageCenterActivity : finish() } - private fun createConfiguration(intent: Intent): EngagementConfiguration { - return EngagementConfiguration.Builder() - .intent(intent) - .build() - } + private fun createConfiguration(intent: Intent): EngagementConfiguration = EngagementConfiguration(intent) } diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/head/ActivityWatcherForChatHead.kt b/widgetssdk/src/main/java/com/glia/widgets/view/head/ActivityWatcherForChatHead.kt index a66dac9d4..f32b7f8bf 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/view/head/ActivityWatcherForChatHead.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/view/head/ActivityWatcherForChatHead.kt @@ -69,7 +69,7 @@ internal class ActivityWatcherForChatHead( } private fun createChatHeadLayout(activity: Activity) { - val chatHeadLayout = ChatHeadLayout(activity.baseContext) + val chatHeadLayout = ChatHeadLayout(activity) chatHeadLayout.layoutParams = ConstraintLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT @@ -173,17 +173,16 @@ internal class ActivityWatcherForChatHead( } } - private fun geCallConfigurationBuilder(): CallConfiguration.Builder { - val configuration = Dependencies.sdkConfigurationManager.buildEngagementConfiguration() - return CallConfiguration.Builder().setEngagementConfiguration(configuration) - } + private fun getDefaultCallConfiguration(): CallConfiguration = Dependencies.sdkConfigurationManager + .buildEngagementConfiguration() + .let(::CallConfiguration) private fun navigateToChat(activity: Activity?) { activity?.let { val intent = ChatActivity.getIntent( it, null, // No need to set contextId because engagement is already ongoing - null // No need to set queueId because engagement is already ongoing + emptyList() // No need to set queueId because engagement is already ongoing ) it.startActivity(intent) } @@ -199,7 +198,7 @@ internal class ActivityWatcherForChatHead( activity?.let { val intent = CallActivity.getIntent( it, - geCallConfigurationBuilder().setIsUpgradeToCall(true).build() + getDefaultCallConfiguration().copy(isUpgradeToCall = true) ) it.startActivity(intent) } diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/head/ChatHeadView.kt b/widgetssdk/src/main/java/com/glia/widgets/view/head/ChatHeadView.kt index 9b4d96e33..36137c0e5 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/view/head/ChatHeadView.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/view/head/ChatHeadView.kt @@ -92,14 +92,14 @@ internal class ChatHeadView @JvmOverloads constructor( serviceChatHeadController.setBuildTimeTheme(theme) } - override fun showUnreadMessageCount(unreadMessageCount: Int) { + override fun showUnreadMessageCount(count: Int) { post { if (isCallVisualizerUseCase.invoke()) { binding.chatBubbleBadge.isVisible = false } else { binding.chatBubbleBadge.apply { - text = unreadMessageCount.toString() - isVisible = isDisplayUnreadMessageBadge(unreadMessageCount) + text = count.toString() + isVisible = isDisplayUnreadMessageBadge(count) } } } @@ -109,12 +109,12 @@ internal class ChatHeadView @JvmOverloads constructor( // Unused } - override fun showOperatorImage(operatorProfileImgUrl: String) { + override fun showOperatorImage(operatorImgUrl: String) { post { binding.apply { queueingLottieAnimation.visibility = GONE placeholderView.visibility = GONE - profilePictureView.load(operatorProfileImgUrl) + profilePictureView.load(operatorImgUrl) } } } @@ -195,8 +195,7 @@ internal class ChatHeadView @JvmOverloads constructor( } override fun navigateToCall() { - val activityConfig = - CallConfiguration.Builder().setEngagementConfiguration(engagementConfiguration).build() + val activityConfig = CallConfiguration(engagementConfiguration) val intent = CallActivity.getIntent(context, activityConfig) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK @@ -225,7 +224,7 @@ internal class ChatHeadView @JvmOverloads constructor( private fun createHybridConfiguration( buildTimeTheme: UiTheme, - engagementConfiguration: com.glia.widgets.core.configuration.EngagementConfiguration? + engagementConfiguration: EngagementConfiguration? ) { configuration = createBuildTimeConfiguration(buildTimeTheme) val runTimeTheme = engagementConfiguration?.runTimeTheme ?: return From 1d63c0d819b5c701448dccc3964e5752303b5a4d Mon Sep 17 00:00:00 2001 From: Davit Dolmazyan Date: Wed, 18 Sep 2024 09:52:51 +0300 Subject: [PATCH 002/116] Separate Intent-related functionality - Moved all the Intent-related functionality to the IntentHelper class. - Abstraction will let us to easily mock this layer. MOB 3582 --- .../java/com/glia/exampleapp/MainFragment.kt | 13 +- .../com/glia/widgets/call/CallActivity.kt | 35 +--- .../call/CallActivityConfigurationHelper.kt | 18 ++ .../widgets/call/CallActivityIntentHelper.kt | 33 ---- .../java/com/glia/widgets/call/CallView.kt | 5 +- .../CallVisualizerActivityWatcher.kt | 9 +- .../com/glia/widgets/chat/ChatActivity.kt | 55 +----- .../java/com/glia/widgets/chat/ChatView.kt | 64 ++----- .../GliaSdkConfigurationManager.java | 4 +- .../widgets/core/notification/Extensions.kt | 16 -- .../java/com/glia/widgets/di/Dependencies.kt | 27 ++- .../filepreview/ui/FilePreviewActivity.kt | 28 +-- .../glia/widgets/helper/ContextExtensions.kt | 10 + .../helper/IntentConfigurationHelper.kt | 32 ---- .../com/glia/widgets/helper/IntentHelper.kt | 173 ++++++++++++++++++ .../OperatorRequestActivityWatcher.kt | 8 +- .../view/head/ActivityWatcherForChatHead.kt | 17 +- .../glia/widgets/view/head/ChatHeadView.kt | 29 +-- .../widgets/webbrowser/WebBrowserActivity.kt | 17 +- .../CallVisualizerActivityWatcherTest.kt | 9 +- .../OperatorRequestActivityWatcherTest.kt | 34 ++-- 21 files changed, 312 insertions(+), 324 deletions(-) create mode 100644 widgetssdk/src/main/java/com/glia/widgets/call/CallActivityConfigurationHelper.kt delete mode 100644 widgetssdk/src/main/java/com/glia/widgets/call/CallActivityIntentHelper.kt delete mode 100644 widgetssdk/src/main/java/com/glia/widgets/helper/IntentConfigurationHelper.kt create mode 100644 widgetssdk/src/main/java/com/glia/widgets/helper/IntentHelper.kt diff --git a/app/src/main/java/com/glia/exampleapp/MainFragment.kt b/app/src/main/java/com/glia/exampleapp/MainFragment.kt index dd6d69d34..61c23a638 100644 --- a/app/src/main/java/com/glia/exampleapp/MainFragment.kt +++ b/app/src/main/java/com/glia/exampleapp/MainFragment.kt @@ -4,6 +4,7 @@ import android.content.DialogInterface import android.content.Intent import android.content.SharedPreferences import android.os.Bundle +import android.os.Parcelable import android.text.InputType import android.util.TypedValue import android.view.Gravity @@ -274,12 +275,12 @@ class MainFragment : Fragment() { private fun navigateToChat(chatType: ChatType) { val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) - val intent = ChatActivity.getIntent( - requireContext(), - getContextAssetIdFromPrefs(sharedPreferences), - getQueueIdsFromPrefs(sharedPreferences), - chatType - ) + val intent = Intent(requireContext(), ChatActivity::class.java).apply { + putExtra(GliaWidgets.CHAT_TYPE, chatType as Parcelable) + putExtra(GliaWidgets.CONTEXT_ASSET_ID, getContextAssetIdFromPrefs(sharedPreferences)) + putExtra(GliaWidgets.QUEUE_IDS, ArrayList(getQueueIdsFromPrefs(sharedPreferences))) + setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) + } startActivity(intent) } diff --git a/widgetssdk/src/main/java/com/glia/widgets/call/CallActivity.kt b/widgetssdk/src/main/java/com/glia/widgets/call/CallActivity.kt index 4b049cb1c..9697d02ed 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/call/CallActivity.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/call/CallActivity.kt @@ -1,19 +1,16 @@ package com.glia.widgets.call -import android.content.Context -import android.content.Intent import android.os.Bundle import com.glia.widgets.GliaWidgets 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.ChatActivity import com.glia.widgets.di.Dependencies +import com.glia.widgets.helper.IntentHelper import com.glia.widgets.helper.Logger import com.glia.widgets.helper.TAG import com.glia.widgets.locale.LocaleString -import com.glia.widgets.webbrowser.WebBrowserActivity.Companion.intent import kotlin.properties.Delegates /** @@ -44,6 +41,7 @@ import kotlin.properties.Delegates
* */ class CallActivity : FadeTransitionActivity() { + private val intentHelper: IntentHelper by lazy { Dependencies.intentHelper } private var callConfiguration: CallConfiguration by Delegates.notNull() private var callView: CallView by Delegates.notNull() @@ -63,7 +61,7 @@ class CallActivity : FadeTransitionActivity() { // Legacy company name support Dependencies.sdkConfigurationManager.setLegacyCompanyName(intent.getStringExtra(GliaWidgets.COMPANY_NAME)) - callConfiguration = CallActivityIntentHelper.readConfiguration(this) + callConfiguration = CallActivityConfigurationHelper.readConfiguration(this) if (this.intent.hasExtra(GliaWidgets.USE_OVERLAY)) { // Integrator has passed a deprecated GliaWidgets.USE_OVERLAY parameter with Intent // Override bubble configuration with USE_OVERLAY value @@ -81,9 +79,9 @@ class CallActivity : FadeTransitionActivity() { callView.setUiTheme(callConfiguration.engagementConfiguration?.runTimeTheme) onBackClickedListener?.also(callView::setOnBackClickedListener) - // In case the engagement ends, Activity is removed from the device's Recents menu + // 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 Recents menu and the app's backstack was empty. + // the app from the Recent menu and the app's backstack was empty. callView.setOnEndListener { this.finishAndRemoveTask() } callView.setOnMinimizeListener { this.finish() } @@ -132,31 +130,12 @@ class CallActivity : FadeTransitionActivity() { private fun navigateToChat() { Logger.d(TAG, "navigateToChat") - val engagementConfiguration = callConfiguration.engagementConfiguration - val queueIds = engagementConfiguration?.queueIds?.let { ArrayList(it) } - val newIntent = Intent(applicationContext, ChatActivity::class.java) - .putExtra(GliaWidgets.QUEUE_IDS, queueIds) - .putExtra(GliaWidgets.CONTEXT_ASSET_ID, engagementConfiguration?.contextAssetId) - .putExtra(GliaWidgets.UI_THEME, engagementConfiguration?.runTimeTheme) - .putExtra(GliaWidgets.SCREEN_SHARING_MODE, engagementConfiguration?.screenSharingMode) + val newIntent = intentHelper.chatIntent(this, callConfiguration.engagementConfiguration ?: return) startActivity(newIntent) } private fun navigateToWebBrowser(title: LocaleString, url: String) { - val newIntent = intent(this, title, url) + val newIntent = intentHelper.webBrowserIntent(this, title, url) startActivity(newIntent) } - - internal companion object { - - /** - * Creates and fills out Intent for starting CallActivity - * @param context - Context object - * @param callConfiguration - CallActivity configuration - * @return - Intent for Starting CallActivity - */ - fun getIntent(context: Context, callConfiguration: CallConfiguration): Intent { - return CallActivityIntentHelper.createIntent(context, callConfiguration) - } - } } diff --git a/widgetssdk/src/main/java/com/glia/widgets/call/CallActivityConfigurationHelper.kt b/widgetssdk/src/main/java/com/glia/widgets/call/CallActivityConfigurationHelper.kt new file mode 100644 index 000000000..4e25aecc9 --- /dev/null +++ b/widgetssdk/src/main/java/com/glia/widgets/call/CallActivityConfigurationHelper.kt @@ -0,0 +1,18 @@ +package com.glia.widgets.call + +import androidx.appcompat.app.AppCompatActivity +import com.glia.androidsdk.Engagement +import com.glia.widgets.GliaWidgets +import com.glia.widgets.core.configuration.EngagementConfiguration +import com.glia.widgets.helper.getSerializableExtraCompat + +internal object CallActivityConfigurationHelper { + + fun readConfiguration(activity: AppCompatActivity): CallConfiguration { + val intent = activity.intent + val engagementConfiguration = EngagementConfiguration(intent) + val mediaType = intent.getSerializableExtraCompat(GliaWidgets.MEDIA_TYPE) + val isUpgradeToCall = intent.getBooleanExtra(GliaWidgets.IS_UPGRADE_TO_CALL, false) + return CallConfiguration(engagementConfiguration = engagementConfiguration, mediaType = mediaType, isUpgradeToCall = isUpgradeToCall) + } +} 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 e3bce3eab..000000000 --- a/widgetssdk/src/main/java/com/glia/widgets/call/CallActivityIntentHelper.kt +++ /dev/null @@ -1,33 +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 com.glia.widgets.helper.getSerializableExtraCompat - -internal object CallActivityIntentHelper { - - fun createIntent(context: Context, callConfiguration: CallConfiguration): Intent { - val engagementConfiguration = - callConfiguration.engagementConfiguration ?: throw NullPointerException("WidgetsSdk Configuration can't be null") - - return Intent(context, CallActivity::class.java) - .putExtra(GliaWidgets.QUEUE_IDS, engagementConfiguration.queueIds?.let(::ArrayList)) - .putExtra(GliaWidgets.CONTEXT_ASSET_ID, engagementConfiguration.contextAssetId) - .putExtra(GliaWidgets.UI_THEME, engagementConfiguration.runTimeTheme) - .putExtra(GliaWidgets.SCREEN_SHARING_MODE, engagementConfiguration.screenSharingMode) - .putExtra(GliaWidgets.MEDIA_TYPE, callConfiguration.mediaType) - .putExtra(GliaWidgets.IS_UPGRADE_TO_CALL, callConfiguration.isUpgradeToCall) - } - - fun readConfiguration(activity: AppCompatActivity): CallConfiguration { - val intent = activity.intent - val engagementConfiguration = EngagementConfiguration(intent) - val mediaType = intent.getSerializableExtraCompat(GliaWidgets.MEDIA_TYPE) - val isUpgradeToCall = intent.getBooleanExtra(GliaWidgets.IS_UPGRADE_TO_CALL, false) - return CallConfiguration(engagementConfiguration, mediaType, isUpgradeToCall) - } -} 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..1afb77fe9 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/call/CallView.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/call/CallView.kt @@ -34,6 +34,7 @@ import com.glia.widgets.core.dialog.model.DialogState import com.glia.widgets.databinding.CallButtonsLayoutBinding import com.glia.widgets.databinding.CallViewBinding import com.glia.widgets.di.Dependencies +import com.glia.widgets.helper.IntentHelper import com.glia.widgets.helper.Logger import com.glia.widgets.helper.SimpleWindowInsetsAndAnimationHandler import com.glia.widgets.helper.TAG @@ -86,6 +87,7 @@ internal class CallView( defStyleAttr, defStyleRes ), CallContract.View, DialogDelegate by DialogDelegateImpl() { + private val intentHelper: IntentHelper by lazy { Dependencies.intentHelper } private val callTheme: CallTheme? by lazy { Dependencies.gliaThemeManager.theme?.callTheme @@ -617,8 +619,7 @@ internal class CallView( positiveButtonClickListener = { resetDialogStateAndDismiss() callController?.overlayPermissionsDialogDismissed() - val overlayIntent = Dependencies.intentConfigurationHelper.createForOverlayPermissionScreen(context) - this.context.startActivity(overlayIntent) + this.context.startActivity(intentHelper.overlayPermissionIntent(context)) }, negativeButtonClickListener = { resetDialogStateAndDismiss() 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..3ed3b474b 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/callvisualizer/CallVisualizerActivityWatcher.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/callvisualizer/CallVisualizerActivityWatcher.kt @@ -2,16 +2,17 @@ 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 import com.glia.widgets.helper.DialogHolderActivity import com.glia.widgets.helper.GliaActivityManager +import com.glia.widgets.helper.IntentHelper import com.glia.widgets.helper.Logger import com.glia.widgets.helper.OneTimeEvent import com.glia.widgets.helper.TAG 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 intentHelper: IntentHelper ) : 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) + activity.startActivity(intentHelper.webBrowserIntent(activity, title, url)) } private fun showSnackBar(activity: Activity) { diff --git a/widgetssdk/src/main/java/com/glia/widgets/chat/ChatActivity.kt b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatActivity.kt index a7904616f..d6a606d9c 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/chat/ChatActivity.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatActivity.kt @@ -1,19 +1,18 @@ package com.glia.widgets.chat -import android.content.Context import android.content.Intent import android.os.Bundle -import android.os.Parcelable import androidx.activity.OnBackPressedCallback import androidx.activity.enableEdgeToEdge 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.di.Dependencies.sdkConfigurationManager +import com.glia.widgets.helper.IntentHelper import com.glia.widgets.helper.Logger import com.glia.widgets.helper.TAG import com.glia.widgets.helper.Utils @@ -47,6 +46,7 @@ import kotlin.properties.Delegates *

 
*/ class ChatActivity : FadeTransitionActivity() { + private val intentHelper: IntentHelper by lazy { Dependencies.intentHelper } private var chatView: ChatView by Delegates.notNull() private var engagementConfiguration: EngagementConfiguration by Delegates.notNull() @@ -125,7 +125,7 @@ class ChatActivity : FadeTransitionActivity() { //TODO: Check why theme attribute is not anymore used private fun startCallScreen(theme: UiTheme, mediaType: String) { startActivity( - CallActivity.getIntent( + intentHelper.callIntent( applicationContext, defaultCallConfiguration.copy(mediaType = Utils.toMediaType(mediaType), isUpgradeToCall = true) ) @@ -134,52 +134,7 @@ class ChatActivity : FadeTransitionActivity() { } private fun backToCallScreen() { - startActivity(CallActivity.getIntent(applicationContext, defaultCallConfiguration)) + startActivity(intentHelper.callIntent(applicationContext, defaultCallConfiguration)) finish() } - - /** - * Just commenting to avoid lint error, this should be removed in next tasks - */ - companion object { - /** - * 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 - */ - fun getIntent( - context: Context, - contextId: String?, - queueIds: List - ): Intent { - return Intent(context, ChatActivity::class.java) - .putExtra(GliaWidgets.CONTEXT_ASSET_ID, contextId) - .putStringArrayListExtra(GliaWidgets.QUEUE_IDS, ArrayList(queueIds)) - } - - /** - * 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 - */ - fun getIntent( - context: Context, - contextId: String?, - queueIds: List, - chatType: ChatType? - ): Intent { - return Intent(context, ChatActivity::class.java) - .putExtra(GliaWidgets.CONTEXT_ASSET_ID, contextId) - .putExtra(GliaWidgets.QUEUE_IDS, ArrayList(queueIds)) - .putExtra(GliaWidgets.COMPANY_NAME, "Legacy company") - .putExtra(GliaWidgets.CHAT_TYPE, chatType as Parcelable?) - } - } } 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 a19a4cef9..d95fd576d 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/chat/ChatView.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/chat/ChatView.kt @@ -1,11 +1,8 @@ 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 @@ -47,7 +44,7 @@ import com.glia.widgets.core.dialog.model.DialogState 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.helper.IntentHelper import com.glia.widgets.helper.Logger import com.glia.widgets.helper.SimpleTextWatcher import com.glia.widgets.helper.SimpleWindowInsetsAndAnimationHandler @@ -65,6 +62,7 @@ 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.safeStartActivity import com.glia.widgets.helper.setLocaleContentDescription import com.glia.widgets.helper.setLocaleHint import com.glia.widgets.locale.LocaleString @@ -81,7 +79,6 @@ 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.google.android.material.shape.MarkerEdgeTreatment import com.google.android.material.theme.overlay.MaterialThemeOverlay import java.util.concurrent.Executor @@ -90,11 +87,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 intentHelper: IntentHelper by lazy { Dependencies.intentHelper } private var controller: ChatContract.Controller? = null private var dialogCallback: DialogContract.Controller.Callback? = null @@ -201,6 +194,11 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In SimpleWindowInsetsAndAnimationHandler(this, appBarOrToolBar = binding.appBarView) } + @JvmOverloads + constructor( + context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = R.attr.gliaChatStyle + ) : this(context, attrs, defStyleAttr, R.style.Application_Glia_Chat) + /** * @param uiTheme sets this view's appearance using the parameters provided in the * [com.glia.widgets.UiTheme] @@ -409,7 +407,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()) + context.startActivity(intentHelper.filePreviewIntent(context, attachmentFile), options.toBundle()) insetsController?.hideKeyboard() } @@ -431,9 +429,7 @@ internal class ChatView(context: Context, attrs: AttributeSet?, defStyleAttr: In } override fun navigateToWebBrowserActivity(title: LocaleString, url: String) { - context.startActivity( - WebBrowserActivity.intent(context, title, url) - ) + context.startActivity(intentHelper.webBrowserIntent(context, title, url)) } override fun showEngagementConfirmationDialog() { @@ -462,36 +458,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 { + context.safeStartActivity(intentHelper.openEmailIntent(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 { + context.safeStartActivity(intentHelper.dialerIntent(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)) - } + context.safeStartActivity(intentHelper.openUriIntent(uri)) { + Logger.e(TAG, "No app to open url - $uri") + showToast(localeProvider.getString(R.string.error_general)) } } @@ -766,11 +749,7 @@ 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) + this.context.startActivity(intentHelper.overlayPermissionIntent(context)) }, negativeButtonClickListener = { controller?.overlayPermissionsDialogDismissed() resetDialogStateAndDismiss() @@ -794,14 +773,9 @@ 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)) + context.safeStartActivity(intentHelper.openFileIntent(getContentUriCompat(file.fileName, context), file.contentType)) { + showToast(message = localeProvider.getString(R.string.android_file_view_error)) + } } override fun onLocalFileOpenClick(attachment: LocalAttachment) { 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 index c304e93b9..288ec7394 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/core/configuration/GliaSdkConfigurationManager.java +++ b/widgetssdk/src/main/java/com/glia/widgets/core/configuration/GliaSdkConfigurationManager.java @@ -10,8 +10,6 @@ import com.glia.widgets.helper.Logger; import com.glia.widgets.helper.ResourceProvider; -import org.jetbrains.annotations.Nullable; - /** * @hide */ @@ -98,7 +96,7 @@ public void setScreenSharingMode(ScreenSharing.Mode screenSharingMode) { this.screenSharingMode = screenSharingMode; } - @Nullable + @NonNull public EngagementConfiguration buildEngagementConfiguration() { return new EngagementConfiguration( companyName, 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/di/Dependencies.kt b/widgetssdk/src/main/java/com/glia/widgets/di/Dependencies.kt index 2ecd9303b..4d89c6831 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/di/Dependencies.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/di/Dependencies.kt @@ -25,8 +25,8 @@ import com.glia.widgets.engagement.completion.EngagementCompletionActivityWatche 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.IntentHelper +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 @@ -43,19 +43,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 +67,13 @@ internal object Dependencies { @VisibleForTesting set var schedulers: Schedulers = GliaWidgetsSchedulers() @VisibleForTesting set + @JvmStatic var sdkConfigurationManager: GliaSdkConfigurationManager = GliaSdkConfigurationManager() @VisibleForTesting set - val intentConfigurationHelper: IntentConfigurationHelper = IntentConfigurationHelperImpl() + + val intentHelper: IntentHelper by lazy { IntentHelperImpl(sdkConfigurationManager) } + @JvmStatic lateinit var repositoryFactory: RepositoryFactory @VisibleForTesting set @@ -119,14 +126,16 @@ internal object Dependencies { controllerFactory.callVisualizerController, GliaActivityManagerImpl(), localeProvider, - gliaThemeManager + gliaThemeManager, + intentHelper ) application.registerActivityLifecycleCallbacks(callVisualizerActivityWatcher) val activityWatcherForChatHead = ActivityWatcherForChatHead( - controllerFactory.activityWatcherForChatHeadController - ) + controllerFactory.activityWatcherForChatHeadController, + intentHelper + ) application.registerActivityLifecycleCallbacks(activityWatcherForChatHead) val activityWatcherForLiveObservation = ActivityWatcherForLiveObservation( @@ -137,8 +146,8 @@ internal object Dependencies { application.registerActivityLifecycleCallbacks(activityWatcherForLiveObservation) val activityWatcherForPermissionsRequest = ActivityWatcherForPermissionsRequest( - controllerFactory.permissionsController - ) + controllerFactory.permissionsController + ) application.registerActivityLifecycleCallbacks(activityWatcherForPermissionsRequest) callVisualizerManager = CallVisualizerManager( @@ -154,7 +163,7 @@ internal object Dependencies { val operatorRequestActivityWatcher = OperatorRequestActivityWatcher( controllerFactory.operatorRequestController, - intentConfigurationHelper, + intentHelper, GliaActivityManagerImpl() ) application.registerActivityLifecycleCallbacks(operatorRequestActivityWatcher) diff --git a/widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/FilePreviewActivity.kt b/widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/FilePreviewActivity.kt index 650b0fa19..67c30b39b 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/FilePreviewActivity.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/filepreview/ui/FilePreviewActivity.kt @@ -19,11 +19,11 @@ 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.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 @@ -33,6 +33,8 @@ import com.glia.widgets.helper.setLocaleContentDescription import com.glia.widgets.helper.showToast import java.io.File +private const val WRITE_PERMISSION_REQUEST_CODE = 110011 + /** * Glia internal class. * @@ -81,8 +83,8 @@ internal class FilePreviewActivity : AppCompatActivity(), FilePreviewContract.Vi if (intent.hasExtra(LOCAL_IMAGE_URI)) { filePreviewController?.onLocalImageReceived(intent.getParcelable(LOCAL_IMAGE_URI) ?: return) } else { - val bitmapId = intent.getStringExtra(IMAGE_ID_KEY).orEmpty() - val bitmapName = intent.getStringExtra(IMAGE_ID_NAME).orEmpty() + val bitmapId = intent.getStringExtra(ExtraKeys.FILE_PREVIEW_IMAGE_ID).orEmpty() + val bitmapName = intent.getStringExtra(ExtraKeys.FILE_PREVIEW_IMAGE_NAME).orEmpty() filePreviewController?.onImageDataReceived(bitmapId, bitmapName) filePreviewController?.onImageRequested() } @@ -217,24 +219,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/helper/ContextExtensions.kt b/widgetssdk/src/main/java/com/glia/widgets/helper/ContextExtensions.kt index 51dc4631b..adcb10cdb 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/helper/ContextExtensions.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/helper/ContextExtensions.kt @@ -77,6 +77,16 @@ internal val Activity.isGlia: Boolean internal val AlertDialog.parentActivity: Activity? get() = context.asActivity() +internal fun Context.safeStartActivity(intent: Intent, onError: () -> Unit) { + if (intent.resolveActivity(packageManager) != null) { + startActivity(intent) + } else { + onError() + } +} + +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) 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 fc254f675..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 defaultConfiguration: CallConfiguration - get() = Dependencies.sdkConfigurationManager - .buildEngagementConfiguration() - .let(::CallConfiguration) - - override fun createForCall(context: Context, mediaType: MediaType, upgradeToCall: Boolean): Intent = CallActivity.getIntent( - context, - defaultConfiguration.copy(mediaType = mediaType, isUpgradeToCall = upgradeToCall) - ) - - 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..7f75a17ff --- /dev/null +++ b/widgetssdk/src/main/java/com/glia/widgets/helper/IntentHelper.kt @@ -0,0 +1,173 @@ +@file:Suppress("DEPRECATION") + +package com.glia.widgets.helper + +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.GliaWidgets +import com.glia.widgets.UiTheme +import com.glia.widgets.call.CallActivity +import com.glia.widgets.call.CallConfiguration +import com.glia.widgets.callvisualizer.EndScreenSharingActivity +import com.glia.widgets.chat.ChatActivity +import com.glia.widgets.chat.ChatType +import com.glia.widgets.core.configuration.EngagementConfiguration +import com.glia.widgets.core.configuration.GliaSdkConfigurationManager +import com.glia.widgets.filepreview.ui.FilePreviewActivity +import com.glia.widgets.locale.LocaleString +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 FILE_PREVIEW_IMAGE_ID = "file_preview_image_id" + const val FILE_PREVIEW_IMAGE_NAME = "file_preview_image_name" +} + +internal interface IntentHelper { + fun chatIntent(context: Context, queueIds: List, chatType: ChatType? = null, contextId: String? = null): Intent + + fun chatIntent(context: Context, engagementConfiguration: EngagementConfiguration): Intent + + fun secureConversationsChatIntent(context: Context): Intent + + fun callIntent(context: Context, mediaType: MediaType, upgradeToCall: Boolean = true): Intent + + fun callIntent(context: Context, callConfiguration: CallConfiguration): Intent + + fun filePreviewIntent(context: Context, attachment: AttachmentFile): Intent + + fun shareImageIntent(context: Context, fileName: String): Intent + + fun surveyIntent(context: Context, survey: Survey, uiTheme: UiTheme): 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 +} + +internal class IntentHelperImpl(private val configurationManager: GliaSdkConfigurationManager) : IntentHelper { + private val defaultEngagementConfiguration: EngagementConfiguration + get() = configurationManager.buildEngagementConfiguration() + + + override fun chatIntent(context: Context, queueIds: List, chatType: ChatType?, contextId: String?): Intent = + Intent(context, ChatActivity::class.java).apply { + (chatType as? Parcelable)?.also { putExtra(GliaWidgets.CHAT_TYPE, it) } + contextId?.also { putExtra(GliaWidgets.CONTEXT_ASSET_ID, it) } + putExtra(GliaWidgets.QUEUE_IDS, ArrayList(queueIds)) + setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) + setSafeFlags(context) + } + + override fun chatIntent(context: Context, engagementConfiguration: EngagementConfiguration): Intent = Intent(context, ChatActivity::class.java) + .putExtra(GliaWidgets.QUEUE_IDS, engagementConfiguration.queueIds?.let { ArrayList(it) }) + .putExtra(GliaWidgets.CONTEXT_ASSET_ID, engagementConfiguration.contextAssetId) + .putExtra(GliaWidgets.UI_THEME, engagementConfiguration.runTimeTheme) + .putExtra(GliaWidgets.SCREEN_SHARING_MODE, engagementConfiguration.screenSharingMode) + .setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) + .setSafeFlags(context) + + override fun secureConversationsChatIntent(context: Context): Intent = Intent(context, ChatActivity::class.java) + .putExtra(GliaWidgets.CHAT_TYPE, ChatType.SECURE_MESSAGING as Parcelable) + .setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) + .setSafeFlags(context) + + override fun callIntent(context: Context, mediaType: MediaType, upgradeToCall: Boolean): Intent = callIntent( + context, + CallConfiguration( + engagementConfiguration = defaultEngagementConfiguration, + mediaType = mediaType, + isUpgradeToCall = upgradeToCall + ) + ) + + override fun callIntent(context: Context, callConfiguration: CallConfiguration): Intent { + val engagementConfiguration = + callConfiguration.engagementConfiguration ?: throw NullPointerException("WidgetsSdk Configuration can't be null") + + return Intent(context, CallActivity::class.java) + .putExtra(GliaWidgets.QUEUE_IDS, engagementConfiguration.queueIds?.let(::ArrayList)) + .putExtra(GliaWidgets.CONTEXT_ASSET_ID, engagementConfiguration.contextAssetId) + .putExtra(GliaWidgets.UI_THEME, engagementConfiguration.runTimeTheme) + .putExtra(GliaWidgets.SCREEN_SHARING_MODE, engagementConfiguration.screenSharingMode) + .putExtra(GliaWidgets.MEDIA_TYPE, callConfiguration.mediaType) + .putExtra(GliaWidgets.IS_UPGRADE_TO_CALL, callConfiguration.isUpgradeToCall) + .setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) + .setSafeFlags(context) + } + + override fun filePreviewIntent(context: Context, attachment: AttachmentFile): Intent { + return Intent(context, FilePreviewActivity::class.java) + .putExtra(ExtraKeys.FILE_PREVIEW_IMAGE_ID, attachment.id) + .putExtra(ExtraKeys.FILE_PREVIEW_IMAGE_NAME, attachment.name) + .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, uiTheme: UiTheme): Intent = Intent(context, SurveyActivity::class.java) + .putExtra(GliaWidgets.UI_THEME, uiTheme) + .putExtra(GliaWidgets.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) + } +} diff --git a/widgetssdk/src/main/java/com/glia/widgets/operator/OperatorRequestActivityWatcher.kt b/widgetssdk/src/main/java/com/glia/widgets/operator/OperatorRequestActivityWatcher.kt index 734f94b81..24ba1ce29 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/operator/OperatorRequestActivityWatcher.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/operator/OperatorRequestActivityWatcher.kt @@ -13,7 +13,7 @@ import com.glia.androidsdk.Engagement import com.glia.widgets.base.BaseSingleActivityWatcher import com.glia.widgets.call.CallActivity import com.glia.widgets.helper.GliaActivityManager -import com.glia.widgets.helper.IntentConfigurationHelper +import com.glia.widgets.helper.IntentHelper import com.glia.widgets.helper.Logger import com.glia.widgets.helper.OneTimeEvent import com.glia.widgets.helper.isGlia @@ -27,7 +27,7 @@ private const val TAG = "RequestHandlerActivityWatcher" internal class OperatorRequestActivityWatcher( private val controller: OperatorRequestContract.Controller, - private val intentConfigurationHelper: IntentConfigurationHelper, + private val intentHelper: IntentHelper, gliaActivityManager: GliaActivityManager ) : BaseSingleActivityWatcher(gliaActivityManager) { @@ -82,7 +82,7 @@ internal class OperatorRequestActivityWatcher( } private fun openOverlayPermissionsScreen(activity: Activity) { - val overlayIntent = intentConfigurationHelper.createForOverlayPermissionScreen(activity) + val overlayIntent = intentHelper.overlayPermissionIntent(activity) if (overlayIntent.resolveActivity(activity.packageManager) != null) { activity.startActivity(overlayIntent) @@ -137,7 +137,7 @@ internal class OperatorRequestActivityWatcher( activity.isGlia -> finishActivities() } - activity.startActivity(intentConfigurationHelper.createForCall(activity, mediaType)) + activity.startActivity(intentHelper.callIntent(activity, mediaType)) } private fun showUpgradeDialog(state: ControllerState.RequestMediaUpgrade, activity: Activity, consumeCallback: () -> Unit) { diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/head/ActivityWatcherForChatHead.kt b/widgetssdk/src/main/java/com/glia/widgets/view/head/ActivityWatcherForChatHead.kt index f32b7f8bf..072022c66 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/view/head/ActivityWatcherForChatHead.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/view/head/ActivityWatcherForChatHead.kt @@ -2,7 +2,6 @@ package com.glia.widgets.view.head import android.annotation.SuppressLint import android.app.Activity -import android.content.Intent import android.util.Log import android.view.View import android.view.ViewGroup @@ -11,15 +10,13 @@ import androidx.core.util.Pair import androidx.core.view.contains import com.glia.widgets.R import com.glia.widgets.base.BaseActivityStackWatcher -import com.glia.widgets.call.CallActivity import com.glia.widgets.call.CallConfiguration -import com.glia.widgets.callvisualizer.EndScreenSharingActivity import com.glia.widgets.callvisualizer.EndScreenSharingView -import com.glia.widgets.chat.ChatActivity import com.glia.widgets.chat.ChatView import com.glia.widgets.di.Dependencies import com.glia.widgets.filepreview.ui.FilePreviewView import com.glia.widgets.helper.DialogHolderView +import com.glia.widgets.helper.IntentHelper import com.glia.widgets.helper.Logger import com.glia.widgets.helper.TAG import com.glia.widgets.helper.WeakReferenceDelegate @@ -29,7 +26,8 @@ import com.glia.widgets.view.head.controller.ActivityWatcherForChatHeadContract @SuppressLint("CheckResult") internal class ActivityWatcherForChatHead( - val controller: ActivityWatcherForChatHeadContract.Controller + val controller: ActivityWatcherForChatHeadContract.Controller, + private val intentHelper: IntentHelper ) : BaseActivityStackWatcher(), ActivityWatcherForChatHeadContract.Watcher { init { @@ -179,9 +177,8 @@ internal class ActivityWatcherForChatHead( private fun navigateToChat(activity: Activity?) { activity?.let { - val intent = ChatActivity.getIntent( + val intent = intentHelper.chatIntent( it, - null, // No need to set contextId because engagement is already ongoing emptyList() // No need to set queueId because engagement is already ongoing ) it.startActivity(intent) @@ -189,14 +186,12 @@ internal class ActivityWatcherForChatHead( } private fun navigateToEndScreenSharing(activity: Activity?) { - val intent = Intent(activity, EndScreenSharingActivity::class.java) - // No need to set contextId because engagement is already ongoing - activity?.startActivity(intent) + activity?.startActivity(intentHelper.endScreenSharingIntent(activity)) } private fun navigateToCall(activity: Activity?) { activity?.let { - val intent = CallActivity.getIntent( + val intent = intentHelper.callIntent( it, getDefaultCallConfiguration().copy(isUpgradeToCall = true) ) diff --git a/widgetssdk/src/main/java/com/glia/widgets/view/head/ChatHeadView.kt b/widgetssdk/src/main/java/com/glia/widgets/view/head/ChatHeadView.kt index 36137c0e5..e36407319 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/view/head/ChatHeadView.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/view/head/ChatHeadView.kt @@ -2,7 +2,6 @@ package com.glia.widgets.view.head import android.app.Service import android.content.Context -import android.content.Intent import android.content.res.TypedArray import android.graphics.PorterDuff import android.util.AttributeSet @@ -14,18 +13,15 @@ import androidx.core.view.AccessibilityDelegateCompat import androidx.core.view.ViewCompat import androidx.core.view.accessibility.AccessibilityNodeInfoCompat import androidx.core.view.isVisible -import com.glia.widgets.GliaWidgets import com.glia.widgets.R import com.glia.widgets.UiTheme -import com.glia.widgets.call.CallActivity import com.glia.widgets.call.CallConfiguration -import com.glia.widgets.callvisualizer.EndScreenSharingActivity -import com.glia.widgets.chat.ChatActivity import com.glia.widgets.core.callvisualizer.domain.IsCallVisualizerScreenSharingUseCase import com.glia.widgets.core.configuration.EngagementConfiguration import com.glia.widgets.databinding.ChatHeadViewBinding import com.glia.widgets.di.Dependencies import com.glia.widgets.engagement.domain.IsCurrentEngagementCallVisualizerUseCase +import com.glia.widgets.helper.IntentHelper import com.glia.widgets.helper.Utils import com.glia.widgets.helper.addColorFilter import com.glia.widgets.helper.getColorCompat @@ -53,6 +49,7 @@ internal class ChatHeadView @JvmOverloads constructor( defStyleAttr, defStyleRes ), ChatHeadContract.View { + private val intentHelper: IntentHelper by lazy { Dependencies.intentHelper } private val binding by lazy { ChatHeadViewBinding.inflate(layoutInflater, this) } private var engagementConfiguration: EngagementConfiguration? = null private var configuration: ChatHeadConfiguration by Delegates.notNull() @@ -94,7 +91,7 @@ internal class ChatHeadView @JvmOverloads constructor( override fun showUnreadMessageCount(count: Int) { post { - if (isCallVisualizerUseCase.invoke()) { + if (isCallVisualizerUseCase()) { binding.chatBubbleBadge.isVisible = false } else { binding.chatBubbleBadge.apply { @@ -190,21 +187,19 @@ internal class ChatHeadView @JvmOverloads constructor( override fun navigateToChat() { engagementConfiguration?.let { - context.startActivity(getNavigationIntent(context, ChatActivity::class.java, it)) + context.startActivity(intentHelper.chatIntent(context, it)) } } override fun navigateToCall() { val activityConfig = CallConfiguration(engagementConfiguration) - val intent = CallActivity.getIntent(context, activityConfig) - intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + val intent = intentHelper.callIntent(context, activityConfig) context.startActivity(intent) } override fun navigateToEndScreenSharing() { - val intent = Intent(context, EndScreenSharingActivity::class.java) - intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + val intent = intentHelper.endScreenSharingIntent(context) context.startActivity(intent) } @@ -324,17 +319,5 @@ internal class ChatHeadView @JvmOverloads constructor( companion object { @JvmStatic fun getInstance(context: Context): ChatHeadView = ChatHeadView(context) - - @JvmStatic - private fun getNavigationIntent( - context: Context, - cls: Class<*>, - engagementConfiguration: EngagementConfiguration - ): Intent = Intent(context, cls) - .putExtra(GliaWidgets.QUEUE_IDS, engagementConfiguration.queueIds?.let { ArrayList(it) }) - .putExtra(GliaWidgets.CONTEXT_ASSET_ID, engagementConfiguration.contextAssetId) - .putExtra(GliaWidgets.UI_THEME, engagementConfiguration.runTimeTheme) - .putExtra(GliaWidgets.SCREEN_SHARING_MODE, engagementConfiguration.screenSharingMode) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } } diff --git a/widgetssdk/src/main/java/com/glia/widgets/webbrowser/WebBrowserActivity.kt b/widgetssdk/src/main/java/com/glia/widgets/webbrowser/WebBrowserActivity.kt index 3ee454337..448d1db32 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/webbrowser/WebBrowserActivity.kt +++ b/widgetssdk/src/main/java/com/glia/widgets/webbrowser/WebBrowserActivity.kt @@ -1,11 +1,11 @@ package com.glia.widgets.webbrowser -import android.content.Context import android.content.Intent import android.net.Uri import android.os.Bundle import com.glia.widgets.base.FadeTransitionActivity import com.glia.widgets.databinding.WebBrowserAvtivityBinding +import com.glia.widgets.helper.ExtraKeys import com.glia.widgets.helper.getParcelable import com.glia.widgets.locale.LocaleString @@ -33,8 +33,8 @@ internal class WebBrowserActivity : super.onCreate(savedInstanceState) setContentView(binding.root) - val title = intent.getParcelable(TITLE_KEY) - val url = intent.getStringExtra(URL_KEY).orEmpty() + val title = intent.getParcelable(ExtraKeys.WEB_BROWSER_TITLE) + val url = intent.getStringExtra(ExtraKeys.WEB_BROWSER_URL).orEmpty() webBrowserView.onLinkClickListener = this webBrowserView.onFinishListener = this @@ -45,15 +45,4 @@ internal class WebBrowserActivity : override fun onLinkClick(url: Uri) { startActivity(Intent(Intent.ACTION_VIEW, url)) } - - companion object { - private const val TITLE_KEY = "title" - private const val URL_KEY = "url" - - fun intent(context: Context, title: LocaleString, url: String): Intent { - return Intent(context, WebBrowserActivity::class.java) - .putExtra(TITLE_KEY, title) - .putExtra(URL_KEY, url) - } - } } diff --git a/widgetssdk/src/test/java/com/glia/widgets/callvisualizer/CallVisualizerActivityWatcherTest.kt b/widgetssdk/src/test/java/com/glia/widgets/callvisualizer/CallVisualizerActivityWatcherTest.kt index 4a9c7b57e..49a85072d 100644 --- a/widgetssdk/src/test/java/com/glia/widgets/callvisualizer/CallVisualizerActivityWatcherTest.kt +++ b/widgetssdk/src/test/java/com/glia/widgets/callvisualizer/CallVisualizerActivityWatcherTest.kt @@ -14,6 +14,7 @@ import com.glia.widgets.core.dialog.model.ConfirmationDialogLinks import com.glia.widgets.core.dialog.model.Link import com.glia.widgets.helper.DialogHolderActivity import com.glia.widgets.helper.GliaActivityManager +import com.glia.widgets.helper.IntentHelper import com.glia.widgets.helper.Logger import com.glia.widgets.helper.OneTimeEvent import com.glia.widgets.helper.withRuntimeTheme @@ -50,6 +51,7 @@ class CallVisualizerActivityWatcherTest { private lateinit var watcher: CallVisualizerActivityWatcher private lateinit var mockLocale: LocaleString + private lateinit var intentHelper: IntentHelper @Before fun setUp() { @@ -66,8 +68,9 @@ class CallVisualizerActivityWatcherTest { localeProvider = mockk(relaxed = true) themeManager = mockk(relaxed = true) mockLocale = mockk(relaxed = true) + intentHelper = mockk(relaxed = true) - watcher = CallVisualizerActivityWatcher(controller, gliaActivityManager, localeProvider, themeManager) + watcher = CallVisualizerActivityWatcher(controller, gliaActivityManager, localeProvider, themeManager, intentHelper) verify { controller.state } } @@ -322,9 +325,8 @@ class CallVisualizerActivityWatcherTest { val title = mockLocale val url = "url" val intent: Intent = mockk(relaxed = true) - mockkObject(WebBrowserActivity) - every { WebBrowserActivity.intent(any(), any(), any()) } returns intent + every { intentHelper.webBrowserIntent(any(), any(), any()) } returns intent val activity = emitActivity() val event = createMockEvent(CallVisualizerContract.State.OpenWebBrowserScreen(title, url)) @@ -338,7 +340,6 @@ class CallVisualizerActivityWatcherTest { verify { activity.startActivity(eq(intent)) } confirmVerified(activity, event) - unmockkObject(WebBrowserActivity) } private fun createMockEvent(state: CallVisualizerContract.State, isConsumed: Boolean = false): OneTimeEvent { diff --git a/widgetssdk/src/test/java/com/glia/widgets/operator/OperatorRequestActivityWatcherTest.kt b/widgetssdk/src/test/java/com/glia/widgets/operator/OperatorRequestActivityWatcherTest.kt index 8b319963c..086afc5f8 100644 --- a/widgetssdk/src/test/java/com/glia/widgets/operator/OperatorRequestActivityWatcherTest.kt +++ b/widgetssdk/src/test/java/com/glia/widgets/operator/OperatorRequestActivityWatcherTest.kt @@ -22,7 +22,7 @@ import com.glia.widgets.chat.ChatActivity import com.glia.widgets.engagement.domain.MediaUpgradeOfferData import com.glia.widgets.helper.DialogHolderActivity import com.glia.widgets.helper.GliaActivityManager -import com.glia.widgets.helper.IntentConfigurationHelper +import com.glia.widgets.helper.IntentHelper import com.glia.widgets.helper.Logger import com.glia.widgets.helper.OneTimeEvent import com.glia.widgets.helper.showToast @@ -51,7 +51,7 @@ class OperatorRequestActivityWatcherTest { private val controllerState: PublishProcessor> = PublishProcessor.create() private lateinit var controller: OperatorRequestContract.Controller private lateinit var gliaActivityManager: GliaActivityManager - private lateinit var intentConfigurationHelper: IntentConfigurationHelper + private lateinit var intentHelper: IntentHelper private lateinit var watcher: OperatorRequestActivityWatcher @@ -65,9 +65,9 @@ class OperatorRequestActivityWatcherTest { controller = mockk(relaxUnitFun = true) every { controller.state } returns controllerState gliaActivityManager = mockk(relaxUnitFun = true) - intentConfigurationHelper = mockk(relaxUnitFun = true) + intentHelper = mockk(relaxUnitFun = true) - watcher = OperatorRequestActivityWatcher(controller, intentConfigurationHelper, gliaActivityManager) + watcher = OperatorRequestActivityWatcher(controller, intentHelper, gliaActivityManager) verify { controller.state } } @@ -76,7 +76,7 @@ class OperatorRequestActivityWatcherTest { fun tearDown() { RxAndroidPlugins.reset() - confirmVerified(controller, intentConfigurationHelper) + confirmVerified(controller, intentHelper) unmockkStatic(LOGGER_PATH, CONTEXT_EXTENSIONS_CLASS_PATH) } @@ -127,14 +127,14 @@ class OperatorRequestActivityWatcherTest { @Test fun `openCallActivity will start CallActivity when current activity is not a CallActivity`() { val intent: Intent = mockk(relaxed = true) - every { intentConfigurationHelper.createForCall(any(), any(), any()) } returns intent + every { intentHelper.callIntent(any(), any(), any()) } returns intent fireState( controllerState, watcher, OperatorRequestContract.State.OpenCallActivity(Engagement.MediaType.VIDEO) ) { event, activity -> verify { event.consume(any()) } verify { gliaActivityManager.finishActivities() } - verify { intentConfigurationHelper.createForCall(any(), any(), any()) } + verify { intentHelper.callIntent(any(), any(), any()) } verify { activity.startActivity(intent) } confirmVerified(intent, activity, event) @@ -144,14 +144,14 @@ class OperatorRequestActivityWatcherTest { @Test fun `openCallActivity will start CallActivity when current activity is not a GliaActivity`() { val intent: Intent = mockk(relaxed = true) - every { intentConfigurationHelper.createForCall(any(), any(), any()) } returns intent + every { intentHelper.callIntent(any(), any(), any()) } returns intent fireState( controllerState, watcher, OperatorRequestContract.State.OpenCallActivity(Engagement.MediaType.VIDEO) ) { event, activity -> verify { event.consume(any()) } verify(exactly = 0) { gliaActivityManager.finishActivities() } - verify { intentConfigurationHelper.createForCall(any(), any(), any()) } + verify { intentHelper.callIntent(any(), any(), any()) } verify { activity.startActivity(intent) } confirmVerified(intent, activity, event) @@ -161,14 +161,14 @@ class OperatorRequestActivityWatcherTest { @Test fun `openCallActivity will do nothing when current activity is Call Activity`() { val intent: Intent = mockk(relaxed = true) - every { intentConfigurationHelper.createForCall(any(), any(), any()) } returns intent + every { intentHelper.callIntent(any(), any(), any()) } returns intent fireState( controllerState, watcher, OperatorRequestContract.State.OpenCallActivity(Engagement.MediaType.VIDEO) ) { event, activity -> verify { event.consume(any()) } verify(exactly = 0) { gliaActivityManager.finishActivities() } - verify(exactly = 0) { intentConfigurationHelper.createForCall(any(), any(), any()) } + verify(exactly = 0) { intentHelper.callIntent(any(), any(), any()) } verify(exactly = 0) { activity.startActivity(intent) } confirmVerified(intent, activity, event) @@ -364,17 +364,17 @@ class OperatorRequestActivityWatcherTest { val overlayIntent: Intent = mockk { every { resolveActivity(any()) } returns componentName } - every { intentConfigurationHelper.createForOverlayPermissionScreen(any()) } returns overlayIntent + every { intentHelper.overlayPermissionIntent(any()) } returns overlayIntent fireState( controllerState, watcher, OperatorRequestContract.State.OpenOverlayPermissionScreen ) { _, activity -> - verify { intentConfigurationHelper.createForOverlayPermissionScreen(eq(activity)) } + verify { intentHelper.overlayPermissionIntent(eq(activity)) } verify { overlayIntent.resolveActivity(any()) } verify { activity.startActivity(eq(overlayIntent)) } verify { controller.overlayPermissionScreenOpened() } - confirmVerified(overlayIntent, componentName, intentConfigurationHelper) + confirmVerified(overlayIntent, componentName, intentHelper) } } @@ -383,17 +383,17 @@ class OperatorRequestActivityWatcherTest { val overlayIntent: Intent = mockk { every { resolveActivity(any()) } returns null } - every { intentConfigurationHelper.createForOverlayPermissionScreen(any()) } returns overlayIntent + every { intentHelper.overlayPermissionIntent(any()) } returns overlayIntent fireState( controllerState, watcher, OperatorRequestContract.State.OpenOverlayPermissionScreen ) { _, activity -> - verify { intentConfigurationHelper.createForOverlayPermissionScreen(eq(activity)) } + verify { intentHelper.overlayPermissionIntent(eq(activity)) } verify { overlayIntent.resolveActivity(any()) } verify { controller.failedToOpenOverlayPermissionScreen() } - confirmVerified(overlayIntent, intentConfigurationHelper) + confirmVerified(overlayIntent, intentHelper) } } From f3630e16f1c727174c5db21739158548574bf0cc Mon Sep 17 00:00:00 2001 From: Andrii Horishnii Date: Thu, 12 Sep 2024 23:04:37 +0300 Subject: [PATCH 003/116] Implement Entry Widget (embedded view) MOB-3478 --- .../glia/widgets/di/ControllerFactory.java | 6 ++ .../entrywidget/EntryWidgetContract.kt | 28 ++++++ .../entrywidget/EntryWidgetController.kt | 30 ++++++ .../widgets/entrywidget/EntryWidgetView.kt | 44 +++++++++ .../entrywidget/adapter/EntryWidgetAdapter.kt | 67 ++++++++++++++ .../EntryWidgetErrorStateViewHolder.kt | 33 +++++++ .../adapter/EntryWidgetItemDecoration.kt | 57 ++++++++++++ .../EntryWidgetMediaTypeItemViewHolder.kt | 52 +++++++++++ .../adapter/EntryWidgetPoweredByViewHolder.kt | 14 +++ .../src/main/res/drawable/bg_circle.xml | 5 + .../res/drawable/bg_entry_widget_loading.xml | 4 + widgetssdk/src/main/res/drawable/ic_audio.xml | 10 ++ widgetssdk/src/main/res/drawable/ic_chat.xml | 10 ++ widgetssdk/src/main/res/drawable/ic_video.xml | 10 ++ .../res/layout/entry_widget_error_item.xml | 48 ++++++++++ .../layout/entry_widget_media_type_item.xml | 92 +++++++++++++++++++ .../layout/entry_widget_powered_by_item.xml | 34 +++++++ widgetssdk/src/main/res/values/dimens.xml | 3 + .../src/main/res/values/new_strings.xml | 24 +++++ 19 files changed, 571 insertions(+) create mode 100644 widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetContract.kt create mode 100644 widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetController.kt create mode 100644 widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetView.kt create mode 100644 widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetAdapter.kt create mode 100644 widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetErrorStateViewHolder.kt create mode 100644 widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetItemDecoration.kt create mode 100644 widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetMediaTypeItemViewHolder.kt create mode 100644 widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetPoweredByViewHolder.kt create mode 100644 widgetssdk/src/main/res/drawable/bg_circle.xml create mode 100644 widgetssdk/src/main/res/drawable/bg_entry_widget_loading.xml create mode 100644 widgetssdk/src/main/res/drawable/ic_audio.xml create mode 100644 widgetssdk/src/main/res/drawable/ic_chat.xml create mode 100644 widgetssdk/src/main/res/drawable/ic_video.xml create mode 100644 widgetssdk/src/main/res/layout/entry_widget_error_item.xml create mode 100644 widgetssdk/src/main/res/layout/entry_widget_media_type_item.xml create mode 100644 widgetssdk/src/main/res/layout/entry_widget_powered_by_item.xml 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..cfb1e7a72 100644 --- a/widgetssdk/src/main/java/com/glia/widgets/di/ControllerFactory.java +++ b/widgetssdk/src/main/java/com/glia/widgets/di/ControllerFactory.java @@ -17,6 +17,8 @@ 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.entrywidget.EntryWidgetContract; +import com.glia.widgets.entrywidget.EntryWidgetController; import com.glia.widgets.filepreview.ui.FilePreviewContract; import com.glia.widgets.filepreview.ui.FilePreviewController; import com.glia.widgets.helper.Logger; @@ -385,4 +387,8 @@ public OperatorRequestContract.Controller getOperatorRequestController() { useCaseFactory.getReleaseScreenSharingResourcesUseCase() ); } + + public EntryWidgetContract.Controller getEntryWidgetController() { + return new EntryWidgetController(); + } } 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..bf7536684 --- /dev/null +++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetContract.kt @@ -0,0 +1,28 @@ +package com.glia.widgets.entrywidget + +import com.glia.widgets.base.BaseController +import com.glia.widgets.base.BaseView + +internal interface EntryWidgetContract { + + enum class ItemType { + VIDEO_CALL, + AUDIO_CALL, + CHAT, + SECURE_MESSAGE, + LOADING_STATE, + EMPTY_STATE, + ERROR_STATE, + PROVIDED_BY + } + + interface Controller : BaseController { + fun setView(view: View) + fun onItemClicked(itemType: ItemType) + } + + interface View : BaseView { + fun showItems(items: List) + fun dismiss() + } +} 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..dbf95a570 --- /dev/null +++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetController.kt @@ -0,0 +1,30 @@ +package com.glia.widgets.entrywidget + +internal class EntryWidgetController : EntryWidgetContract.Controller { + private lateinit var view: EntryWidgetContract.View + + override fun setView(view: EntryWidgetContract.View) { + this.view = view + + // TODO: Remove this hardcoded list of items + val items = listOf( + EntryWidgetContract.ItemType.VIDEO_CALL, + EntryWidgetContract.ItemType.AUDIO_CALL, + EntryWidgetContract.ItemType.CHAT, + EntryWidgetContract.ItemType.SECURE_MESSAGE, + EntryWidgetContract.ItemType.PROVIDED_BY + ) + view.showItems(items) + } + + override fun onItemClicked(itemType: EntryWidgetContract.ItemType) { + // TODO: Handle item click + + // Dismiss the widget + view.dismiss() + } + + override fun onDestroy() { + // Clean up + } +} 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..f2064349c --- /dev/null +++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetView.kt @@ -0,0 +1,44 @@ +package com.glia.widgets.entrywidget + +import android.content.Context +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.EntryWidgetItemDecoration +import com.glia.widgets.helper.getDrawableCompat + +internal class EntryWidgetView( + context: Context +) : RecyclerView(context, null, 0), EntryWidgetContract.View { + var onDismissListener: (() -> Unit)? = null + + private lateinit var controller: EntryWidgetContract.Controller + private val adapter = EntryWidgetAdapter().also { setAdapter(it) } + + init { + layoutManager = LinearLayoutManager(context) + getDrawableCompat(R.drawable.bg_separator_gray)?.let { + addItemDecoration(EntryWidgetItemDecoration(it)) + } + adapter.onItemClickListener = { + controller.onItemClicked(it) + } + + setController(Dependencies.controllerFactory.entryWidgetController) + } + + override fun setController(controller: EntryWidgetContract.Controller) { + this.controller = controller + controller.setView(this) + } + + override fun showItems(items: List) { + adapter.items = items + } + + override fun dismiss() { + onDismissListener?.invoke() + } +} 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..171a75bcb --- /dev/null +++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetAdapter.kt @@ -0,0 +1,67 @@ +package com.glia.widgets.entrywidget.adapter + +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.glia.widgets.databinding.EntryWidgetErrorItemBinding +import com.glia.widgets.databinding.EntryWidgetMediaTypeItemBinding +import com.glia.widgets.databinding.EntryWidgetPoweredByItemBinding +import com.glia.widgets.entrywidget.EntryWidgetContract +import com.glia.widgets.helper.layoutInflater + +internal class EntryWidgetAdapter : RecyclerView.Adapter() { + + enum class ViewType { + CONTACT_ITEM, + ERROR_ITEM, + PROVIDED_BY_ITEM + } + + var items: List? = null + set(value) { + field = value + notifyDataSetChanged() + } + + var onItemClickListener: ((EntryWidgetContract.ItemType) -> Unit)? = null + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return when (viewType) { + ViewType.ERROR_ITEM.ordinal -> EntryWidgetErrorStateViewHolder( + EntryWidgetErrorItemBinding.inflate(parent.layoutInflater, parent, false) + ) + ViewType.PROVIDED_BY_ITEM.ordinal -> EntryWidgetPoweredByViewHolder( + EntryWidgetPoweredByItemBinding.inflate(parent.layoutInflater, parent, false) + ) + else -> EntryWidgetMediaTypeItemViewHolder( + EntryWidgetMediaTypeItemBinding.inflate(parent.layoutInflater, parent, false) + ) + } + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + items?.get(position)?.let { item -> + holder.bind(item) { + onItemClickListener?.invoke(item) + } + } + } + + override fun getItemViewType(position: Int): Int { + return when (items?.get(position)) { + EntryWidgetContract.ItemType.EMPTY_STATE, + EntryWidgetContract.ItemType.ERROR_STATE -> ViewType.ERROR_ITEM.ordinal + EntryWidgetContract.ItemType.PROVIDED_BY -> ViewType.PROVIDED_BY_ITEM.ordinal + else -> ViewType.CONTACT_ITEM.ordinal + } + } + + override fun getItemCount(): Int { + return items?.size ?: 0 + } + + abstract class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + open fun bind(itemType: EntryWidgetContract.ItemType, onClickListener: View.OnClickListener) {} + } + +} 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..669b79e1b --- /dev/null +++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetErrorStateViewHolder.kt @@ -0,0 +1,33 @@ +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 + +internal class EntryWidgetErrorStateViewHolder( + private val binding: EntryWidgetErrorItemBinding +) : EntryWidgetAdapter.ViewHolder(binding.root) { + + override fun bind(itemType: EntryWidgetContract.ItemType, onClickListener: View.OnClickListener) { + when (itemType) { + EntryWidgetContract.ItemType.EMPTY_STATE -> { + 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.ERROR_STATE -> { + 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 + } + else -> { + // Do nothing + } + } + } +} diff --git a/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetItemDecoration.kt b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetItemDecoration.kt new file mode 100644 index 000000000..218cf7518 --- /dev/null +++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetItemDecoration.kt @@ -0,0 +1,57 @@ +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 EntryWidgetItemDecoration( + 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 childCount = parent.childCount + + for (i in 0 until childCount) { + val child = parent.getChildAt(i) + val position = parent.getChildAdapterPosition(child) + + 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) + 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.CONTACT_ITEM.ordinal + } +} diff --git a/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetMediaTypeItemViewHolder.kt b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetMediaTypeItemViewHolder.kt new file mode 100644 index 000000000..d6fce42cd --- /dev/null +++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetMediaTypeItemViewHolder.kt @@ -0,0 +1,52 @@ +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.EntryWidgetMediaTypeItemBinding +import com.glia.widgets.entrywidget.EntryWidgetContract +import com.glia.widgets.helper.setLocaleContentDescription +import com.glia.widgets.helper.setLocaleText + +internal class EntryWidgetMediaTypeItemViewHolder( + private val binding: EntryWidgetMediaTypeItemBinding +) : EntryWidgetAdapter.ViewHolder(binding.root) { + + override fun bind(itemType: EntryWidgetContract.ItemType, onClickListener: View.OnClickListener) { + binding.root.setOnClickListener(onClickListener) + when (itemType) { + EntryWidgetContract.ItemType.VIDEO_CALL -> { + 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.root.setLocaleContentDescription(R.string.entry_widget_video_button_accessibility) + } + EntryWidgetContract.ItemType.AUDIO_CALL -> { + 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.root.setLocaleContentDescription(R.string.entry_widget_audio_button_accessibility) + } + 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.root.setLocaleContentDescription(R.string.entry_widget_live_chat_button_accessibility) + } + EntryWidgetContract.ItemType.SECURE_MESSAGE -> { + 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.root.setLocaleContentDescription(R.string.entry_widget_secure_messaging_button_accessibility) + } + else -> { + binding.icon.setImageResource(0) + binding.title.text = null + binding.description.text = null + binding.root.contentDescription = null + } + } + + binding.loadingGroup.isVisible = itemType == EntryWidgetContract.ItemType.LOADING_STATE + } +} 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..ebf3f94d1 --- /dev/null +++ b/widgetssdk/src/main/java/com/glia/widgets/entrywidget/adapter/EntryWidgetPoweredByViewHolder.kt @@ -0,0 +1,14 @@ +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( + private val binding: EntryWidgetPoweredByItemBinding +) : EntryWidgetAdapter.ViewHolder(binding.root) { + + init { + binding.title.setLocaleText(R.string.general_powered) + } +} diff --git a/widgetssdk/src/main/res/drawable/bg_circle.xml b/widgetssdk/src/main/res/drawable/bg_circle.xml new file mode 100644 index 000000000..09e4e7abd --- /dev/null +++ b/widgetssdk/src/main/res/drawable/bg_circle.xml @@ -0,0 +1,5 @@ + + + + diff --git a/widgetssdk/src/main/res/drawable/bg_entry_widget_loading.xml b/widgetssdk/src/main/res/drawable/bg_entry_widget_loading.xml new file mode 100644 index 000000000..8a165034e --- /dev/null +++ b/widgetssdk/src/main/res/drawable/bg_entry_widget_loading.xml @@ -0,0 +1,4 @@ + + + + diff --git a/widgetssdk/src/main/res/drawable/ic_audio.xml b/widgetssdk/src/main/res/drawable/ic_audio.xml new file mode 100644 index 000000000..55448bd63 --- /dev/null +++ b/widgetssdk/src/main/res/drawable/ic_audio.xml @@ -0,0 +1,10 @@ + + + diff --git a/widgetssdk/src/main/res/drawable/ic_chat.xml b/widgetssdk/src/main/res/drawable/ic_chat.xml new file mode 100644 index 000000000..d5067d67f --- /dev/null +++ b/widgetssdk/src/main/res/drawable/ic_chat.xml @@ -0,0 +1,10 @@ + + + diff --git a/widgetssdk/src/main/res/drawable/ic_video.xml b/widgetssdk/src/main/res/drawable/ic_video.xml new file mode 100644 index 000000000..dabfc6b37 --- /dev/null +++ b/widgetssdk/src/main/res/drawable/ic_video.xml @@ -0,0 +1,10 @@ + + + diff --git a/widgetssdk/src/main/res/layout/entry_widget_error_item.xml b/widgetssdk/src/main/res/layout/entry_widget_error_item.xml new file mode 100644 index 000000000..5b14e5356 --- /dev/null +++ b/widgetssdk/src/main/res/layout/entry_widget_error_item.xml @@ -0,0 +1,48 @@ + + + + + + + + + + diff --git a/widgetssdk/src/main/res/layout/entry_widget_media_type_item.xml b/widgetssdk/src/main/res/layout/entry_widget_media_type_item.xml new file mode 100644 index 000000000..26706b197 --- /dev/null +++ b/widgetssdk/src/main/res/layout/entry_widget_media_type_item.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + diff --git a/widgetssdk/src/main/res/layout/entry_widget_powered_by_item.xml b/widgetssdk/src/main/res/layout/entry_widget_powered_by_item.xml new file mode 100644 index 000000000..e2611d96e --- /dev/null +++ b/widgetssdk/src/main/res/layout/entry_widget_powered_by_item.xml @@ -0,0 +1,34 @@ + + + + + + + + diff --git a/widgetssdk/src/main/res/values/dimens.xml b/widgetssdk/src/main/res/values/dimens.xml index cf7ab4027..41771a5c3 100644 --- a/widgetssdk/src/main/res/values/dimens.xml +++ b/widgetssdk/src/main/res/values/dimens.xml @@ -92,4 +92,7 @@ 72dp + + + 40dp diff --git a/widgetssdk/src/main/res/values/new_strings.xml b/widgetssdk/src/main/res/values/new_strings.xml index 97b264063..31f91e1ee 100644 --- a/widgetssdk/src/main/res/values/new_strings.xml +++ b/widgetssdk/src/main/res/values/new_strings.xml @@ -214,4 +214,28 @@ This action is not currently supported on mobile. %1$s \n Card: %2$d of %3$d. + + Live Chat + For the texter in all of us + Chat with us + Audio + Speak through your phone + Call with us + Video + You’ll see us, but we won’t see you + Video call with us + Secure Messaging + Start a conversation, we’ll get back to you + Secure Messaging with us + Support team is currently offline + No active operators available + We are here to assist you during our business hours. + No active operators available + Oops! Contacts Couldn\'t Be Loaded + Contacts Couldn\'t Be Loaded + We couldn\'t load the contacts at this time. This may be due to a temporary syncing issue or network problem. + Contacts Couldn\'t Be Loaded + Try again + Try to connect again + From bb125191e42d214f3406e62b525f38bea34b8cee Mon Sep 17 00:00:00 2001 From: Andrii Horishnii Date: Thu, 12 Sep 2024 21:12:07 +0300 Subject: [PATCH 004/116] Implement Entry Widget (bottom sheet) MOB-3476 --- .../java/com/glia/exampleapp/MainFragment.kt | 5 ++ app/src/main/res/layout/main_fragment.xml | 14 ++-- app/src/main/res/values/strings.xml | 1 + widgetssdk/src/main/AndroidManifest.xml | 5 ++ .../entrywidget/EntryWidgetActivity.kt | 24 +++++++ .../entrywidget/EntryWidgetContract.kt | 6 ++ .../entrywidget/EntryWidgetFragment.kt | 70 +++++++++++++++++++ .../widgets/entrywidget/EntryWidgetView.kt | 10 ++- .../entrywidget/adapter/EntryWidgetAdapter.kt | 7 +- .../EntryWidgetErrorStateViewHolder.kt | 7 +- .../src/main/res/drawable/bg_bottom_sheet.xml | 8 +++ .../res/drawable/bg_entry_widget_divider.xml | 14 ++++ .../src/main/res/drawable/ic_drag_handle.xml | 11 +++ .../main/res/layout/entry_widget_fragment.xml | 33 +++++++++ widgetssdk/src/main/res/values/dimens.xml | 1 + widgetssdk/src/main/res/values/themes.xml | 9 +++ 16 files changed, 212 insertions(+), 13 deletions(-) create mode 100644 widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetActivity.kt create mode 100644 widgetssdk/src/main/java/com/glia/widgets/entrywidget/EntryWidgetFragment.kt create mode 100644 widgetssdk/src/main/res/drawable/bg_bottom_sheet.xml create mode 100644 widgetssdk/src/main/res/drawable/bg_entry_widget_divider.xml create mode 100644 widgetssdk/src/main/res/drawable/ic_drag_handle.xml create mode 100644 widgetssdk/src/main/res/layout/entry_widget_fragment.xml diff --git a/app/src/main/java/com/glia/exampleapp/MainFragment.kt b/app/src/main/java/com/glia/exampleapp/MainFragment.kt index 61c23a638..bbf9160c2 100644 --- a/app/src/main/java/com/glia/exampleapp/MainFragment.kt +++ b/app/src/main/java/com/glia/exampleapp/MainFragment.kt @@ -37,6 +37,7 @@ import com.glia.widgets.call.CallActivity import com.glia.widgets.chat.ChatActivity import com.glia.widgets.chat.ChatType import com.glia.widgets.core.notification.NotificationActionReceiver +import com.glia.widgets.entrywidget.EntryWidgetActivity import com.glia.widgets.messagecenter.MessageCenterActivity import com.google.android.material.appbar.MaterialToolbar import kotlin.concurrent.thread @@ -74,6 +75,10 @@ class MainFragment : Fragment() { view.findViewById(R.id.settings_button) .setOnClickListener { navController.navigate(R.id.settings) } + view.findViewById(R.id.entry_widget_button) + .setOnClickListener { + startActivity(Intent(requireContext(), EntryWidgetActivity::class.java)) + } view.findViewById(R.id.chat_activity_button) .setOnClickListener { navigateToChat(ChatType.LIVE_CHAT) } view.findViewById(R.id.audio_call_button) diff --git a/app/src/main/res/layout/main_fragment.xml b/app/src/main/res/layout/main_fragment.xml index 31aaf060d..80a417840 100644 --- a/app/src/main/res/layout/main_fragment.xml +++ b/app/src/main/res/layout/main_fragment.xml @@ -67,17 +67,17 @@ app:layout_constraintStart_toStartOf="@id/start_guideline" app:layout_constraintTop_toBottomOf="@id/tool_bar_layout" /> - + app:layout_constraintTop_toBottomOf="@id/settings_button" />