Skip to content
Closed
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
23 changes: 23 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
android:screenOrientation="sensor"
android:supportsRtl="true"
android:theme="@style/Theme.App.Starting"
tools:replace="android:allowBackup,android:dataExtractionRules,android:fullBackupContent"
tools:targetApi="tiramisu">
<activity
android:name=".AppActivity"
Expand Down Expand Up @@ -76,6 +77,28 @@
<data android:host="@string/branch_host" />
<data android:host="@string/branch_alternate_host" />
</intent-filter>

<!-- AppsFlyer URI Scheme -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="academyapp" />
</intent-filter>

<!-- AppsFlyer OneLink App Links -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="https" />
<data android:host="go.proanalytics.academy" />
<data android:host="proanalytics.onelink.me" />
</intent-filter>
</activity>

<provider
Expand Down
11 changes: 11 additions & 0 deletions app/src/main/java/org/openedx/app/AppActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.openedx.app.databinding.ActivityAppBinding
import org.openedx.app.deeplink.AppsFlyerDeepLinkHandler
import org.openedx.app.deeplink.DeepLink
import org.openedx.auth.presentation.logistration.LogistrationFragment
import org.openedx.auth.presentation.signin.SignInFragment
Expand Down Expand Up @@ -59,6 +60,7 @@ class AppActivity : AppCompatActivity(), InsetHolder, WindowSizeHolder {
private val profileRouter by inject<ProfileRouter>()
private val downloadDialogManager by inject<DownloadDialogManager>()
private val calendarSyncScheduler by inject<CalendarSyncScheduler>()
private val appsFlyerDeepLinkHandler by inject<AppsFlyerDeepLinkHandler>()

private val branchLogger = Logger(BRANCH_TAG)

Expand Down Expand Up @@ -114,6 +116,7 @@ class AppActivity : AppCompatActivity(), InsetHolder, WindowSizeHolder {
setupInitialFragment(savedInstanceState)
observeLogoutEvent()
observeDownloadFailedDialog()
observeAppsFlyerDeepLink()

calendarSyncScheduler.scheduleDailySync()
}
Expand Down Expand Up @@ -208,6 +211,14 @@ class AppActivity : AppCompatActivity(), InsetHolder, WindowSizeHolder {
}
}

private fun observeAppsFlyerDeepLink() {
lifecycleScope.launch {
appsFlyerDeepLinkHandler.deepLink.collect { deepLink ->
viewModel.makeExternalRoute(supportFragmentManager, deepLink)
}
}
}

