Skip to content

Commit

Permalink
MBL-1861: Fix survey notifications not working & MBL-1862: Add suppor…
Browse files Browse the repository at this point in the history
…t for survey url to deeplinks (#2168)
  • Loading branch information
leighdouglas authored Nov 14, 2024
1 parent 3cedeab commit 81c088c
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.kickstarter.R
import com.kickstarter.libs.DeviceRegistrarType
import com.kickstarter.libs.PushNotifications
import com.kickstarter.models.Activity
import com.kickstarter.models.Urls
import com.kickstarter.models.pushdata.GCM
import com.kickstarter.services.apiresponses.PushNotificationEnvelope
import javax.inject.Inject
Expand Down Expand Up @@ -217,8 +218,7 @@ class DebugPushNotificationsView @JvmOverloads constructor(context: Context, att
val envelope: PushNotificationEnvelope = PushNotificationEnvelope.builder()
.gcm(gcm)
.survey(
PushNotificationEnvelope.Survey.builder()
.id(18249859L)
PushNotificationEnvelope.Survey.builder().id(18249859L).urls(PushNotificationEnvelope.Urls.builder().web(PushNotificationEnvelope.Web.builder().survey("/projects/:creator_param/:project_param/backing/survey_responses").build()).build())
.projectId(PROJECT_ID)
.build()
)
Expand Down
18 changes: 6 additions & 12 deletions app/src/main/java/com/kickstarter/libs/PushNotifications.kt
Original file line number Diff line number Diff line change
Expand Up @@ -139,19 +139,13 @@ class PushNotifications(
subscriptions.add(
notifications
.filter { obj: PushNotificationEnvelope -> obj.isSurvey() }
.flatMap { envelope: PushNotificationEnvelope ->
this.fetchSurveyResponseWithEnvelope(
envelope
)
}
.filter {
isNotNull()
}
.observeOn(Schedulers.newThread())
.subscribe { envelopeAndSurveyResponse: Pair<PushNotificationEnvelope, SurveyResponse> ->
.subscribe {
this.displayNotificationFromSurveyResponseActivity(
envelopeAndSurveyResponse.first,
envelopeAndSurveyResponse.second
it
)
}
)
Expand Down Expand Up @@ -327,14 +321,14 @@ class PushNotifications(

private fun displayNotificationFromSurveyResponseActivity(
envelope: PushNotificationEnvelope,
surveyResponse: SurveyResponse
) {
val gcm = envelope.gcm()

val survey = envelope.survey() ?: return
val surveyUrlPath = envelope.survey()?.urls()?.web()?.survey() ?: return

val notification = notificationBuilder(gcm.title(), gcm.alert(), CHANNEL_SURVEY)
.setContentIntent(surveyResponseContentIntent(envelope, surveyResponse))
.setContentIntent(surveyResponseContentIntent(envelope, surveyUrlPath))
.build()
notificationManager().notify(envelope.signature(), notification)
}
Expand Down Expand Up @@ -441,12 +435,12 @@ class PushNotifications(

private fun surveyResponseContentIntent(
envelope: PushNotificationEnvelope,
surveyResponse: SurveyResponse
surveyUrlPath: String
): PendingIntent {
val activityFeedIntent = Intent(this.context, ActivityFeedActivity::class.java)

val surveyResponseIntent = Intent(this.context, SurveyResponseActivity::class.java)
.putExtra(IntentKey.SURVEY_RESPONSE, surveyResponse)
.putExtra(IntentKey.NOTIFICATION_SURVEY_RESPONSE, surveyUrlPath)

val taskStackBuilder = TaskStackBuilder.create(this.context)
.addNextIntentWithParentStack(activityFeedIntent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ fun Uri.isProjectSurveyUri(webEndpoint: String): Boolean {
PROJECT_SURVEY.matcher(path()).matches() || PROJECT_SURVEY_EDIT.matcher(path())
.matches() || PROJECT_SURVEY_EDIT_ADDRESS.matcher(path())
.matches() || PROJECT_SURVEY_BACKING_REDEEM.matcher(path())
.matches() || PROJECT_SURVEY_RESPONSE.matcher(path())
.matches() || PROJECT_SURVEY_BACKING_PLEDGE_REDEMPTION.matcher(path()).matches()
)
}
Expand Down Expand Up @@ -259,6 +260,11 @@ private val PROJECT_SURVEY_EDIT_ADDRESS = Pattern.compile(
"\\A\\/projects(\\/[a-zA-Z0-9_-]+)?\\/[a-zA-Z0-9_-]+\\/surveys\\/[a-zA-Z0-9-_]+\\/edit_address\\z"
)

// /projects/:creator_param/:project_param/backing/survey_responses
private val PROJECT_SURVEY_RESPONSE = Pattern.compile(
"\\A\\/projects(\\/[a-zA-Z0-9_-]+)?\\/[a-zA-Z0-9_-]+\\/backing\\/survey_responses\\z"
)

// /projects/:creator_param/:project_param/backing/pledge_redemption
private val PROJECT_SURVEY_BACKING_PLEDGE_REDEMPTION = Pattern.compile(
"\\A\\/projects(\\/[a-zA-Z0-9_-]+)?\\/[a-zA-Z0-9_-]+\\/backing\\/pledge_redemption\\z"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.kickstarter.services.apiresponses

import android.os.Parcelable
import com.kickstarter.models.SurveyResponse.Urls
import com.kickstarter.models.SurveyResponse.Urls.Web
import com.kickstarter.models.pushdata.Activity
import com.kickstarter.models.pushdata.GCM
import kotlinx.parcelize.Parcelize
Expand Down Expand Up @@ -212,21 +214,27 @@ class PushNotificationEnvelope private constructor(
@Parcelize
class Survey private constructor(
private val id: Long,
private val projectId: Long
private val projectId: Long,
private val urls: Urls?
) : Parcelable {
fun id() = this.id
fun projectId() = this.projectId
fun urls() = this.urls

@Parcelize
data class Builder(
private var id: Long = 0L,
private var projectId: Long = 0L
private var projectId: Long = 0L,
private var urls: Urls? = null

) : Parcelable {
fun id(id: Long) = apply { this.id = id }
fun projectId(projectId: Long) = apply { this.projectId = projectId }
fun urls(urls: Urls?) = apply { urls?.let { this.urls = it } }
fun build() = Survey(
id = id,
projectId = projectId
projectId = projectId,
urls = urls
)
}

Expand All @@ -252,6 +260,76 @@ class PushNotificationEnvelope private constructor(
}
}

@Parcelize
class Urls private constructor(
private val web: Web
) : Parcelable {
fun web() = this.web

@Parcelize
data class Builder(
private var web: Web = Web.builder()
.build()
) : Parcelable {
fun web(web: Web?) = apply { this.web = web ?: Web.builder().build() }

fun build() = Urls(
web = web
)
}

fun toBuilder() = Builder(
web = web,
)

companion object {
@JvmStatic
fun builder() = Builder()
}

override fun equals(obj: Any?): Boolean {
var equals = super.equals(obj)
if (obj is Urls) {
equals = web() == obj.web()
}
return equals
}
}

@Parcelize
class Web private constructor(
private val survey: String?
) : Parcelable {
fun survey() = this.survey

@Parcelize
data class Builder(
private var survey: String? = null
) : Parcelable {
fun survey(survey: String?) = apply { this.survey = survey }
fun build() = Web(
survey = survey
)
}

fun toBuilder() = Builder(
survey = survey
)

companion object {
@JvmStatic
fun builder() = Builder()
}

override fun equals(obj: Any?): Boolean {
var equals = super.equals(obj)
if (obj is Web) {
equals = survey() == obj.survey()
}
return equals
}
}

companion object {
private val PROJECT_NOTIFICATION_CATEGORIES = listOf(
com.kickstarter.models.Activity.CATEGORY_BACKING,
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/com/kickstarter/ui/IntentKey.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ object IntentKey {
const val PUSH_TOKEN = "com.kickstarter.kickstarter.intent_push_token"
const val REF_TAG = "com.kickstarter.kickstarter.ref_tag"
const val SURVEY_RESPONSE = "com.kickstarter.kickstarter.survey_response"
const val NOTIFICATION_SURVEY_RESPONSE = "com.kickstarter.kickstarter.survey_response"
const val DEEPLINK_SURVEY_RESPONSE = "com.kickstarter.kickstarter.deeplink_survey_response"
const val TOOLBAR_TITLE = "com.kickstarter.kickstarter.intent_toolbar_title"
const val TRACKING_CLIENT_TYPE_TAG = "com.kickstarter.kickstarter.intent_tracking_client_tag"
const val UPDATE = "com.kickstarter.kickstarter.intent_update"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ class DeepLinkActivity : AppCompatActivity() {
.build()
).build()
if (isLoggedIn) {
startSurveyResponseActivity(surveyResponse)
startSurveyResponseActivity(uri.toString())
} else {
startLoginForSurveys(surveyResponse)
startLoginForSurveys(uri.toString())
}
}.addToDisposable(disposables)
}
Expand Down Expand Up @@ -214,17 +214,17 @@ class DeepLinkActivity : AppCompatActivity() {
finish()
}

private fun startLoginForSurveys(surveyResponse: SurveyResponse) {
private fun startLoginForSurveys(surveyResponseUrl: String) {
val intent = Intent(this, LoginToutActivity::class.java)
.putExtra(IntentKey.LOGIN_REASON, LoginReason.DEFAULT)
.putExtra(IntentKey.SURVEY_RESPONSE, surveyResponse)
.putExtra(IntentKey.DEEPLINK_SURVEY_RESPONSE, surveyResponseUrl)
startActivityForResult(intent, ActivityRequestCodes.LOGIN_FLOW)
}

private fun startSurveyResponseActivity(surveyResponse: SurveyResponse) {
private fun startSurveyResponseActivity(surveyResponseUrl: String) {
ApplicationUtils.startNewDiscoveryActivity(this)
val intent = Intent(this, SurveyResponseActivity::class.java)
.putExtra(IntentKey.SURVEY_RESPONSE, surveyResponse)
.putExtra(IntentKey.DEEPLINK_SURVEY_RESPONSE, surveyResponseUrl)
startActivity(intent)
finish()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import com.kickstarter.libs.utils.extensions.getEnvironment
import com.kickstarter.libs.utils.extensions.getResetPasswordIntent
import com.kickstarter.libs.utils.extensions.isNotNull
import com.kickstarter.libs.utils.extensions.showAlertDialog
import com.kickstarter.models.SurveyResponse
import com.kickstarter.models.chrome.ChromeTabsHelper
import com.kickstarter.services.apiresponses.ErrorEnvelope.FacebookUser
import com.kickstarter.ui.IntentKey
Expand Down Expand Up @@ -331,16 +330,17 @@ class LoginToutActivity : ComponentActivity() {
}

private fun goToSurveyIfSurveyPresent() {
val surveyResponse = IntentCompat.getParcelableExtra(intent, IntentKey.SURVEY_RESPONSE, SurveyResponse::class.java)
surveyResponse?.let {
startSurveyResponseActivity(surveyResponse)
val surveyResponseDeeplink = IntentCompat.getParcelableExtra(intent, IntentKey.DEEPLINK_SURVEY_RESPONSE, String::class.java)

surveyResponseDeeplink?.let {
startSurveyResponseActivity(surveyResponseDeeplink)
}
}

private fun startSurveyResponseActivity(surveyResponse: SurveyResponse) {
private fun startSurveyResponseActivity(surveyResponseUrl: String) {
ApplicationUtils.startNewDiscoveryActivity(this)
val intent = Intent(this, SurveyResponseActivity::class.java)
.putExtra(IntentKey.SURVEY_RESPONSE, surveyResponse)
.putExtra(IntentKey.DEEPLINK_SURVEY_RESPONSE, surveyResponseUrl)
startActivity(intent)
finish()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ package com.kickstarter.viewmodels

import android.content.Intent
import android.util.Pair
import androidx.core.net.toUri
import androidx.lifecycle.ViewModelProvider
import com.kickstarter.libs.Environment
import com.kickstarter.libs.rx.transformers.Transformers
import com.kickstarter.libs.utils.UrlUtils
import com.kickstarter.libs.utils.extensions.addToDisposable
import com.kickstarter.libs.utils.extensions.isNotNull
import com.kickstarter.libs.utils.extensions.path
import com.kickstarter.models.SurveyResponse
import com.kickstarter.ui.IntentKey
import io.reactivex.Observable
Expand Down Expand Up @@ -63,23 +67,47 @@ interface SurveyResponseViewModel {
private val disposables = CompositeDisposable()

init {
val surveyResponse = intent()
.map<Any?> { it.getParcelableExtra(IntentKey.SURVEY_RESPONSE) }
.ofType(SurveyResponse::class.java)

val surveyWebUrl = surveyResponse
val surveyActivityUrl = intent()
.filter {
it.hasExtra(IntentKey.SURVEY_RESPONSE) && it.getParcelableExtra<SurveyResponse>(IntentKey.SURVEY_RESPONSE).isNotNull()
}
.map { requireNotNull(it.getParcelableExtra(IntentKey.SURVEY_RESPONSE)) }
.ofType(SurveyResponse::class.java)
.map {
it.urls()?.web()?.survey() ?: ""
}

surveyWebUrl
.subscribe { webViewUrl.onNext(it) }
val surveyNotificationUrl = intent()
.filter {
it.hasExtra(IntentKey.NOTIFICATION_SURVEY_RESPONSE) && !it.getStringExtra(IntentKey.NOTIFICATION_SURVEY_RESPONSE)
.isNullOrEmpty()
}
.map { requireNotNull(it.getStringExtra(IntentKey.NOTIFICATION_SURVEY_RESPONSE)) }
.ofType(String::class.java)
.map { UrlUtils.appendPath(environment.webEndpoint(), it) }

val surveyDeeplinkUrl = intent()
.filter {
it.hasExtra(IntentKey.DEEPLINK_SURVEY_RESPONSE) && !it.getStringExtra(IntentKey.DEEPLINK_SURVEY_RESPONSE)
.isNullOrEmpty()
}
.map { requireNotNull(it.getStringExtra(IntentKey.DEEPLINK_SURVEY_RESPONSE)) }
.ofType(String::class.java)
.map { environment.webEndpoint() + it.toUri().path() }

val surveyUrl = Observable.merge(surveyActivityUrl, surveyNotificationUrl, surveyDeeplinkUrl)

surveyUrl
.subscribe {
webViewUrl.onNext(it)
}
.addToDisposable(disposables)

val projectRequestAndSurveyUrl =
Observable.combineLatest<Request, String?, Pair<Request, String>>(
projectUriRequest,
surveyWebUrl
surveyUrl
) { a: Request?, b: String? -> Pair.create(a, b) }

projectRequestAndSurveyUrl
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,32 @@ class SurveyResponseViewModelTest : KSRobolectricTestCase() {
}

@Test
fun testWebViewUrl() {
fun `open webview when survey is opened from activity feed`() {
val surveyResponse = surveyResponse()

setUpEnvironment(environment(), Intent().putExtra(IntentKey.SURVEY_RESPONSE, surveyResponse))

webViewUrl.assertValues(surveyResponse.urls()?.web()?.survey())
}

@Test
fun `open webview when survey is opened from notification and url contains host`() {
val surveyUrlPath = "projects/1231313/test-project-notification/backing/survey_responses"

setUpEnvironment(environment().toBuilder().webEndpoint("www.test.dev/").build(), Intent().putExtra(IntentKey.NOTIFICATION_SURVEY_RESPONSE, surveyUrlPath))

webViewUrl.assertValues("www.test.dev/projects/1231313/test-project-notification/backing/survey_responses")
}

@Test
fun `open webview when survey is opened from deeplink`() {
val surveyUrlPath = "http://www.test.com/projects/1231313/test-project-deeplink/backing/survey_responses"

setUpEnvironment(environment().toBuilder().webEndpoint("www.kickstarter.com").build(), Intent().putExtra(IntentKey.DEEPLINK_SURVEY_RESPONSE, surveyUrlPath))

webViewUrl.assertValues("www.kickstarter.com/projects/1231313/test-project-deeplink/backing/survey_responses")
}

@After
fun clear() {
disposables.clear()
Expand Down

0 comments on commit 81c088c

Please sign in to comment.