Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,26 @@ import android.os.Bundle
import android.view.MenuItem
import androidx.activity.viewModels
import androidx.annotation.VisibleForTesting
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE
import androidx.fragment.app.commit
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.asFlow
import androidx.lifecycle.asLiveData
import androidx.lifecycle.switchMap
import dagger.hilt.android.AndroidEntryPoint
import java.util.Locale
import kotlinx.coroutines.flow.combine
import org.ccci.gto.android.common.androidx.fragment.app.backStackEntries
import org.ccci.gto.android.common.androidx.fragment.app.hasPendingActions
import org.ccci.gto.android.common.androidx.lifecycle.ImmutableLiveData
import org.ccci.gto.android.common.androidx.lifecycle.combineWith
import org.ccci.gto.android.common.androidx.lifecycle.toggleValue
import org.cru.godtools.base.HOST_DYNALINKS
import org.cru.godtools.base.HOST_GODTOOLSAPP_COM
import org.cru.godtools.base.SCHEME_GODTOOLS
import org.cru.godtools.base.URI_SHARE_BASE
import org.cru.godtools.base.tool.activity.MultiLanguageToolActivity
import org.cru.godtools.base.tool.model.Event
import org.cru.godtools.shared.tool.parser.model.Manifest
Expand Down Expand Up @@ -49,6 +56,14 @@ class CyoaActivity :
// track this tool open
if (savedInstanceState == null) dataModel.toolCode.value?.let { trackToolOpen(it, Manifest.Type.CYOA) }

supportFragmentManager.registerFragmentLifecycleCallbacks(
object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentStarted(fm: FragmentManager, f: Fragment) = updatePageFragmentLiveData()
override fun onFragmentDestroyed(fm: FragmentManager, f: Fragment) = updatePageFragmentLiveData()
},
false
)

dataModel.activeManifest.observe(this) { it?.let { showInitialPageIfNecessary(it) } }
}

Expand Down Expand Up @@ -181,6 +196,31 @@ class CyoaActivity :
// endregion Training Tips
// endregion UI

// region Share Link Logic
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
public override val shareLinkUriLiveData by lazy {
dataModel.activeManifest.combineWith(activePageLiveData) { manifest, page ->
manifest?.buildShareLink(page)?.build()?.toString()
}
}

private val activePageLiveData by lazy {
pageFragmentLiveData.switchMap { it?.page ?: ImmutableLiveData<Page?>(null) }
}

private fun Manifest.buildShareLink(page: Page?): Uri.Builder? {
val tool = code ?: return null
val locale = locale ?: return null
return URI_SHARE_BASE.buildUpon()
.appendEncodedPath(locale.toString().lowercase(Locale.ENGLISH))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

locale.toString() might be wrong for locales with regions, if it's a java Locale object you will want to use locale.toLanguageTag(), if it's the fluidsonic Locale object, I'm not totally sure what format it uses for .toString()

.appendPath("tool")
.appendPath("v2")
.appendPath(tool)
.apply { page?.let { appendPath(it.id) } }
Comment on lines +216 to +219

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you are properly handling page collection pages here or card collection pages

.appendQueryParameter("icid", "gtshare")
}
// endregion Share Link Logic

// region Page management
@VisibleForTesting
internal val pageFragment
Expand All @@ -190,6 +230,13 @@ class CyoaActivity :
}
private val activePage get() = pageFragment?.page?.value

private val pageFragmentLiveData = MutableLiveData<CyoaPageFragment<*, *>?>(null)

private fun updatePageFragmentLiveData() {
val fragment = supportFragmentManager.primaryNavigationFragment as? CyoaPageFragment<*, *>
if (pageFragmentLiveData.value !== fragment) pageFragmentLiveData.value = fragment
}
Comment on lines +233 to +238

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you shouldn't be storing the fragment in a LiveData. I don't think your current implementation would leak anything, but storing an active fragment like this has potential to leak the fragment if it's not handled carefully.

You should probably just store what page the fragment is for


private fun showInitialPageIfNecessary(manifest: Manifest) {
if (pageFragment != null) return

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue
import kotlinx.coroutines.flow.MutableStateFlow
Expand All @@ -44,6 +45,7 @@ import org.cru.godtools.shared.tool.parser.model.page.backgroundColor
import org.cru.godtools.shared.tool.parser.model.page.backgroundImageGravity
import org.cru.godtools.shared.tool.parser.model.page.backgroundImageScaleType
import org.cru.godtools.tool.cyoa.BuildConfig.HOST_GODTOOLS_CUSTOM_URI
import org.cru.godtools.tool.cyoa.CyoaDeepLink
import org.cru.godtools.tool.cyoa.R
import org.junit.Rule
import org.junit.runner.RunWith
Expand Down Expand Up @@ -737,6 +739,71 @@ class CyoaActivityTest {
}
// endregion Update Manifest

// region Share Link Logic
@Test
fun `Share Link - contains current page`() {
manifestEnglish.value = manifest(listOf(page1, page2))

scenario {
it.onActivity {
it.shareLinkUriLiveData.observeForever { }

assertEquals("https://knowgod.com/en/tool/v2/test/page1?icid=gtshare", it.shareLinkUriLiveData.value)
}
}
}

@Test
fun `Share Link - updates on navigation`() {
manifestEnglish.value = manifest(listOf(page1, page2))

scenario {
it.onActivity {
it.shareLinkUriLiveData.observeForever { }

it.showPage(page2)
it.assertPageStack("page1", "page2")
assertEquals("https://knowgod.com/en/tool/v2/test/page2?icid=gtshare", it.shareLinkUriLiveData.value)

it.onBackPressed()
it.assertPageStack("page1")
assertEquals("https://knowgod.com/en/tool/v2/test/page1?icid=gtshare", it.shareLinkUriLiveData.value)
}
}
}

@Test
fun `Share Link - parseable by CyoaDeepLink`() {
manifestEnglish.value = manifest(listOf(page1, page2))

scenario {
it.onActivity {
it.shareLinkUriLiveData.observeForever { }

it.showPage(page2)
it.assertPageStack("page1", "page2")

val uri = Uri.parse(it.shareLinkUriLiveData.value)
val deepLink = assertNotNull(CyoaDeepLink.parseKnowGodDeepLink(uri))
assertEquals(TOOL, deepLink.tool)
assertEquals("page2", deepLink.page)
assertEquals(Locale.ENGLISH, deepLink.activeLocale)
}
}
}

@Test
fun `Share Link - no active manifest`() {
scenario {
it.onActivity {
it.shareLinkUriLiveData.observeForever { }

assertNull(it.shareLinkUriLiveData.value)
}
}
}
// endregion Share Link Logic

private val CyoaActivity.dataModel get() = viewModels<MultiLanguageToolActivityDataModel>().value

private fun CyoaActivity.assertPageStack(vararg pages: String) {
Expand Down