From 14eb4c7be666d6448c39f503df215c50fab7ea55 Mon Sep 17 00:00:00 2001
From: "HyunWoo Lee (Nunu Lee)" <54518925+l2hyunwoo@users.noreply.github.com>
Date: Tue, 29 Oct 2024 14:58:56 +0900
Subject: [PATCH] =?UTF-8?q?[#948]=20=EC=A3=BC=EC=9A=94=20=ED=99=94?=
=?UTF-8?q?=EB=A9=B4=20DeepLink=20=EC=B2=98=EB=A6=AC=20(#950)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/build.gradle.kts | 6 ++
app/src/main/AndroidManifest.xml | 42 ++++++------
.../official/deeplink/AppDeeplinkModule.kt | 30 +++++++++
.../feature/attendance/AttendanceActivity.kt | 2 +
.../deeplink/DeepLinkSchemeActivity.kt | 67 +++++++++++++++++++
.../official/feature/home/HomeActivity.kt | 3 +-
build-logic/convention/build.gradle.kts | 4 ++
.../sopt/official/plugin/DeeplinkPlugin.kt | 21 ++++++
core/webview/build.gradle.kts | 6 ++
.../webview/deeplink/WebDeeplinkModule.kt | 30 +++++++++
.../official/webview/view/WebViewActivity.kt | 13 +++-
feature/fortune/build.gradle.kts | 6 ++
.../feature/fortune/FortuneActivity.kt | 2 +
.../fortune/deeplink/FortuneDeeplinkModule.kt | 30 +++++++++
gradle/libs.versions.toml | 3 +
15 files changed, 241 insertions(+), 24 deletions(-)
create mode 100644 app/src/main/java/org/sopt/official/deeplink/AppDeeplinkModule.kt
create mode 100644 app/src/main/java/org/sopt/official/feature/deeplink/DeepLinkSchemeActivity.kt
create mode 100644 build-logic/convention/src/main/kotlin/org/sopt/official/plugin/DeeplinkPlugin.kt
create mode 100644 core/webview/src/main/java/org/sopt/official/webview/deeplink/WebDeeplinkModule.kt
create mode 100644 feature/fortune/src/main/java/org/sopt/official/feature/fortune/deeplink/FortuneDeeplinkModule.kt
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 5c5663364..321a4bd46 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -29,6 +29,7 @@ plugins {
sopt("application")
sopt("test")
sopt("compose")
+ sopt("deeplink")
alias(libs.plugins.google.services)
alias(libs.plugins.crashlytics)
alias(libs.plugins.ktlint)
@@ -112,6 +113,11 @@ android {
}
}
+ksp {
+ arg("deepLink.incremental", "true")
+ arg("deepLink.customAnnotations", "com.airbnb.AppDeepLink|com.airbnb.WebDeepLink")
+}
+
dependencies {
implementation(projects.core.designsystem)
implementation(projects.domain.soptamp)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3792c53ca..0533c2d0f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -62,53 +62,55 @@
+ android:exported="true">
+
+
+
+
+
+
+
+
+
+ android:launchMode="singleTop" />
+ android:launchMode="singleTop" />
+ android:launchMode="singleTop" />
+ android:launchMode="singleTop" />
+ android:exported="false" />
+ android:exported="false" />
+ android:exported="false" />
+ android:exported="false" />
+ android:exported="false" />
diff --git a/app/src/main/java/org/sopt/official/deeplink/AppDeeplinkModule.kt b/app/src/main/java/org/sopt/official/deeplink/AppDeeplinkModule.kt
new file mode 100644
index 000000000..e480bbbe0
--- /dev/null
+++ b/app/src/main/java/org/sopt/official/deeplink/AppDeeplinkModule.kt
@@ -0,0 +1,30 @@
+/*
+ * MIT License
+ * Copyright 2024 SOPT - Shout Our Passion Together
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package org.sopt.official.deeplink
+
+import com.airbnb.deeplinkdispatch.DeepLinkModule
+
+@DeepLinkModule
+class AppDeeplinkModule
diff --git a/app/src/main/java/org/sopt/official/feature/attendance/AttendanceActivity.kt b/app/src/main/java/org/sopt/official/feature/attendance/AttendanceActivity.kt
index 606c3bc8a..d63cda2f7 100644
--- a/app/src/main/java/org/sopt/official/feature/attendance/AttendanceActivity.kt
+++ b/app/src/main/java/org/sopt/official/feature/attendance/AttendanceActivity.kt
@@ -45,6 +45,7 @@ import androidx.core.view.isVisible
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
+import com.airbnb.deeplinkdispatch.DeepLink
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -65,6 +66,7 @@ import org.sopt.official.feature.attendance.model.AttendanceState
import org.sopt.official.type.SoptColors
@AndroidEntryPoint
+@DeepLink("sopt://attendance")
class AttendanceActivity : AppCompatActivity() {
private lateinit var binding: ActivityAttendanceBinding
private val viewModel by viewModels()
diff --git a/app/src/main/java/org/sopt/official/feature/deeplink/DeepLinkSchemeActivity.kt b/app/src/main/java/org/sopt/official/feature/deeplink/DeepLinkSchemeActivity.kt
new file mode 100644
index 000000000..660fa88ba
--- /dev/null
+++ b/app/src/main/java/org/sopt/official/feature/deeplink/DeepLinkSchemeActivity.kt
@@ -0,0 +1,67 @@
+/*
+ * MIT License
+ * Copyright 2024 SOPT - Shout Our Passion Together
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package org.sopt.official.feature.deeplink
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import com.airbnb.deeplinkdispatch.DeepLinkHandler
+import dagger.hilt.android.AndroidEntryPoint
+import org.sopt.official.common.navigator.NavigatorProvider
+import org.sopt.official.deeplink.AppDeeplinkModule
+import org.sopt.official.deeplink.AppDeeplinkModuleRegistry
+import org.sopt.official.feature.fortune.deeplink.FortuneDeeplinkModule
+import org.sopt.official.feature.fortune.deeplink.FortuneDeeplinkModuleRegistry
+import org.sopt.official.network.persistence.SoptDataStore
+import org.sopt.official.webview.deeplink.WebDeeplinkModule
+import org.sopt.official.webview.deeplink.WebDeeplinkModuleRegistry
+import javax.inject.Inject
+
+@AndroidEntryPoint
+@DeepLinkHandler(value = [AppDeeplinkModule::class, FortuneDeeplinkModule::class, WebDeeplinkModule::class])
+class DeepLinkSchemeActivity : AppCompatActivity() {
+
+ @Inject
+ lateinit var dataStore: SoptDataStore
+
+ @Inject
+ lateinit var navigator: NavigatorProvider
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ if (dataStore.accessToken.isEmpty()) {
+ startActivity(navigator.getAuthActivityIntent())
+ finish()
+ return
+ }
+
+ DeepLinkDelegate(
+ AppDeeplinkModuleRegistry(),
+ FortuneDeeplinkModuleRegistry(),
+ WebDeeplinkModuleRegistry()
+ ).dispatchFrom(this)
+ finish()
+ }
+}
diff --git a/app/src/main/java/org/sopt/official/feature/home/HomeActivity.kt b/app/src/main/java/org/sopt/official/feature/home/HomeActivity.kt
index a597abfaf..d38ac7d68 100644
--- a/app/src/main/java/org/sopt/official/feature/home/HomeActivity.kt
+++ b/app/src/main/java/org/sopt/official/feature/home/HomeActivity.kt
@@ -29,7 +29,6 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Rect
-import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.view.View
@@ -44,6 +43,7 @@ import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
+import com.airbnb.deeplinkdispatch.DeepLink
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -83,6 +83,7 @@ import java.io.Serializable
import javax.inject.Inject
@AndroidEntryPoint
+@DeepLink("sopt://home")
class HomeActivity : AppCompatActivity() {
private val binding by viewBinding(ActivitySoptMainBinding::inflate)
private val viewModel by viewModels()
diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts
index 0908cf66a..20077b227 100644
--- a/build-logic/convention/build.gradle.kts
+++ b/build-logic/convention/build.gradle.kts
@@ -62,5 +62,9 @@ gradlePlugin {
id = "org.sopt.official.retrofit"
implementationClass = "org.sopt.official.plugin.RetrofitPlugin"
}
+ create("deeplink") {
+ id = "org.sopt.official.deeplink"
+ implementationClass = "org.sopt.official.plugin.DeeplinkPlugin"
+ }
}
}
diff --git a/build-logic/convention/src/main/kotlin/org/sopt/official/plugin/DeeplinkPlugin.kt b/build-logic/convention/src/main/kotlin/org/sopt/official/plugin/DeeplinkPlugin.kt
new file mode 100644
index 000000000..140da83a0
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/org/sopt/official/plugin/DeeplinkPlugin.kt
@@ -0,0 +1,21 @@
+package org.sopt.official.plugin
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.artifacts.VersionCatalogsExtension
+import org.gradle.kotlin.dsl.dependencies
+import org.gradle.kotlin.dsl.getByType
+
+class DeeplinkPlugin : Plugin {
+ override fun apply(target: Project) = with(target) {
+ with(plugins) {
+ apply("com.google.devtools.ksp")
+ }
+
+ val libs = extensions.getByType().named("libs")
+ dependencies {
+ "implementation"(libs.findLibrary("deeplink.dispatch").get())
+ "ksp"(libs.findLibrary("deeplink.dispatch.processor").get())
+ }
+ }
+}
diff --git a/core/webview/build.gradle.kts b/core/webview/build.gradle.kts
index 5711f9b5d..3a4399a1b 100644
--- a/core/webview/build.gradle.kts
+++ b/core/webview/build.gradle.kts
@@ -25,6 +25,7 @@
plugins {
sopt("feature")
+ sopt("deeplink")
}
android {
@@ -34,6 +35,11 @@ android {
}
}
+ksp {
+ arg("deepLink.incremental", "true")
+ arg("deepLink.customAnnotations", "com.airbnb.AppDeepLink|com.airbnb.WebDeepLink")
+}
+
dependencies {
implementation(projects.core.network)
implementation(projects.core.common)
diff --git a/core/webview/src/main/java/org/sopt/official/webview/deeplink/WebDeeplinkModule.kt b/core/webview/src/main/java/org/sopt/official/webview/deeplink/WebDeeplinkModule.kt
new file mode 100644
index 000000000..ee2c9a5c2
--- /dev/null
+++ b/core/webview/src/main/java/org/sopt/official/webview/deeplink/WebDeeplinkModule.kt
@@ -0,0 +1,30 @@
+/*
+ * MIT License
+ * Copyright 2024 SOPT - Shout Our Passion Together
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package org.sopt.official.webview.deeplink
+
+import com.airbnb.deeplinkdispatch.DeepLinkModule
+
+@DeepLinkModule
+class WebDeeplinkModule
diff --git a/core/webview/src/main/java/org/sopt/official/webview/view/WebViewActivity.kt b/core/webview/src/main/java/org/sopt/official/webview/view/WebViewActivity.kt
index e2de64672..700bbd871 100644
--- a/core/webview/src/main/java/org/sopt/official/webview/view/WebViewActivity.kt
+++ b/core/webview/src/main/java/org/sopt/official/webview/view/WebViewActivity.kt
@@ -35,11 +35,13 @@ import androidx.activity.addCallback
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
+import com.airbnb.deeplinkdispatch.DeepLink
import dagger.hilt.android.AndroidEntryPoint
import org.sopt.official.common.util.viewBinding
import org.sopt.official.webview.databinding.ActivityWebViewBinding
@AndroidEntryPoint
+@DeepLink("sopt://web")
class WebViewActivity : AppCompatActivity() {
private val binding by viewBinding(ActivityWebViewBinding::inflate)
private var filePathCallback: ValueCallback>? = null
@@ -79,8 +81,13 @@ class WebViewActivity : AppCompatActivity() {
}
private fun handleLinkUrl() {
- val linkUrl = intent.getStringExtra(INTENT_URL)
- linkUrl?.let { binding.webView.loadUrl(it) }
+ if (intent.getBooleanExtra(DeepLink.IS_DEEP_LINK, false)) {
+ val url = intent.extras?.getString(INTENT_URL) ?: intent.getStringExtra(INTENT_URL) ?: "https://google.com"
+ binding.webView.loadUrl(url)
+ } else {
+ val linkUrl = intent.getStringExtra(INTENT_URL)
+ linkUrl?.let { binding.webView.loadUrl(it) }
+ }
}
private fun handleOnBackPressed() {
@@ -100,6 +107,6 @@ class WebViewActivity : AppCompatActivity() {
}
companion object {
- const val INTENT_URL = "_intent_url"
+ const val INTENT_URL = "url"
}
}
diff --git a/feature/fortune/build.gradle.kts b/feature/fortune/build.gradle.kts
index 5f5e62f91..04566440d 100644
--- a/feature/fortune/build.gradle.kts
+++ b/feature/fortune/build.gradle.kts
@@ -26,12 +26,18 @@ plugins {
sopt("feature")
sopt("compose")
sopt("test")
+ sopt("deeplink")
}
android {
namespace = "org.sopt.official.feature.fortune"
}
+ksp {
+ arg("deepLink.incremental", "true")
+ arg("deepLink.customAnnotations", "com.airbnb.AppDeepLink|com.airbnb.WebDeepLink")
+}
+
dependencies {
// domain
implementation(projects.domain.fortune)
diff --git a/feature/fortune/src/main/java/org/sopt/official/feature/fortune/FortuneActivity.kt b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/FortuneActivity.kt
index 63f175309..7478a50ba 100644
--- a/feature/fortune/src/main/java/org/sopt/official/feature/fortune/FortuneActivity.kt
+++ b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/FortuneActivity.kt
@@ -31,6 +31,7 @@ import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.staticCompositionLocalOf
+import com.airbnb.deeplinkdispatch.DeepLink
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.EntryPointAccessors
import org.sopt.official.analytics.AmplitudeTracker
@@ -51,6 +52,7 @@ internal val LocalAmplitudeTracker = staticCompositionLocalOf
}
@AndroidEntryPoint
+@DeepLink("sopt://fortune")
class FortuneActivity : AppCompatActivity() {
@Inject
diff --git a/feature/fortune/src/main/java/org/sopt/official/feature/fortune/deeplink/FortuneDeeplinkModule.kt b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/deeplink/FortuneDeeplinkModule.kt
new file mode 100644
index 000000000..6b7948ebf
--- /dev/null
+++ b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/deeplink/FortuneDeeplinkModule.kt
@@ -0,0 +1,30 @@
+/*
+ * MIT License
+ * Copyright 2024 SOPT - Shout Our Passion Together
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package org.sopt.official.feature.fortune.deeplink
+
+import com.airbnb.deeplinkdispatch.DeepLinkModule
+
+@DeepLinkModule
+class FortuneDeeplinkModule
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 7279f82a0..804527e01 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -55,6 +55,7 @@ compose-destination = "1.11.7"
coil = "2.7.0"
lottie = "6.6.0"
dotsindicator = "5.1.0"
+deepLinkDispatch = "6.2.2"
google-services = "4.4.2"
crashlytics = "3.0.2"
@@ -180,6 +181,8 @@ constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayo
benchmark-macro-junit4 = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "benchmark-macro-junit4" }
profileinstaller = { group = "androidx.profileinstaller", name = "profileinstaller", version.ref = "profileinstaller" }
dotsindicator = { module = "com.tbuonomo:dotsindicator", version.ref = "dotsindicator" }
+deeplink-dispatch = { group = "com.airbnb", name = "deeplinkdispatch", version.ref = "deepLinkDispatch" }
+deeplink-dispatch-processor = { group = "com.airbnb", name = "deeplinkdispatch-processor", version.ref = "deepLinkDispatch" }
[bundles]
compose = [