override fun onStart() {
super.onStart()

Expand Down
6 changes: 6 additions & 0 deletions app/src/main/java/org/openedx/app/OpenEdXApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import io.branch.referral.Branch
import org.koin.android.ext.android.inject
import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin
import org.openedx.app.deeplink.AppsFlyerDeepLinkHandler
import org.openedx.app.deeplink.BranchBrazeDeeplinkHandler
import org.openedx.app.di.appModule
import org.openedx.app.di.networkingModule
Expand All @@ -20,6 +21,7 @@ class OpenEdXApp : Application() {

private val config by inject<Config>()
private val pluginManager by inject<PluginManager>()
private val appsFlyerDeepLinkHandler by inject<AppsFlyerDeepLinkHandler>()

override fun onCreate() {
super.onCreate()
Expand All @@ -44,6 +46,10 @@ class OpenEdXApp : Application() {
Branch.getAutoInstance(this)
}

if (config.getAppsFlyerConfig().enabled) {
appsFlyerDeepLinkHandler.init(this, config.getAppsFlyerConfig().devKey)
}

if (config.getBrazeConfig().isEnabled && config.getFirebaseConfig().enabled) {
val isCloudMessagingEnabled = config.getFirebaseConfig().isCloudMessagingEnabled &&
config.getBrazeConfig().isPushNotificationsEnabled
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.openedx.app.deeplink

import android.content.Context
import com.appsflyer.AppsFlyerLib
import com.appsflyer.deeplink.DeepLinkListener
import com.appsflyer.deeplink.DeepLinkResult
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import org.json.JSONObject
import org.openedx.core.utils.Logger

class AppsFlyerDeepLinkHandler {

private val logger = Logger(TAG)

private val _deepLink = MutableSharedFlow<DeepLink>(replay = 1, extraBufferCapacity = 1)
val deepLink: SharedFlow<DeepLink> = _deepLink.asSharedFlow()

fun init(context: Context, devKey: String) {
val appsFlyerLib = AppsFlyerLib.getInstance()
appsFlyerLib.subscribeForDeepLink(object : DeepLinkListener {
override fun onDeepLinking(deepLinkResult: DeepLinkResult) {
when (deepLinkResult.status) {
DeepLinkResult.Status.FOUND -> {
logger.i { "AppsFlyer deep link found." }
_deepLink.tryEmit(DeepLink(buildParams(deepLinkResult)))
}

DeepLinkResult.Status.NOT_FOUND -> {
logger.i { "AppsFlyer deep link not found." }
}

DeepLinkResult.Status.ERROR -> {
logger.e { "AppsFlyer deep link resolution failed. Caused by - ${deepLinkResult.error}" }
}
}
}
})
appsFlyerLib.init(devKey, null, context)
appsFlyerLib.start(context)
}

// Defaults every OneLink click to the registration screen unless the OneLink
// template explicitly sets a `screen_name` deep link parameter.
private fun buildParams(deepLinkResult: DeepLinkResult): Map<String, String> {
val clickEvent: JSONObject? = deepLinkResult.deepLink?.clickEvent
val params = mutableMapOf<String, String>()
clickEvent?.keys()?.forEach { key -> params[key] = clickEvent.optString(key) }

if (!params.containsKey(DeepLink.Keys.SCREEN_NAME.value)) {
params[DeepLink.Keys.SCREEN_NAME.value] = DeepLinkType.REGISTER.type
}
return params
}

companion object {
private const val TAG = "AppsFlyer"
}
}
1 change: 1 addition & 0 deletions app/src/main/java/org/openedx/app/deeplink/DeepLink.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ enum class DeepLinkType(val type: String) {
REMOVE_BETA_TESTER("remove_beta_tester"),
FORUM_RESPONSE("forum_response"),
FORUM_COMMENT("forum_comment"),
REGISTER("register"),
NONE("");

companion object {
Expand Down
16 changes: 16 additions & 0 deletions app/src/main/java/org/openedx/app/deeplink/DeepLinkRouter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.openedx.app.AppRouter
import org.openedx.app.MainFragment
import org.openedx.app.R
import org.openedx.auth.presentation.signin.SignInFragment
import org.openedx.auth.presentation.signup.SignUpFragment
import org.openedx.core.FragmentViewType
import org.openedx.core.config.Config
import org.openedx.core.data.storage.CorePreferences
Expand Down Expand Up @@ -41,6 +42,7 @@ class DeepLinkRouter(
DeepLinkType.DISCOVERY -> navigateToDiscoveryScreen(fm)
DeepLinkType.DISCOVERY_COURSE_DETAIL -> navigateToCourseDetail(fm, deepLink)
DeepLinkType.DISCOVERY_PROGRAM_DETAIL -> navigateToProgramDetail(fm, deepLink)
DeepLinkType.REGISTER -> navigateToRegistration(fm)
else -> handleLoggedOutOrUserNavigation(fm, deepLink)
}
}
Expand Down Expand Up @@ -192,6 +194,20 @@ class DeepLinkRouter(
}
}

private fun navigateToRegistration(fm: FragmentManager) {
when {
isUserLoggedIn -> navigateToDashboard(fm)
!config.isRegistrationEnabled() -> navigateToSignIn(fm)
appRouter.getVisibleFragment(fm = fm) !is SignUpFragment -> {
appRouter.navigateToSignUp(
fm = fm,
courseId = null,
infoType = null
)
}
}
}

private fun navigateToCourseDashboard(
fm: FragmentManager,
deepLink: DeepLink,
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/org/openedx/app/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import org.openedx.app.AppRouter
import org.openedx.app.BuildConfig
import org.openedx.app.PluginManager
import org.openedx.app.data.storage.PreferencesManager
import org.openedx.app.deeplink.AppsFlyerDeepLinkHandler
import org.openedx.app.deeplink.DeepLinkRouter
import org.openedx.app.room.AppDatabase
import org.openedx.app.room.DATABASE_NAME
Expand Down Expand Up @@ -129,6 +130,7 @@ val appModule = module {
single<WhatsNewRouter> { get<AppRouter>() }
single<AppUpgradeRouter> { get<AppRouter>() }
single { DeepLinkRouter(get(), get(), get(), get(), get(), get()) }
single { AppsFlyerDeepLinkHandler() }
single<CalendarRouter> { get<AppRouter>() }
single<DownloadsRouter> { get<AppRouter>() }

Expand Down
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ buildscript {

// Third-party library versions
branch_sdk_version = '5.20.0'
appsflyer_sdk_version = '6.17.4'
play_services_ads_identifier_version = '18.2.0'
install_referrer_version = '2.2'
snakeyaml_version = '2.4'
Expand Down
3 changes: 3 additions & 0 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ dependencies {
api "com.google.android.gms:play-services-ads-identifier:$play_services_ads_identifier_version"
api "com.android.installreferrer:installreferrer:$install_referrer_version"

// AppsFlyer SDK Integration
api "com.appsflyer:af-android-sdk:$appsflyer_sdk_version"

// Zip
api "net.lingala.zip4j:zip4j:$zip_version"

Expand Down
11 changes: 11 additions & 0 deletions core/src/main/java/org/openedx/core/config/AppsFlyerConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.openedx.core.config

import com.google.gson.annotations.SerializedName

data class AppsFlyerConfig(
@SerializedName("ENABLED")
val enabled: Boolean = false,

@SerializedName("DEV_KEY")
val devKey: String = "",
)
5 changes: 5 additions & 0 deletions core/src/main/java/org/openedx/core/config/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ class Config(context: Context) {
return getObjectOrNewInstance(BRANCH, BranchConfig::class.java)
}

fun getAppsFlyerConfig(): AppsFlyerConfig {
return getObjectOrNewInstance(APPSFLYER, AppsFlyerConfig::class.java)
}

fun isWhatsNewEnabled(): Boolean {
return getBoolean(WHATS_NEW_ENABLED, false)
}
Expand Down Expand Up @@ -189,6 +193,7 @@ class Config(context: Context) {
private const val DASHBOARD = "DASHBOARD"
private const val EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES"
private const val BRANCH = "BRANCH"
private const val APPSFLYER = "APPSFLYER"
private const val UI_COMPONENTS = "UI_COMPONENTS"
private const val PLATFORM_NAME = "PLATFORM_NAME"
}
Expand Down
4 changes: 4 additions & 0 deletions default_config/dev/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ BRANCH:
HOST: ''
ALTERNATE_HOST: ''

APPSFLYER:
ENABLED: false
DEV_KEY: ''

EXPERIMENTAL_FEATURES:
APP_LEVEL_DOWNLOADS:
ENABLED: false
Expand Down
4 changes: 4 additions & 0 deletions default_config/prod/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ BRANCH:
HOST: ''
ALTERNATE_HOST: ''

APPSFLYER:
ENABLED: false
DEV_KEY: ''

EXPERIMENTAL_FEATURES:
APP_LEVEL_DOWNLOADS:
ENABLED: false
Expand Down
4 changes: 4 additions & 0 deletions default_config/stage/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ BRANCH:
HOST: ''
ALTERNATE_HOST: ''

APPSFLYER:
ENABLED: false
DEV_KEY: ''

EXPERIMENTAL_FEATURES:
APP_LEVEL_DOWNLOADS:
ENABLED: false
Expand Down