diff --git a/personalization-sdk/build.gradle b/personalization-sdk/build.gradle index 834a1cac..4848b8b8 100644 --- a/personalization-sdk/build.gradle +++ b/personalization-sdk/build.gradle @@ -6,7 +6,7 @@ plugins { id 'kotlin-kapt' } -version='2.0.28' +version='2.0.29' android { compileSdkVersion 34 diff --git a/personalization-sdk/src/main/kotlin/com/personalization/SDK.kt b/personalization-sdk/src/main/kotlin/com/personalization/SDK.kt index 11bb1ebb..ea0fe3e1 100644 --- a/personalization-sdk/src/main/kotlin/com/personalization/SDK.kt +++ b/personalization-sdk/src/main/kotlin/com/personalization/SDK.kt @@ -21,6 +21,7 @@ import com.personalization.sdk.domain.usecases.network.SendNetworkMethodUseCase import com.personalization.sdk.domain.usecases.notification.GetAllNotificationsUseCase import com.personalization.sdk.domain.usecases.preferences.GetPreferencesValueUseCase import com.personalization.sdk.domain.usecases.preferences.InitPreferencesUseCase +import com.personalization.sdk.domain.usecases.recommendation.SetRecommendedByUseCase import com.personalization.sdk.domain.usecases.userSettings.GetUserSettingsValueUseCase import com.personalization.sdk.domain.usecases.userSettings.InitUserSettingsUseCase import com.personalization.stories.StoriesManager @@ -80,9 +81,13 @@ open class SDK { @Inject lateinit var sendNetworkMethodUseCase: SendNetworkMethodUseCase + @Inject + lateinit var setRecommendedByUseCase: SetRecommendedByUseCase + @Inject lateinit var getAllNotificationsUseCase: GetAllNotificationsUseCase + /** * @param shopId Shop key */ @@ -132,6 +137,43 @@ open class SDK { storiesManager.initialize(storiesView) } + /** + * @param listener + */ + fun stories(code: String, listener: OnApiCallbackListener) { + storiesManager.requestStories(code, listener) + } + + /** + * Show stories block by code + * + * @param code Stories block code + */ + fun showStories(code: String) { + storiesManager.showStories(context.mainLooper, code) + } + + /** + * Triggers a story event + * + * @param event Event + * @param code Stories block code + * @param storyId Story ID + * @param slideId Slide ID + */ + fun trackStory(event: String, code: String, storyId: Int, slideId: String) { + if (::storiesManager.isInitialized) { + storiesManager.trackStory( + event = event, + code = code, + storyId = storyId, + slideId = slideId + ) + } else { + Log.i(TAG, "storiesManager is not initialized") + } + } + /** * Update profile data * @@ -670,34 +712,6 @@ open class SDK { } } - /** - * @param listener - */ - fun stories(code: String, listener: OnApiCallbackListener) { - storiesManager.requestStories(code, listener) - } - - /** - * Show stories block by code - * - * @param code Stories block code - */ - fun showStories(code: String) { - storiesManager.showStories(context.mainLooper, code) - } - - /** - * Triggers a story event - * - * @param event Event - * @param code Stories block code - * @param storyId Story ID - * @param slideId Slide ID - */ - fun trackStory(event: String, code: String, storyId: Int, slideId: String) { - storiesManager.trackStory(event, code, storyId, slideId) - } - /** * @param data from data notification */ diff --git a/personalization-sdk/src/main/kotlin/com/personalization/stories/StoriesManager.kt b/personalization-sdk/src/main/kotlin/com/personalization/stories/StoriesManager.kt index 973aa1cd..41f6c8e2 100644 --- a/personalization-sdk/src/main/kotlin/com/personalization/stories/StoriesManager.kt +++ b/personalization-sdk/src/main/kotlin/com/personalization/stories/StoriesManager.kt @@ -16,40 +16,47 @@ import javax.inject.Inject class StoriesManager @Inject constructor( val setRecommendedByUseCase: SetRecommendedByUseCase, - private val sendNetworkMethodUseCase: SendNetworkMethodUseCase + val sendNetworkMethodUseCase: SendNetworkMethodUseCase ) { private lateinit var storiesView: StoriesView internal fun initialize(storiesView: StoriesView) { + Log.d("StoriesManager", "initialize called") this.storiesView = storiesView - updateStories() } internal fun showStories(looper: Looper, code: String) { - requestStories(code, object : OnApiCallbackListener() { - override fun onSuccess(response: JSONObject?) { - response?.let { - Log.d("stories", response.toString()) - try { - val stories = getStories(response) - - if(stories.isEmpty()) return - - resetStoriesStartPositions(stories) - - showStories(looper, stories) - } catch (e: JSONException) { - Log.e(SDK.TAG, e.message, e) + requestStories( + code = code, + listener = object : OnApiCallbackListener() { + override fun onSuccess(response: JSONObject?) { + response?.let { + Log.d("stories", response.toString()) + try { + val stories = getStories(response) + + if (stories.isEmpty()) return + + resetStoriesStartPositions(stories) + + showStories(looper, stories) + } catch (e: JSONException) { + Log.e(SDK.TAG, e.message, e) + } } } } - }) + ) } internal fun requestStories(code: String, listener: OnApiCallbackListener) { - sendNetworkMethodUseCase.getAsync(String.format(REQUEST_STORIES_METHOD, code), JSONObject(), listener) + sendNetworkMethodUseCase.getAsync( + method = String.format(REQUEST_STORIES_METHOD, code), + params = JSONObject(), + listener = listener + ) } /** @@ -78,19 +85,22 @@ class StoriesManager @Inject constructor( } private fun updateStories() { - requestStories(storiesView.code, object : OnApiCallbackListener() { - override fun onSuccess(response: JSONObject?) { - response?.let { - Log.d("stories", response.toString()) - try { - val stories = getStories(response) - storiesView.updateStories(stories) - } catch (e: JSONException) { - Log.e(SDK.TAG, e.message, e) + requestStories( + code = storiesView.code, + listener = object : OnApiCallbackListener() { + override fun onSuccess(response: JSONObject?) { + response?.let { + Log.d("stories", response.toString()) + try { + val stories = getStories(response) + storiesView.updateStories(stories) + } catch (e: JSONException) { + Log.e(SDK.TAG, e.message, e) + } } } } - }) + ) } private fun getStories(json: JSONObject): List { @@ -114,17 +124,20 @@ class StoriesManager @Inject constructor( private fun showStories(looper: Looper, stories: List, startPosition: Int = 0) { val handler = Handler(looper) handler.post { - storiesView.showStories(stories, startPosition) + storiesView.showStories( + stories = stories, + startPosition = startPosition + ) } } companion object { - private const val TRACK_STORIES_METHOD = "track/stories" - private const val REQUEST_STORIES_METHOD = "stories/%s" + const val TRACK_STORIES_METHOD = "track/stories" + const val REQUEST_STORIES_METHOD = "stories/%s" - private const val EVENT_PARAMS_NAME = "event" - private const val STORY_ID_PARAMS_NAME = "story_id" - private const val SLIDE_ID_PARAMS_NAME = "slide_id" - private const val CODE_PARAMS_NAME = "code" + const val EVENT_PARAMS_NAME = "event" + const val STORY_ID_PARAMS_NAME = "story_id" + const val SLIDE_ID_PARAMS_NAME = "slide_id" + const val CODE_PARAMS_NAME = "code" } } diff --git a/personalization-sdk/src/main/kotlin/com/personalization/stories/views/StoriesView.kt b/personalization-sdk/src/main/kotlin/com/personalization/stories/views/StoriesView.kt index ba6423a4..adca7ee5 100644 --- a/personalization-sdk/src/main/kotlin/com/personalization/stories/views/StoriesView.kt +++ b/personalization-sdk/src/main/kotlin/com/personalization/stories/views/StoriesView.kt @@ -136,7 +136,13 @@ class StoriesView : ConstraintLayout, ClickListener { completeShowStory: () -> Unit = { }, cancelShowStory: () -> Unit = { } ) { - val dialog = StoryDialog(this, stories, startPosition, completeShowStory, cancelShowStory) + val dialog = StoryDialog( + storiesView = this, + stories = stories, + startPosition = startPosition, + completeShowStory = completeShowStory, + cancelShowStory = cancelShowStory + ) dialog.show() } diff --git a/personalization-sdk/src/main/kotlin/com/personalization/stories/views/StoryView.kt b/personalization-sdk/src/main/kotlin/com/personalization/stories/views/StoryView.kt index 65121457..3b3bb7da 100644 --- a/personalization-sdk/src/main/kotlin/com/personalization/stories/views/StoryView.kt +++ b/personalization-sdk/src/main/kotlin/com/personalization/stories/views/StoryView.kt @@ -1,3 +1,5 @@ +@file:SuppressLint("ClickableViewAccessibility", "ViewConstructor") + package com.personalization.stories.views import android.annotation.SuppressLint @@ -19,6 +21,7 @@ import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback import com.personalization.R import com.personalization.SDK +import com.personalization.SDK.Companion.TAG import com.personalization.stories.StoryState import com.personalization.stories.models.Slide import com.personalization.stories.models.Story @@ -26,45 +29,39 @@ import com.personalization.stories.views.StoriesProgressView.StoriesListener import com.personalization.stories.views.storyItem.StoryItemView import com.personalization.stories.views.storyItem.StoryItemView.OnPageListener -@SuppressLint("ViewConstructor") -internal class StoryView @SuppressLint("ClickableViewAccessibility") constructor( - private val storiesView: StoriesView, //Heading +internal class StoryView( + private val storiesView: StoriesView, private val storyStateListener: StoryDialog.OnStoryStateListener ) : ConstraintLayout(storiesView.context), StoriesListener, Player.Listener { - private var story: Story? = null - - private var pressTime = 0L - private var completeListener: Runnable? = null - private var prevStoryListener: Runnable? = null private lateinit var storiesProgressView: StoriesProgressView private var onTouchListener: OnTouchListener? = null + private var viewPagerSize: Pair? = null + private val holders = HashMap() + private var prevStoryListener: Runnable? = null + private var completeListener: Runnable? = null private lateinit var mViewPager: ViewPager2 + private lateinit var mute: ToggleButton private var storiesStarted = false private var prevFocusState = true + private var story: Story? = null private var locked = false - private val holders = HashMap() - private lateinit var mute: ToggleButton - - private var viewPagerSize: Pair? = null + private var pressTime = 0L init { inflate(context, R.layout.story_view, this) layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) initViews() - setupViews(); + setupViews() } private fun initViews() { storiesProgressView = findViewById(R.id.storiesProgressView) - mViewPager = findViewById(R.id.storiesViewPager) - mute = findViewById(R.id.mute) } - @SuppressLint("ClickableViewAccessibility") private fun setupViews() { onTouchListener = OnTouchListener { _: View?, event: MotionEvent -> if (storiesStarted) { @@ -72,7 +69,6 @@ internal class StoryView @SuppressLint("ClickableViewAccessibility") constructor MotionEvent.ACTION_DOWN -> { pressTime = System.currentTimeMillis() pause() - // setHeadingVisibility(GONE); return@OnTouchListener false } @@ -81,7 +77,6 @@ internal class StoryView @SuppressLint("ClickableViewAccessibility") constructor if (LIMIT < now - pressTime) { resume() } - // setHeadingVisibility(VISIBLE); return@OnTouchListener LIMIT < now - pressTime } } @@ -95,34 +90,37 @@ internal class StoryView @SuppressLint("ClickableViewAccessibility") constructor mViewPager.clipToPadding = false mViewPager.clipChildren = false mViewPager.offscreenPageLimit = 1 - mViewPager.registerOnPageChangeCallback(object : OnPageChangeCallback() { - override fun onPageSelected(position: Int) { - val player = com.personalization.stories.Player.player - player?.let { - if (player.isPlaying || player.isLoading) { - player.pause() - } - } - story?.let { story -> - for (i in 0 until story.slidesCount) { - if (i != position) { - getHolder(i)?.release() + mViewPager.registerOnPageChangeCallback( + object : OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + val player = com.personalization.stories.Player.player + player?.let { + if (player.isPlaying || player.isLoading) { + player.pause() } } - if (storiesStarted) { - SDK.instance.trackStory( - event = "view", - code = storiesView.code, - storyId = story.id, - slideId = story.getSlide(position).id - ) - playVideo() + story?.let { story -> + for (i in 0 until story.slidesCount) { + if (i != position) { + getHolder(i)?.release() + } + } + + if (storiesStarted) { + SDK.instance.trackStory( + event = "view", + code = storiesView.code, + storyId = story.id, + slideId = story.getSlide(position).id + ) + playVideo() + } } } } - }) + ) com.personalization.stories.Player?.let { player -> player.player?.let { innerPlayer -> @@ -154,8 +152,12 @@ internal class StoryView @SuppressLint("ClickableViewAccessibility") constructor storiesProgressView.setStoriesCount(slidesCount) mViewPager.adapter = ViewPagerAdapter() - // Hack to prevent onPageSelected from triggering when opening the first campaign - mViewPager.setCurrentItem(if (story.startPosition == 0) slidesCount else 0, false) + mViewPager.setCurrentItem( + when (story.startPosition) { + 0 -> slidesCount + else -> 0 + }, false + ) mViewPager.setCurrentItem(story.startPosition, false) } @@ -218,7 +220,7 @@ internal class StoryView @SuppressLint("ClickableViewAccessibility") constructor } override fun onPlayerError(error: PlaybackException) { - Log.e(SDK.TAG, "player error: " + error.message + ", story: " + story?.id) + Log.e(TAG, "player error: " + error.message + ", story: " + story?.id) val holder = currentHolder holder?.storyItem?.let { storyItem -> storyItem.reloadLayout.visibility = VISIBLE @@ -229,7 +231,7 @@ internal class StoryView @SuppressLint("ClickableViewAccessibility") constructor holder.storyItem.reloadLayout.visibility = GONE playVideo() } else { - storiesView.code?.let { code -> + storiesView.code.let { code -> holder.storyItem.update(slide, mViewPager.currentItem, code, story!!.id) } } @@ -255,39 +257,43 @@ internal class StoryView @SuppressLint("ClickableViewAccessibility") constructor viewPagerSize = Pair(viewPagerHeight, viewPagerTopOffset) } storyItem.setViewSize(viewPagerSize!!.first, viewPagerSize!!.second) - storyItem.setOnPageListener(object : OnPageListener { - override fun onPrev() { - previousSlide() - } + storyItem.setOnPageListener( + object : OnPageListener { + override fun onPrev() { + previousSlide() + } - override fun onNext() { - nextSlide() - } + override fun onNext() { + nextSlide() + } - override fun onPrepared(position: Int) { - if (story?.getSlide(position)?.type == "image" && storiesStarted) { - try { - storiesProgressView.resume() - } catch (_: IndexOutOfBoundsException) { + override fun onPrepared(position: Int) { + if (story?.getSlide(position)?.type == "image" && storiesStarted) { + try { + storiesProgressView.resume() + } catch (e: IndexOutOfBoundsException) { + Log.e(TAG, "Video onPrepared $e") + } } } - } - override fun onLocked(lock: Boolean) { - locked = lock - storyStateListener.onStoryStateChanged( - storyState = if(lock) StoryState.PAUSE else StoryState.RUNNING - ) - if (lock) { - pause() - } else { - resume() + override fun onLocked(lock: Boolean) { + locked = lock + storyStateListener.onStoryStateChanged( + storyState = when { + lock -> StoryState.PAUSE + else -> StoryState.RUNNING + } + ) + when { + lock -> pause() + else -> resume() + } } } - }) + ) } - @SuppressLint("ClickableViewAccessibility") fun bind(slide: Slide?, position: Int) { holders[position] = this mute.visibility = GONE @@ -306,12 +312,14 @@ internal class StoryView @SuppressLint("ClickableViewAccessibility") constructor internal inner class ViewPagerAdapter : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PagerHolder { - return PagerHolder(StoryItemView( - context = storiesView.context, - code = storiesView.code, - settings = storiesView.settings, - itemClickListener = storiesView.itemClickListener, - storyStateListener = storyStateListener) + return PagerHolder( + StoryItemView( + context = storiesView.context, + code = storiesView.code, + settings = storiesView.settings, + itemClickListener = storiesView.itemClickListener, + storyStateListener = storyStateListener + ) ) } @@ -337,7 +345,7 @@ internal class StoryView @SuppressLint("ClickableViewAccessibility") constructor } } - fun updateDurations() { + private fun updateDurations() { story?.let { story -> val slidesCount = story.slidesCount val durations = LongArray(slidesCount) @@ -423,8 +431,11 @@ internal class StoryView @SuppressLint("ClickableViewAccessibility") constructor completeListener?.run() } - fun updateDuration(position: Int) { - storiesProgressView.updateStoryDuration(position, story?.getSlide(position)?.duration ?: 0) + private fun updateDuration(position: Int) { + storiesProgressView.updateStoryDuration( + position = position, + duration = story?.getSlide(position)?.duration ?: 0 + ) } fun startStories() {