From 4b2d12ee7420fb8c85001d2cef8617405c2abe5c Mon Sep 17 00:00:00 2001 From: PoornimaApptentive <85186738+PoornimaApptentive@users.noreply.github.com> Date: Wed, 31 Jul 2024 11:31:36 -0700 Subject: [PATCH] Release/6.8.1 (#34) Releasing 6.8.1 --- .github/workflows/android.yml | 8 +-- CHANGELOG.md | 13 ++++ apptentive-core-test/build.gradle | 4 +- .../src/main/AndroidManifest.xml | 3 +- apptentive-core-ui/build.gradle | 4 +- .../src/main/AndroidManifest.xml | 3 +- apptentive-core/build.gradle | 8 ++- apptentive-core/src/main/AndroidManifest.xml | 3 +- .../com/android/core/ApptentiveException.kt | 6 ++ .../com/android/core/DependencyProvider.kt | 4 +- apptentive-enjoyment-dialog/build.gradle | 4 +- .../src/main/AndroidManifest.xml | 3 +- .../enjoyment/EnjoymentDialogFragment.kt | 7 ++ .../enjoyment/EnjoymentDialogViewModel.kt | 14 +++- apptentive-example/build.gradle | 5 +- .../src/main/AndroidManifest.xml | 3 +- apptentive-feedback-test/build.gradle | 4 +- .../src/main/AndroidManifest.xml | 3 +- apptentive-feedback/build.gradle | 4 +- .../src/main/AndroidManifest.xml | 3 +- .../feedback/ApptentiveDefaultClient.kt | 8 ++- .../com/android/feedback/Constants.kt | 2 +- .../ConversationSerializationException.kt | 6 +- apptentive-in-app-review/build.gradle | 4 +- .../InAppReviewInteractionLauncherTest.kt | 8 +-- .../src/main/AndroidManifest.xml | 3 +- .../src/main/AndroidManifest.xml | 3 +- apptentive-message-center/build.gradle | 4 +- .../src/main/AndroidManifest.xml | 3 +- .../view/BaseMessageCenterActivity.kt | 9 ++- .../view/MessageCenterActivity.kt | 58 ++++++++-------- .../messagecenter/view/MessageListAdapter.kt | 20 ++++-- .../view/custom/SimpleTouchImageView.kt | 7 +- apptentive-navigate-to-link/build.gradle | 4 +- .../src/main/AndroidManifest.xml | 5 +- .../android/feedback/link/LinkNavigator.kt | 4 +- .../link/view/NavigateTolinkActivity.kt | 66 ++++++++++++++++++- apptentive-notes/build.gradle | 4 +- apptentive-notes/src/main/AndroidManifest.xml | 3 +- .../textmodal/TextModalDialogFragment.kt | 3 + .../feedback/textmodal/TextModalViewModel.kt | 26 +++++--- apptentive-ratings/build.gradle | 4 +- .../src/main/AndroidManifest.xml | 3 +- .../ratingdialog/RatingDialogFragment.kt | 7 ++ .../ratingdialog/RatingDialogViewModel.kt | 18 ++++- apptentive-survey/build.gradle | 4 +- .../src/main/AndroidManifest.xml | 3 +- .../feedback/survey/BaseSurveyActivity.kt | 3 + build.gradle | 11 ++-- gradle.properties | 5 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 51 files changed, 292 insertions(+), 124 deletions(-) create mode 100644 apptentive-core/src/main/java/apptentive/com/android/core/ApptentiveException.kt diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 102287dd..6b51b205 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -17,10 +17,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: set up JDK 11 + - name: set up JDK 17 uses: actions/setup-java@v3 with: - java-version: '11' + java-version: '17' distribution: 'temurin' cache: gradle @@ -39,10 +39,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: set up JDK 11 + - name: set up JDK 17 uses: actions/setup-java@v3 with: - java-version: '11' + java-version: '17' distribution: 'temurin' cache: gradle diff --git a/CHANGELOG.md b/CHANGELOG.md index 90400f17..9e3984df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +# 2024-07-31 - v6.8.1 + +### Fixes +* Alchemer Survey file upload now working in in-app mode +* Alchemer Survey video sentiment is now working in in-app mode +* Alchemer Survey title will display as the survey header in in-app mode +* Cache the Message Center's input field values for the next time if not updated + +### Improvements +* Upgraded SDK target to Android 14 (API level 34) +* Upgraded Gradle version to 8.0 +* Prevent the SDK from crashing after the dependencies are garbage collected + # 2024-05-29 - v6.8.0 ### New Features * Advanced Customer Research support to show Alchemer long form surveys through prompts diff --git a/apptentive-core-test/build.gradle b/apptentive-core-test/build.gradle index bdda62e5..d365ab20 100644 --- a/apptentive-core-test/build.gradle +++ b/apptentive-core-test/build.gradle @@ -3,8 +3,7 @@ apply plugin: 'kotlin-android' apply plugin: 'org.jlleitschuh.gradle.ktlint' android { - compileSdkVersion rootProject.compileSdkVersion - buildToolsVersion rootProject.buildToolsVersion + compileSdk rootProject.compileSdkVersion defaultConfig { minSdkVersion rootProject.minSdkVersion @@ -20,6 +19,7 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + namespace 'test.com.test' } dependencies { diff --git a/apptentive-core-test/src/main/AndroidManifest.xml b/apptentive-core-test/src/main/AndroidManifest.xml index 05184c87..94cbbcfc 100644 --- a/apptentive-core-test/src/main/AndroidManifest.xml +++ b/apptentive-core-test/src/main/AndroidManifest.xml @@ -1,2 +1 @@ - + diff --git a/apptentive-core-ui/build.gradle b/apptentive-core-ui/build.gradle index 9cc7321d..d83d1032 100644 --- a/apptentive-core-ui/build.gradle +++ b/apptentive-core-ui/build.gradle @@ -5,8 +5,7 @@ plugins { } android { - compileSdkVersion rootProject.compileSdkVersion - buildToolsVersion rootProject.buildToolsVersion + compileSdk rootProject.compileSdkVersion defaultConfig { minSdkVersion rootProject.minSdkVersion @@ -22,6 +21,7 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + namespace 'apptentive.com.android' } dependencies { diff --git a/apptentive-core-ui/src/main/AndroidManifest.xml b/apptentive-core-ui/src/main/AndroidManifest.xml index a7b04aab..a5918e68 100644 --- a/apptentive-core-ui/src/main/AndroidManifest.xml +++ b/apptentive-core-ui/src/main/AndroidManifest.xml @@ -1,5 +1,4 @@ - + \ No newline at end of file diff --git a/apptentive-core/build.gradle b/apptentive-core/build.gradle index f4aa3fc9..c813ddf7 100644 --- a/apptentive-core/build.gradle +++ b/apptentive-core/build.gradle @@ -1,10 +1,11 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'org.jlleitschuh.gradle.ktlint' android { - compileSdkVersion rootProject.compileSdkVersion - buildToolsVersion rootProject.buildToolsVersion + compileSdk rootProject.compileSdkVersion defaultConfig { minSdkVersion rootProject.minSdkVersion @@ -25,6 +26,7 @@ android { warningsAsErrors true abortOnError true } + namespace 'apptentive.com.android.core' } dependencies { @@ -48,7 +50,7 @@ project.ext { artifactId = 'apptentive-core' } -tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { +tasks.withType(KotlinCompile).configureEach { kotlinOptions { freeCompilerArgs += [ //'-Xexplicit-api=warning', // or '-Xexplicit-api=strict' diff --git a/apptentive-core/src/main/AndroidManifest.xml b/apptentive-core/src/main/AndroidManifest.xml index c5b906e8..91a36692 100644 --- a/apptentive-core/src/main/AndroidManifest.xml +++ b/apptentive-core/src/main/AndroidManifest.xml @@ -1,5 +1,4 @@ - + diff --git a/apptentive-core/src/main/java/apptentive/com/android/core/ApptentiveException.kt b/apptentive-core/src/main/java/apptentive/com/android/core/ApptentiveException.kt new file mode 100644 index 00000000..b9e5dce9 --- /dev/null +++ b/apptentive-core/src/main/java/apptentive/com/android/core/ApptentiveException.kt @@ -0,0 +1,6 @@ +package apptentive.com.android.core + +import apptentive.com.android.util.InternalUseOnly + +@InternalUseOnly +open class ApptentiveException(message: String, cause: Throwable? = null) : Exception(message, cause) diff --git a/apptentive-core/src/main/java/apptentive/com/android/core/DependencyProvider.kt b/apptentive-core/src/main/java/apptentive/com/android/core/DependencyProvider.kt index 562f53e2..2c7f25d2 100644 --- a/apptentive-core/src/main/java/apptentive/com/android/core/DependencyProvider.kt +++ b/apptentive-core/src/main/java/apptentive/com/android/core/DependencyProvider.kt @@ -25,7 +25,9 @@ object DependencyProvider { lookup[T::class.java] != null inline fun of(): T { - val provider = lookup[T::class.java] ?: throw IllegalArgumentException("Provider is not registered: ${T::class.java}") + val provider = lookup[T::class.java] ?: throw MissingProviderException("Provider is not registered: ${T::class.java}") return provider.get() as T } } + +class MissingProviderException(message: String, cause: Throwable? = null) : ApptentiveException(message, cause) diff --git a/apptentive-enjoyment-dialog/build.gradle b/apptentive-enjoyment-dialog/build.gradle index 52fef27d..ac9eb9a2 100644 --- a/apptentive-enjoyment-dialog/build.gradle +++ b/apptentive-enjoyment-dialog/build.gradle @@ -3,8 +3,7 @@ apply plugin: 'kotlin-android' apply plugin: 'org.jlleitschuh.gradle.ktlint' android { - compileSdkVersion rootProject.compileSdkVersion - buildToolsVersion rootProject.buildToolsVersion + compileSdk rootProject.compileSdkVersion defaultConfig { minSdkVersion rootProject.minSdkVersion @@ -20,6 +19,7 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + namespace 'apptentive.com.android.feedback.enjoyment' } dependencies { diff --git a/apptentive-enjoyment-dialog/src/main/AndroidManifest.xml b/apptentive-enjoyment-dialog/src/main/AndroidManifest.xml index 41c98fb0..94cbbcfc 100644 --- a/apptentive-enjoyment-dialog/src/main/AndroidManifest.xml +++ b/apptentive-enjoyment-dialog/src/main/AndroidManifest.xml @@ -1,2 +1 @@ - + diff --git a/apptentive-enjoyment-dialog/src/main/java/apptentive/com/android/feedback/enjoyment/EnjoymentDialogFragment.kt b/apptentive-enjoyment-dialog/src/main/java/apptentive/com/android/feedback/enjoyment/EnjoymentDialogFragment.kt index 2af18cf6..c07d038d 100644 --- a/apptentive-enjoyment-dialog/src/main/java/apptentive/com/android/feedback/enjoyment/EnjoymentDialogFragment.kt +++ b/apptentive-enjoyment-dialog/src/main/java/apptentive/com/android/feedback/enjoyment/EnjoymentDialogFragment.kt @@ -75,6 +75,13 @@ internal class EnjoymentDialogFragment : DialogFragment(), ApptentiveActivityInf } } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + viewModel.dismissInteraction.observe(this) { + dismiss() + } + } + override fun onCancel(dialog: DialogInterface) { viewModel.onCancel() super.onCancel(dialog) diff --git a/apptentive-enjoyment-dialog/src/main/java/apptentive/com/android/feedback/enjoyment/EnjoymentDialogViewModel.kt b/apptentive-enjoyment-dialog/src/main/java/apptentive/com/android/feedback/enjoyment/EnjoymentDialogViewModel.kt index b7caabe4..952d9836 100644 --- a/apptentive-enjoyment-dialog/src/main/java/apptentive/com/android/feedback/enjoyment/EnjoymentDialogViewModel.kt +++ b/apptentive-enjoyment-dialog/src/main/java/apptentive/com/android/feedback/enjoyment/EnjoymentDialogViewModel.kt @@ -2,6 +2,9 @@ package apptentive.com.android.feedback.enjoyment import androidx.lifecycle.ViewModel import apptentive.com.android.core.DependencyProvider +import apptentive.com.android.core.LiveEvent +import apptentive.com.android.core.MissingProviderException +import apptentive.com.android.feedback.engagement.EngagementContext import apptentive.com.android.feedback.engagement.EngagementContextFactory import apptentive.com.android.feedback.engagement.Event import apptentive.com.android.feedback.utils.getInteractionBackup @@ -9,7 +12,14 @@ import apptentive.com.android.util.Log import apptentive.com.android.util.LogTags.INTERACTIONS internal class EnjoymentDialogViewModel : ViewModel() { - private val context = DependencyProvider.of().engagementContext() + val dismissInteraction: LiveEvent = LiveEvent() + private val context: EngagementContext? = try { + DependencyProvider.of().engagementContext() + } catch (exception: MissingProviderException) { + dismissInteraction.postValue(Unit) + Log.e(INTERACTIONS, "EngagementContextFactory is not registered, cannot launch EnjoymentDialogViewModel", exception) + null + } private val interaction: EnjoymentDialogInteraction = try { DependencyProvider.of().getEnjoymentDialogInteraction() @@ -42,7 +52,7 @@ internal class EnjoymentDialogViewModel : ViewModel() { } private fun engageCodePoint(name: String) { - context.executors.state.execute { + context?.executors?.state?.execute { context.engage( event = Event.internal(name, interaction.type), interactionId = interaction.id diff --git a/apptentive-example/build.gradle b/apptentive-example/build.gradle index a9a40cdb..fd4f7c1c 100644 --- a/apptentive-example/build.gradle +++ b/apptentive-example/build.gradle @@ -4,15 +4,16 @@ plugins { } android { - compileSdk 31 + compileSdk 34 defaultConfig { applicationId "com.apptentive.apptentive_example" minSdk 21 - targetSdk 31 + targetSdk 34 versionCode 1 versionName "1.0" } + namespace 'com.apptentive.apptentive_example' } dependencies { diff --git a/apptentive-example/src/main/AndroidManifest.xml b/apptentive-example/src/main/AndroidManifest.xml index 30b7b839..da1df46d 100644 --- a/apptentive-example/src/main/AndroidManifest.xml +++ b/apptentive-example/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + diff --git a/apptentive-message-center/src/main/java/apptentive/com/android/feedback/messagecenter/view/BaseMessageCenterActivity.kt b/apptentive-message-center/src/main/java/apptentive/com/android/feedback/messagecenter/view/BaseMessageCenterActivity.kt index bd41390a..c8fb8f46 100644 --- a/apptentive-message-center/src/main/java/apptentive/com/android/feedback/messagecenter/view/BaseMessageCenterActivity.kt +++ b/apptentive-message-center/src/main/java/apptentive/com/android/feedback/messagecenter/view/BaseMessageCenterActivity.kt @@ -3,6 +3,7 @@ package apptentive.com.android.feedback.messagecenter.view import android.app.Activity import android.os.Bundle import androidx.activity.viewModels +import apptentive.com.android.core.MissingProviderException import apptentive.com.android.feedback.Apptentive import apptentive.com.android.feedback.ApptentiveActivityInfo import apptentive.com.android.feedback.dependencyprovider.createMessageCenterViewModel @@ -25,7 +26,13 @@ open class BaseMessageCenterActivity : ApptentiveViewModelActivity(), Apptentive * and managing messages for [MessageCenterActivity] */ val viewModel: MessageCenterViewModel by viewModels { - ViewModelFactory { createMessageCenterViewModel() } + try { + ViewModelFactory { createMessageCenterViewModel() } + } catch (exception: MissingProviderException) { + throw MissingProviderException("One or more dependency providers are not registered $exception") + } catch (exception: Exception) { + throw IllegalStateException("Issue creating MessageCenterViewModel $exception") + } } override fun onCreate(savedInstanceState: Bundle?) { diff --git a/apptentive-message-center/src/main/java/apptentive/com/android/feedback/messagecenter/view/MessageCenterActivity.kt b/apptentive-message-center/src/main/java/apptentive/com/android/feedback/messagecenter/view/MessageCenterActivity.kt index e915a70e..9d0993a6 100644 --- a/apptentive-message-center/src/main/java/apptentive/com/android/feedback/messagecenter/view/MessageCenterActivity.kt +++ b/apptentive-message-center/src/main/java/apptentive/com/android/feedback/messagecenter/view/MessageCenterActivity.kt @@ -37,6 +37,7 @@ import apptentive.com.android.serialization.json.JsonConverter import apptentive.com.android.ui.hideSoftKeyboard import apptentive.com.android.ui.startViewModelActivity import apptentive.com.android.util.Log +import apptentive.com.android.util.LogTags.MESSAGE_CENTER import apptentive.com.android.util.LogTags.PUSH_NOTIFICATION import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.textview.MaterialTextView @@ -87,32 +88,37 @@ internal class MessageCenterActivity : BaseMessageCenterActivity() { super.onCreate(savedInstanceState) setContentView(R.layout.apptentive_activity_message_center) - rootLayout = findViewById(R.id.apptentive_root) - topAppBar = findViewById(R.id.apptentive_toolbar) - topAppBarTitle = findViewById(R.id.apptentive_message_center_title) - messageText = findViewById(R.id.apptentive_composer_text) - attachmentsLayout = findViewById(R.id.apptentive_composer_attachments_layout) - attachmentButton = findViewById(R.id.apptentive_attachment_button) - sendButton = findViewById(R.id.apptentive_send_message_button) - messageList = findViewById(R.id.apptentive_message_list) - composerErrorView = findViewById(R.id.apptentive_composer_error) - title = viewModel.title - topAppBar.title = "" - topAppBarTitle.text = viewModel.title - messageText.hint = viewModel.composerHint - messageListAdapter = MessageListAdapter(viewModel) - messageList.adapter = messageListAdapter - messageListAdapter.submitList(viewModel.buildMessageViewDataModel()) { - scrollRecyclerToFirstUnreadOrLastItem() - } - messageList.itemAnimator = null + try { + rootLayout = findViewById(R.id.apptentive_root) + topAppBar = findViewById(R.id.apptentive_toolbar) + topAppBarTitle = findViewById(R.id.apptentive_message_center_title) + messageText = findViewById(R.id.apptentive_composer_text) + attachmentsLayout = findViewById(R.id.apptentive_composer_attachments_layout) + attachmentButton = findViewById(R.id.apptentive_attachment_button) + sendButton = findViewById(R.id.apptentive_send_message_button) + messageList = findViewById(R.id.apptentive_message_list) + composerErrorView = findViewById(R.id.apptentive_composer_error) + title = viewModel.title + topAppBar.title = "" + topAppBarTitle.text = viewModel.title + messageText.hint = viewModel.composerHint + messageListAdapter = MessageListAdapter(viewModel) + messageList.adapter = messageListAdapter + messageListAdapter.submitList(viewModel.buildMessageViewDataModel()) { + scrollRecyclerToFirstUnreadOrLastItem() + } + messageList.itemAnimator = null - // SupportActionBar should be set before setting NavigationOnClickListener - setSupportActionBar(topAppBar) + // SupportActionBar should be set before setting NavigationOnClickListener + setSupportActionBar(topAppBar) - addObservers() - setListeners() - getPushNotificationPermission() + addObservers() + setListeners() + getPushNotificationPermission() + } catch (e: Exception) { + Log.e(MESSAGE_CENTER, "Error in onCreate", e) + finish() + } } private fun addObservers() { @@ -205,6 +211,7 @@ internal class MessageCenterActivity : BaseMessageCenterActivity() { } attachmentButton.setOnClickListener { + handleDraftMessage(true) selectImage.launch("image/*") } @@ -272,9 +279,8 @@ internal class MessageCenterActivity : BaseMessageCenterActivity() { } viewModel.addAttachments(draftAttachments) } - // Restore profile view - if (messageListAdapter.isProfileViewVisible()) { + if (viewModel.isProfileViewVisible()) { val name = draftSharedPrefs.getString(MESSAGE_CENTER_PROFILE_NAME, "") val email = draftSharedPrefs.getString(MESSAGE_CENTER_PROFILE_EMAIL, "") messageListAdapter.updateEmail(email) diff --git a/apptentive-message-center/src/main/java/apptentive/com/android/feedback/messagecenter/view/MessageListAdapter.kt b/apptentive-message-center/src/main/java/apptentive/com/android/feedback/messagecenter/view/MessageListAdapter.kt index ffb3f9d8..09f5b6b8 100644 --- a/apptentive-message-center/src/main/java/apptentive/com/android/feedback/messagecenter/view/MessageListAdapter.kt +++ b/apptentive-message-center/src/main/java/apptentive/com/android/feedback/messagecenter/view/MessageListAdapter.kt @@ -31,6 +31,8 @@ internal class MessageListAdapter(private val messageViewModel: MessageCenterVie ListAdapter(DiffCallback()) { private var profileView: ProfileView? = null + private var restoreEmailFromDraft: String = "" + private var restoreNameFromDraft: String = "" companion object { private const val TYPE_HEADER = 0 @@ -51,15 +53,23 @@ internal class MessageListAdapter(private val messageViewModel: MessageCenterVie } fun updateEmail(email: String?) { - email?.let { profileView?.updateEmail(email) } + email?.let { + if (profileView != null) + profileView?.updateEmail(email) + else + restoreEmailFromDraft = email + } } fun updateName(name: String?) { - name?.let { profileView?.updateName(name) } + name?.let { + if (profileView != null) + profileView?.updateName(name) + else + restoreNameFromDraft = name + } } - fun isProfileViewVisible(): Boolean = profileView?.isVisible == true - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val layoutInflater = LayoutInflater.from(parent.context) return when (viewType) { @@ -193,6 +203,8 @@ internal class MessageListAdapter(private val messageViewModel: MessageCenterVie profileView?.setEmailHint(viewData.emailHint) profileView?.setNameHint(viewData.nameHint) profileView?.isVisible = viewData.showProfile + profileView?.updateName(restoreNameFromDraft) + profileView?.updateEmail(restoreEmailFromDraft) } } } diff --git a/apptentive-message-center/src/main/java/apptentive/com/android/feedback/messagecenter/view/custom/SimpleTouchImageView.kt b/apptentive-message-center/src/main/java/apptentive/com/android/feedback/messagecenter/view/custom/SimpleTouchImageView.kt index 30646a06..b89e5502 100644 --- a/apptentive-message-center/src/main/java/apptentive/com/android/feedback/messagecenter/view/custom/SimpleTouchImageView.kt +++ b/apptentive-message-center/src/main/java/apptentive/com/android/feedback/messagecenter/view/custom/SimpleTouchImageView.kt @@ -702,7 +702,12 @@ internal class SimpleTouchImageView @JvmOverloads constructor(context: Context, performLongClick() } - override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean { + override fun onFling( + e1: MotionEvent?, + e2: MotionEvent, + velocityX: Float, + velocityY: Float + ): Boolean { // If a previous fling is still active, it should be cancelled so that two flings // are not run simultaneously. fling?.cancelFling() diff --git a/apptentive-navigate-to-link/build.gradle b/apptentive-navigate-to-link/build.gradle index 7ed8a622..a7b46083 100644 --- a/apptentive-navigate-to-link/build.gradle +++ b/apptentive-navigate-to-link/build.gradle @@ -5,8 +5,7 @@ plugins { } android { - compileSdkVersion rootProject.compileSdkVersion - buildToolsVersion rootProject.buildToolsVersion + compileSdk rootProject.compileSdkVersion defaultConfig { minSdkVersion rootProject.minSdkVersion @@ -22,6 +21,7 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + namespace 'apptentive.com.android.feedback.link' } dependencies { diff --git a/apptentive-navigate-to-link/src/main/AndroidManifest.xml b/apptentive-navigate-to-link/src/main/AndroidManifest.xml index f2643f56..4e098fd6 100644 --- a/apptentive-navigate-to-link/src/main/AndroidManifest.xml +++ b/apptentive-navigate-to-link/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + xmlns:tools="http://schemas.android.com/tools"> @@ -15,7 +15,8 @@ + android:theme="@style/Theme.Apptentive" + android:usesCleartextTraffic = "true" /> \ No newline at end of file diff --git a/apptentive-navigate-to-link/src/main/java/apptentive/com/android/feedback/link/LinkNavigator.kt b/apptentive-navigate-to-link/src/main/java/apptentive/com/android/feedback/link/LinkNavigator.kt index 7b163ed6..dd89e020 100644 --- a/apptentive-navigate-to-link/src/main/java/apptentive/com/android/feedback/link/LinkNavigator.kt +++ b/apptentive-navigate-to-link/src/main/java/apptentive/com/android/feedback/link/LinkNavigator.kt @@ -3,6 +3,7 @@ package apptentive.com.android.feedback.link import android.content.Context import android.content.Intent import android.net.Uri +import android.os.Build import android.os.Bundle import androidx.annotation.MainThread import androidx.annotation.VisibleForTesting @@ -23,7 +24,8 @@ internal object LinkNavigator { context = context, interaction = interaction ) { - if (interaction.target == NavigateToLinkInteraction.Target.self) { + // The web view doesn't have a good support prior to Android P + if (interaction.target == NavigateToLinkInteraction.Target.self && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { context.getAppActivity().startViewModelActivity( extras = Bundle().apply { putString("linkUrl", interaction.url) diff --git a/apptentive-navigate-to-link/src/main/java/apptentive/com/android/feedback/link/view/NavigateTolinkActivity.kt b/apptentive-navigate-to-link/src/main/java/apptentive/com/android/feedback/link/view/NavigateTolinkActivity.kt index 4f897011..ac858c6a 100644 --- a/apptentive-navigate-to-link/src/main/java/apptentive/com/android/feedback/link/view/NavigateTolinkActivity.kt +++ b/apptentive-navigate-to-link/src/main/java/apptentive/com/android/feedback/link/view/NavigateTolinkActivity.kt @@ -1,17 +1,35 @@ package apptentive.com.android.feedback.link.view +import android.content.Intent +import android.net.Uri import android.os.Bundle +import android.webkit.ValueCallback +import android.webkit.WebChromeClient import android.webkit.WebView +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts import apptentive.com.android.feedback.link.R import apptentive.com.android.ui.hideSoftKeyboard +import apptentive.com.android.util.Log +import apptentive.com.android.util.LogTags import com.google.android.material.appbar.MaterialToolbar internal class NavigateTolinkActivity : BaseNavigateToLinkActivity() { private lateinit var webView: WebView + private var uploadMessage: ValueCallback>? = null + private lateinit var fileChooserLauncher: ActivityResultLauncher + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.apptentive_activity_navigate_to_link) + // Register the ActivityResultLauncher + fileChooserLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (uploadMessage == null) return@registerForActivityResult + uploadMessage?.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(result.resultCode, result.data)) + uploadMessage = null + } + supportActionBar?.hide() val topAppBar = findViewById(R.id.apptentive_top_app_bar) @@ -21,7 +39,42 @@ internal class NavigateTolinkActivity : BaseNavigateToLinkActivity() { } webView = findViewById(R.id.apptentive_webview_navigate_to_link) - webView.settings.javaScriptEnabled = true + val settings = webView.settings + settings.javaScriptEnabled = true + settings.domStorageEnabled = true + settings.mediaPlaybackRequiresUserGesture = false + settings.javaScriptCanOpenWindowsAutomatically = true + webView.webChromeClient = object : WebChromeClient() { + override fun onShowFileChooser( + webView: WebView?, + filePathCallback: ValueCallback>?, + fileChooserParams: FileChooserParams? + ): Boolean { + if (uploadMessage != null) { + uploadMessage?.onReceiveValue(null) + uploadMessage = null + } + + Log.i(LogTags.SURVEY, "Detected file upload using alchemer survey") + + uploadMessage = filePathCallback + val intent = fileChooserParams?.createIntent() + try { + fileChooserLauncher.launch(intent) + } catch (e: Exception) { + Log.e(LogTags.SURVEY, "Error launching file chooser", e) + uploadMessage = null + return false + } + return true + } + + override fun onReceivedTitle(view: WebView?, title: String?) { + super.onReceivedTitle(view, title) + title?.let { topAppBar.title = it } + } + } + val url = intent.getStringExtra("linkUrl") if (savedInstanceState != null) { webView.restoreState(savedInstanceState) @@ -33,4 +86,15 @@ internal class NavigateTolinkActivity : BaseNavigateToLinkActivity() { super.onSaveInstanceState(outState) webView.saveState(outState) } + + override fun onDestroy() { + super.onDestroy() + webView.apply { + clearHistory() + clearCache(true) + onPause() + removeAllViews() + destroy() + } + } } diff --git a/apptentive-notes/build.gradle b/apptentive-notes/build.gradle index dd74c629..6647ce13 100644 --- a/apptentive-notes/build.gradle +++ b/apptentive-notes/build.gradle @@ -5,8 +5,7 @@ plugins { } android { - compileSdkVersion rootProject.compileSdkVersion - buildToolsVersion rootProject.buildToolsVersion + compileSdk rootProject.compileSdkVersion defaultConfig { minSdkVersion rootProject.minSdkVersion @@ -22,6 +21,7 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + namespace 'apptentive.com.android.feedback.notes' } dependencies { diff --git a/apptentive-notes/src/main/AndroidManifest.xml b/apptentive-notes/src/main/AndroidManifest.xml index e66744d6..6e5ca417 100644 --- a/apptentive-notes/src/main/AndroidManifest.xml +++ b/apptentive-notes/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + diff --git a/apptentive-notes/src/main/java/apptentive/com/android/feedback/textmodal/TextModalDialogFragment.kt b/apptentive-notes/src/main/java/apptentive/com/android/feedback/textmodal/TextModalDialogFragment.kt index 93350ed9..c4c3ab7f 100644 --- a/apptentive-notes/src/main/java/apptentive/com/android/feedback/textmodal/TextModalDialogFragment.kt +++ b/apptentive-notes/src/main/java/apptentive/com/android/feedback/textmodal/TextModalDialogFragment.kt @@ -139,6 +139,9 @@ internal class TextModalDialogFragment : DialogFragment(), ApptentiveActivityInf override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + viewModel.dismissInteraction.observe(this) { + dismiss() + } viewModel.noteHeaderBitmapStream.observe(this) { bitmap -> setupImage(bitmap) } diff --git a/apptentive-notes/src/main/java/apptentive/com/android/feedback/textmodal/TextModalViewModel.kt b/apptentive-notes/src/main/java/apptentive/com/android/feedback/textmodal/TextModalViewModel.kt index 0e482984..be534f5c 100644 --- a/apptentive-notes/src/main/java/apptentive/com/android/feedback/textmodal/TextModalViewModel.kt +++ b/apptentive-notes/src/main/java/apptentive/com/android/feedback/textmodal/TextModalViewModel.kt @@ -11,6 +11,7 @@ import apptentive.com.android.core.DependencyProvider import apptentive.com.android.core.LiveEvent import apptentive.com.android.feedback.EngagementResult import apptentive.com.android.feedback.PrefetchManager +import apptentive.com.android.feedback.engagement.EngagementContext import apptentive.com.android.feedback.engagement.EngagementContextFactory import apptentive.com.android.feedback.engagement.Event import apptentive.com.android.feedback.engagement.interactions.InteractionResponse @@ -20,7 +21,14 @@ import apptentive.com.android.util.Log import apptentive.com.android.util.LogTags.INTERACTIONS internal class TextModalViewModel : ViewModel() { - private val context = DependencyProvider.of().engagementContext() + val dismissInteraction: LiveEvent = LiveEvent() + private val context: EngagementContext? = try { + DependencyProvider.of().engagementContext() + } catch (exception: Exception) { + Log.e(INTERACTIONS, "Provider is not registered, could not create engagement context", exception) + dismissInteraction.postValue(Unit) + null + } private val interaction: TextModalModel = try { DependencyProvider.of().getTextModalModel() @@ -51,7 +59,7 @@ internal class TextModalViewModel : ViewModel() { title = action.label, callback = { // invoke action - context.executors.state.execute(createActionCallback(action, index)) + context?.executors?.state?.execute(createActionCallback(action, index)) // dismiss UI onDismiss?.invoke() @@ -62,7 +70,7 @@ internal class TextModalViewModel : ViewModel() { title = action.label, callback = { // invoke action - context.executors.state.execute(createActionCallback(action, index)) + context?.executors?.state?.execute(createActionCallback(action, index)) // dismiss UI onDismiss?.invoke() @@ -76,7 +84,7 @@ internal class TextModalViewModel : ViewModel() { private val noteHeaderEvent = LiveEvent() val noteHeaderBitmapStream: LiveData = noteHeaderEvent init { - context.executors.state.execute { + context?.executors?.state?.execute { interaction.richContent?.url?.let { url -> PrefetchManager.getImage(url)?.let { isWiderImage = it.width > MAX_IMAGE_WIDTH @@ -87,7 +95,7 @@ internal class TextModalViewModel : ViewModel() { } fun onCancel() { - context.executors.state.execute { + context?.executors?.state?.execute { engageCodePoint(CODE_POINT_CANCEL) } } @@ -97,7 +105,7 @@ internal class TextModalViewModel : ViewModel() { data: Map? = null, actionId: String? = null ) { - context.engage( + context?.engage( event = Event.internal(codePoint, interaction = "TextModal"), interactionId = interaction.id, data = data, @@ -110,7 +118,7 @@ internal class TextModalViewModel : ViewModel() { private fun createActionCallback(action: TextModalModel.Action, index: Int): Callback = when (action) { is TextModalModel.Action.Dismiss -> { { - context.executors.state.execute { + context?.executors?.state?.execute { Log.i(INTERACTIONS, "Note dismissed") // engage event val data = createEventData(action, index) @@ -120,7 +128,7 @@ internal class TextModalViewModel : ViewModel() { } is TextModalModel.Action.Invoke -> { { - context.executors.state.execute { + context?.executors?.state?.execute { Log.i(INTERACTIONS, "Note action invoked") // run invocation @@ -134,7 +142,7 @@ internal class TextModalViewModel : ViewModel() { } is TextModalModel.Action.Event -> { { - context.executors.state.execute { + context?.executors?.state?.execute { Log.i(INTERACTIONS, "Note event engaged") // engage target event diff --git a/apptentive-ratings/build.gradle b/apptentive-ratings/build.gradle index 8ab09c23..457f6a65 100644 --- a/apptentive-ratings/build.gradle +++ b/apptentive-ratings/build.gradle @@ -5,8 +5,7 @@ plugins { } android { - compileSdkVersion rootProject.compileSdkVersion - buildToolsVersion rootProject.buildToolsVersion + compileSdk rootProject.compileSdkVersion defaultConfig { minSdkVersion rootProject.minSdkVersion @@ -22,6 +21,7 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + namespace 'apptentive.com.android.feedback.ratings' } dependencies { diff --git a/apptentive-ratings/src/main/AndroidManifest.xml b/apptentive-ratings/src/main/AndroidManifest.xml index 62e569de..6e5ca417 100644 --- a/apptentive-ratings/src/main/AndroidManifest.xml +++ b/apptentive-ratings/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + diff --git a/apptentive-ratings/src/main/java/apptentive/com/android/feedback/ratingdialog/RatingDialogFragment.kt b/apptentive-ratings/src/main/java/apptentive/com/android/feedback/ratingdialog/RatingDialogFragment.kt index 63d1f564..6d6a74f8 100644 --- a/apptentive-ratings/src/main/java/apptentive/com/android/feedback/ratingdialog/RatingDialogFragment.kt +++ b/apptentive-ratings/src/main/java/apptentive/com/android/feedback/ratingdialog/RatingDialogFragment.kt @@ -70,6 +70,13 @@ internal class RatingDialogFragment : DialogFragment(), ApptentiveActivityInfo { } } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + viewModel.dismissInteraction.observe(this) { + dismiss() + } + } + override fun onCancel(dialog: DialogInterface) { viewModel.onCancel() super.onCancel(dialog) diff --git a/apptentive-ratings/src/main/java/apptentive/com/android/feedback/ratingdialog/RatingDialogViewModel.kt b/apptentive-ratings/src/main/java/apptentive/com/android/feedback/ratingdialog/RatingDialogViewModel.kt index afe760e9..7f023c04 100644 --- a/apptentive-ratings/src/main/java/apptentive/com/android/feedback/ratingdialog/RatingDialogViewModel.kt +++ b/apptentive-ratings/src/main/java/apptentive/com/android/feedback/ratingdialog/RatingDialogViewModel.kt @@ -2,6 +2,9 @@ package apptentive.com.android.feedback.ratingdialog import androidx.lifecycle.ViewModel import apptentive.com.android.core.DependencyProvider +import apptentive.com.android.core.LiveEvent +import apptentive.com.android.core.MissingProviderException +import apptentive.com.android.feedback.engagement.EngagementContext import apptentive.com.android.feedback.engagement.EngagementContextFactory import apptentive.com.android.feedback.engagement.Event import apptentive.com.android.feedback.utils.getInteractionBackup @@ -9,7 +12,18 @@ import apptentive.com.android.util.Log import apptentive.com.android.util.LogTags.INTERACTIONS internal class RatingDialogViewModel : ViewModel() { - private val context = DependencyProvider.of().engagementContext() + val dismissInteraction = LiveEvent() + private val context: EngagementContext? = try { + DependencyProvider.of().engagementContext() + } catch (exception: MissingProviderException) { + dismissInteraction.postValue(Unit) + Log.e( + INTERACTIONS, + "EngagementContextFactory is not registered, cannot launch RatingDialogViewModel", + exception + ) + null + } private val interaction: RatingDialogInteraction = try { DependencyProvider.of().getRatingDialogInteraction() } catch (exception: Exception) { @@ -40,7 +54,7 @@ internal class RatingDialogViewModel : ViewModel() { } private fun engageCodePoint(name: String) { - context.executors.state.execute { + context?.executors?.state?.execute { context.engage( event = Event.internal(name, interaction.type), interactionId = interaction.id diff --git a/apptentive-survey/build.gradle b/apptentive-survey/build.gradle index 88f68321..69fb8812 100644 --- a/apptentive-survey/build.gradle +++ b/apptentive-survey/build.gradle @@ -5,8 +5,7 @@ plugins { } android { - compileSdkVersion rootProject.compileSdkVersion - buildToolsVersion rootProject.buildToolsVersion + compileSdk rootProject.compileSdkVersion defaultConfig { minSdkVersion rootProject.minSdkVersion @@ -22,6 +21,7 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + namespace 'apptentive.com.android.feedback.survey' } dependencies { diff --git a/apptentive-survey/src/main/AndroidManifest.xml b/apptentive-survey/src/main/AndroidManifest.xml index 32c6029d..29871a5e 100644 --- a/apptentive-survey/src/main/AndroidManifest.xml +++ b/apptentive-survey/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - +