From 43dfc23f864bad4f82c8d20c80934765c881bca0 Mon Sep 17 00:00:00 2001 From: A117870935 Date: Thu, 18 May 2023 18:03:56 +0530 Subject: [PATCH] Tealium and Adjust SDKs added from following branches: feature/NMCLOUD-339 feature/NMCLOUD-428 NMC-3897 -- Adjust v5 migration from v4 --- app/build.gradle.kts | 15 +- app/src/main/AndroidManifest.xml | 11 ++ .../nextcloud/client/di/ActivityInjector.kt | 7 +- .../client/preferences/AppPreferences.java | 18 +++ .../preferences/AppPreferencesImpl.java | 25 +++ .../android/marketTracking/AdjustSdkUtils.kt | 76 ++++++++++ .../android/marketTracking/TealiumSdkUtils.kt | 107 +++++++++++++ .../marketTracking/TrackingScanInterface.kt | 14 ++ .../TrackingScanInterfaceImpl.kt | 18 +++ .../com/nmc/android/ui/PrivacyUserAction.kt | 9 ++ .../java/com/owncloud/android/MainApp.java | 13 ++ .../authentication/AuthenticatorActivity.kt | 12 ++ .../ui/activity/FileDisplayActivity.kt | 4 + .../android/ui/activity/SettingsActivity.java | 8 + .../android/ui/dialog/AccountRemovalDialog.kt | 143 +++--------------- .../android/ui/fragment/FileDetailFragment.kt | 3 + .../ui/fragment/FileDetailSharingFragment.kt | 10 ++ .../fragment/OCFileListBottomSheetDialog.kt | 10 ++ .../android/ui/fragment/OCFileListFragment.kt | 40 ++++- .../fragment/autoUpload/AutoUploadFragment.kt | 4 + .../handler/AutoUploadClickHandler.kt | 23 +++ app/src/main/res/values-de/strings.xml | 3 +- app/src/main/res/values/strings.xml | 3 +- gradle.properties | 2 +- gradle/libs.versions.toml | 10 ++ settings.gradle.kts | 2 + 26 files changed, 460 insertions(+), 130 deletions(-) create mode 100644 app/src/main/java/com/nmc/android/marketTracking/AdjustSdkUtils.kt create mode 100644 app/src/main/java/com/nmc/android/marketTracking/TealiumSdkUtils.kt create mode 100644 app/src/main/java/com/nmc/android/marketTracking/TrackingScanInterface.kt create mode 100644 app/src/main/java/com/nmc/android/marketTracking/TrackingScanInterfaceImpl.kt create mode 100644 app/src/main/java/com/nmc/android/ui/PrivacyUserAction.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c822052f1d13..6dbf16708c3b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -79,6 +79,7 @@ val ndkEnv = buildMap { } } +val adjustAppToken = project.properties["ADJUST_APP_TOKEN"] val ncTestServerUsername = providers.gradleProperty("NC_TEST_SERVER_USERNAME").get() val ncTestServerPassword = providers.gradleProperty("NC_TEST_SERVER_PASSWORD").get() val ncTestServerBaseUrl = providers.gradleProperty("NC_TEST_SERVER_BASEURL").get() @@ -116,7 +117,8 @@ android { buildConfigField("boolean", "CI", ciBuild.toString()) buildConfigField("boolean", "RUNTIME_PERF_ANALYSIS", perfAnalysis.toString()) - + // NMC Customization: adjust token + buildConfigField("String", "ADJUST_APP_TOKEN", adjustAppToken.toString()) // arguments to be passed to functional tests testInstrumentationRunner = if (shotTest) "com.karumi.shot.ShotTestRunner" else "com.nextcloud.client.TestRunner" @@ -517,6 +519,14 @@ dependencies { implementation(libs.kotlinx.coroutines.core) // endregion + // NMC region + // Adjust SDK --> https://github.com/adjust/android_sdk + implementation(libs.adjust.android) + implementation(libs.installreferrer) + // tealium sdk + implementation(libs.tealium.library) + // endregion + // region Google Play dependencies, upon each update first test: new registration, receive push add(gplayImplementationConfiguration, libs.bundles.gplay) // endregion @@ -532,4 +542,7 @@ dependencies { // kotlinx.serialization implementation(libs.kotlinx.serialization.json) + + // NMC: dependency required to capture Advertising ID for Adjust & MoEngage SDK + implementation(libs.play.services.ads.identifier) } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1f1c5a60abbe..0670765710e8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -662,6 +662,17 @@ android:name="com.nextcloud.client.etm.EtmActivity" android:exported="false" android:theme="@style/Theme.ownCloud.Toolbar" /> + + + + + + + + androidInjector() { return dispatchingAndroidInjector; } + //NMC Customization + private void initMarketTrackingSdks(){ + TealiumSdkUtils.initialiseTealiumSDK(this); + AdjustSdkUtils.initialiseAdjustSDK(this); + } + public static void setAppTheme(DarkMode mode) { switch (mode) { case LIGHT -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); diff --git a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.kt b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.kt index 2ef83e482af3..7698e953090e 100644 --- a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.kt +++ b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.kt @@ -60,6 +60,8 @@ import com.nextcloud.repository.ClientRepository import com.nextcloud.utils.SnackbarUtil import com.nextcloud.utils.extensions.getParcelableArgument import com.nextcloud.utils.mdm.MDMConfig +import com.nmc.android.marketTracking.AdjustSdkUtils +import com.nmc.android.marketTracking.TealiumSdkUtils import com.nextcloud.utils.text.LinkFormatter import com.owncloud.android.MainApp import com.owncloud.android.R @@ -970,6 +972,10 @@ open class AuthenticatorActivity : } private fun endSuccess() { + // NMC: track successful login event + AdjustSdkUtils.trackEvent(AdjustSdkUtils.EVENT_TOKEN_SUCCESSFUL_LOGIN, preferences) + TealiumSdkUtils.trackEvent(TealiumSdkUtils.EVENT_SUCCESSFUL_LOGIN, preferences) + if (!onlyAdd) { startActivity(postLoginIntent()) } @@ -1117,6 +1123,12 @@ open class AuthenticatorActivity : return false // always return false to grant that the software keyboard is hidden anyway } + override fun onStart() { + super.onStart() + // track screen view when activity is visible + TealiumSdkUtils.trackView(TealiumSdkUtils.SCREEN_VIEW_LOGIN, preferences) + } + // region Operations service private fun doOnResumeAndBound() { val binder = operationsServiceBinder ?: return diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index ff83034b36db..4414c00a4ff6 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -76,6 +76,7 @@ import com.nextcloud.utils.extensions.lastFragment import com.nextcloud.utils.extensions.navigateToAllFiles import com.nextcloud.utils.extensions.setVisibleIf import com.nextcloud.utils.view.FastScrollUtils +import com.nmc.android.marketTracking.TealiumSdkUtils import com.owncloud.android.MainApp import com.owncloud.android.R import com.owncloud.android.authentication.PassCodeManager @@ -583,6 +584,9 @@ class FileDisplayActivity : saveLastDisplayedAccountName(accountName) EventBus.getDefault().post(TokenPushEvent()) checkForNewDevVersionNecessary(applicationContext) + + // NMC: track screen view when activity is visible + TealiumSdkUtils.trackView(TealiumSdkUtils.SCREEN_VIEW_FILE_BROWSER, preferences) } private fun resolveInitialFile(storageManager: FileDataStorageManager): OCFile? = diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java index 6580a0119b24..d7be826ffa70 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java @@ -50,6 +50,7 @@ import com.nextcloud.client.network.ClientFactory; import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.client.preferences.AppPreferences; +import com.nmc.android.marketTracking.TealiumSdkUtils; import com.nextcloud.client.preferences.AppPreferencesImpl; import com.nextcloud.client.preferences.DarkMode; import com.nextcloud.client.utils.IntentUtil; @@ -1237,6 +1238,13 @@ protected void onPostCreate(Bundle savedInstanceState) { getDelegate().onPostCreate(savedInstanceState); } + @Override + protected void onStart() { + super.onStart(); + //track screen view when activity is visible + TealiumSdkUtils.trackView(TealiumSdkUtils.SCREEN_VIEW_SETTINGS, preferences); + } + @Override protected void onDestroy() { super.onDestroy(); diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/AccountRemovalDialog.kt b/app/src/main/java/com/owncloud/android/ui/dialog/AccountRemovalDialog.kt index ab5db8acb154..76b80c09d5ca 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/AccountRemovalDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/AccountRemovalDialog.kt @@ -9,33 +9,24 @@ package com.owncloud.android.ui.dialog import android.app.Dialog -import android.graphics.drawable.Drawable +import android.content.DialogInterface import android.os.Bundle -import android.view.View -import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.nextcloud.client.account.User -import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.di.Injectable import com.nextcloud.client.jobs.BackgroundJobManager import com.nextcloud.client.player.media3.PlaybackModel -import com.nextcloud.client.utils.IntentUtil -import com.nextcloud.utils.avatar.AvatarGenerationListener -import com.nextcloud.utils.avatar.AvatarGenerator +import com.nextcloud.client.preferences.AppPreferences +import com.nmc.android.marketTracking.AdjustSdkUtils +import com.nmc.android.marketTracking.TealiumSdkUtils import com.nextcloud.utils.extensions.getParcelableArgument -import com.nextcloud.utils.text.LinkFormatter import com.owncloud.android.R -import com.owncloud.android.databinding.AccountRemovalDialogBinding -import com.owncloud.android.datamodel.FileDataStorageManager -import com.owncloud.android.ui.dialog.extensions.themeButtons import com.owncloud.android.utils.theme.ViewThemeUtils import javax.inject.Inject -class AccountRemovalDialog : - DialogFragment(), - AvatarGenerationListener, - Injectable { +// NMC Customization: We don't need two option for logout. On logout directly logout the user locally from the app +class AccountRemovalDialog : DialogFragment(), Injectable { @Inject lateinit var backgroundJobManager: BackgroundJobManager @@ -47,134 +38,40 @@ class AccountRemovalDialog : lateinit var playbackModel: PlaybackModel @Inject - lateinit var avatarGenerator: AvatarGenerator + lateinit var appPreferences: AppPreferences private var user: User? = null - private lateinit var alertDialog: AlertDialog - private var _binding: AccountRemovalDialogBinding? = null - val binding get() = _binding!! override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) user = requireArguments().getParcelableArgument(KEY_USER, User::class.java) } - override fun onStart() { - super.onStart() - - // disable positive button and apply theming - alertDialog = dialog as AlertDialog - alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false - - viewThemeUtils.platform.themeRadioButton(binding.radioLocalRemove) - viewThemeUtils.platform.themeRadioButton(binding.radioRequestDeletion) - alertDialog.themeButtons(viewThemeUtils) - binding.userName.text = UserAccountManager.getDisplayName(user) - binding.account.text = user?.let { LinkFormatter.toUnicodeDomain(it.accountName) } - } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - _binding = AccountRemovalDialogBinding.inflate(layoutInflater) - - // start avatar generation - setAvatar() - - // hide second option when plug-in isn't installed - if (hasDropAccount()) { - binding.requestDeletion.visibility = View.VISIBLE - } - - val builder = - MaterialAlertDialogBuilder(requireActivity()) - .setTitle(R.string.delete_account) - .setView(binding.root) - .setNegativeButton(R.string.common_cancel) { _, _ -> } - .setPositiveButton(R.string.delete_account) { _, _ -> removeAccount() } - - // allow selection by clicking on list element - binding.localRemove.setOnClickListener { - binding.radioLocalRemove.performClick() - } - binding.requestDeletion.setOnClickListener { - binding.radioRequestDeletion.performClick() - } - - // set listeners for custom radio button list - binding.radioLocalRemove.setOnClickListener { - binding.radioRequestDeletion.isChecked = false - alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).apply { - text = getText(R.string.delete_account) - isEnabled = true + val builder = MaterialAlertDialogBuilder(requireActivity()) + .setTitle(R.string.delete_account) + .setMessage(resources.getString(R.string.delete_account_warning)) + .setIcon(R.drawable.ic_warning) + .setPositiveButton(R.string.common_ok) { _: DialogInterface?, _: Int -> + user?.let { user -> + stopMediaPlayerAndHidePip() + // track adjust and tealium events on logout confirmed + AdjustSdkUtils.trackEvent(AdjustSdkUtils.EVENT_TOKEN_SETTINGS_LOGOUT, appPreferences) + TealiumSdkUtils.trackEvent(TealiumSdkUtils.EVENT_SETTINGS_LOGOUT, appPreferences) + backgroundJobManager.startAccountRemovalJob(user.accountName, false) + } } - } - binding.radioRequestDeletion.setOnClickListener { - binding.radioLocalRemove.isChecked = false - alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).apply { - text = getString(R.string.request_account_deletion_button) - isEnabled = true - } - } - - viewThemeUtils.dialog.colorMaterialAlertDialogBackground(requireActivity(), builder) + .setNegativeButton(R.string.common_cancel, null) return builder.create() } - /** - * Get value of `drop-account` capability. - */ - private fun hasDropAccount(): Boolean { - val capability = FileDataStorageManager(user, context?.contentResolver).getCapability(user) - return capability.dropAccount.isTrue - } - - /** - * Start removal of account. Depending on which option is checked, either a browser will open to request deletion, - * or the local account will be removed immediately. - */ - private fun removeAccount() { - user?.let { user -> - stopMediaPlayerAndHidePip() - if (binding.radioRequestDeletion.isChecked) { - IntentUtil.startLinkIntent(requireActivity(), user.server.uri.toString() + DROP_ACCOUNT_URI) - } else { - backgroundJobManager.startAccountRemovalJob(user.accountName, false) - } - } - } - private fun stopMediaPlayerAndHidePip() { playbackModel.release() } - /** - * Start avatar generation. - */ - private fun setAvatar() { - try { - val imageView = binding.userIcon - imageView.tag = user!!.accountName - avatarGenerator.setAccountAvatar( - user!!, - this, - resources.getDimension(R.dimen.list_item_avatar_icon_radius), - imageView - ) - } catch (_: Exception) { - } - } - - override fun avatarGenerated(avatarDrawable: Drawable?, callContext: Any?) { - avatarDrawable?.let { - binding.userIcon.setImageDrawable(it) - } - } - - override fun shouldCallGeneratedCallback(tag: String?, callContext: Any?): Boolean = binding.userIcon.tag == tag - companion object { private const val KEY_USER = "USER" - private const val DROP_ACCOUNT_URI = "/settings/user/drop_account" @JvmStatic fun newInstance(user: User) = AccountRemovalDialog().apply { diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.kt index 7ed558385c64..5afcf4336d74 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.kt @@ -57,6 +57,7 @@ import com.nextcloud.utils.extensions.getParcelableArgument import com.nextcloud.utils.mdm.MDMConfig import com.nextcloud.utils.text.DisplayTextFormatter import com.nextcloud.utils.thumbnail.FolderThumbnailGenerator +import com.nmc.android.marketTracking.TealiumSdkUtils import com.owncloud.android.R import com.owncloud.android.databinding.FileDetailsFragmentBinding import com.owncloud.android.datamodel.FileDataStorageManager @@ -200,6 +201,8 @@ class FileDetailFragment : super.onStart() listenForTransferProgress() EventBus.getDefault().register(this) + // NMC: track screen view when fragment is visible + TealiumSdkUtils.trackView(TealiumSdkUtils.SCREEN_VIEW_SHARING, preferences) } override fun onResume() { diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.kt index d56341b621bc..98c3a7a3ad9d 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.kt @@ -38,6 +38,7 @@ import com.nextcloud.client.account.User import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.di.Injectable import com.nextcloud.client.network.ClientFactory +import com.nextcloud.client.preferences.AppPreferences import com.nextcloud.client.utils.IntentUtil import com.nextcloud.utils.SnackbarUtil import com.nextcloud.utils.avatar.AvatarGenerationListener @@ -48,6 +49,8 @@ import com.nextcloud.utils.extensions.setVisibleIf import com.nextcloud.utils.extensions.supportsUnifiedShare import com.nextcloud.utils.extensions.toServerCredentials import com.nextcloud.utils.mdm.MDMConfig.shareViaUser +import com.nmc.android.marketTracking.AdjustSdkUtils +import com.nmc.android.marketTracking.TealiumSdkUtils import com.owncloud.android.R import com.owncloud.android.databinding.FileDetailsSharingFragmentBinding import com.owncloud.android.datamodel.FileDataStorageManager @@ -120,6 +123,9 @@ class FileDetailSharingFragment : @Inject lateinit var avatarGenerator: AvatarGenerator + @Inject + lateinit var appPreferences: AppPreferences + // region lifecycle methods override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -755,6 +761,10 @@ class FileDetailSharingFragment : // create without password fileOperationsHelper?.shareFileViaPublicShare(file, null) + + // track event on creating share link + AdjustSdkUtils.trackEvent(AdjustSdkUtils.EVENT_TOKEN_CREATE_SHARING_LINK, appPreferences) + TealiumSdkUtils.trackEvent(TealiumSdkUtils.EVENT_CREATE_SHARING_LINK, appPreferences) } override fun createSecureFileDrop() { diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.kt b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.kt index 22d4f255a9fb..4c780bfbdd95 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListBottomSheetDialog.kt @@ -30,11 +30,13 @@ import com.nextcloud.client.device.DeviceInfo import com.nextcloud.client.di.Injectable import com.nextcloud.client.documentscan.AppScanOptionalFeature import com.nextcloud.utils.BuildHelper.isFlavourGPlay +import com.nextcloud.client.preferences.AppPreferencesImpl import com.nextcloud.utils.EditorUtils import com.nextcloud.utils.extensions.isNetworkAndServerAvailableSuspended import com.nextcloud.utils.extensions.isTemplateAvailable import com.nextcloud.utils.extensions.setVisibleIf import com.owncloud.android.MainApp +import com.nmc.android.marketTracking.TealiumSdkUtils import com.owncloud.android.R import com.owncloud.android.databinding.FileListActionsBottomSheetFragmentBinding import com.owncloud.android.datamodel.ArbitraryDataProvider @@ -137,6 +139,14 @@ class OCFileListBottomSheetDialog( binding.menuUploadFromApp.text = context.getString(R.string.upload_files) } } + + // track screen view when fragment is visible + TealiumSdkUtils.trackView( + TealiumSdkUtils.SCREEN_VIEW_FAB_PLUS, + // need to create direct instance of AppPreferences as Injection doesn't work in Dialogs + // if we take AppPreferences as parameter in constructor it will affect the other NMC PRs test cases + AppPreferencesImpl.fromContext(fileActivity) + ) } private fun checkCreateEncryptedFolderVisibility() { diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.kt index 01f945d5f4bb..f7cd864f61dc 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.kt @@ -66,9 +66,13 @@ import com.nextcloud.utils.extensions.listenEncryptionDialogResult import com.nextcloud.utils.extensions.logFileSize import com.nextcloud.utils.extensions.resolveMimeType import com.nextcloud.utils.extensions.slideHideBottomBehavior +import com.nmc.android.marketTracking.AdjustSdkUtils +import com.nmc.android.marketTracking.TrackingScanInterfaceImpl +import com.nmc.android.marketTracking.TealiumSdkUtils import com.nextcloud.utils.fileNameValidator.FileNameValidator import com.nextcloud.utils.thumbnail.ThumbnailGenerator import com.nextcloud.utils.view.FastScrollUtils +import com.nmc.android.marketTracking.TrackingScanInterface import com.owncloud.android.MainApp import com.owncloud.android.R import com.owncloud.android.datamodel.ArbitraryDataProvider @@ -170,6 +174,13 @@ open class OCFileListFragment : @Inject lateinit var deviceInfo: DeviceInfo + /** + * Things to note about both the branches. 1. nmc/1867-scanbot branch: --> interface won't be initialised --> + * calling of interface method will be done here 2. nmc/1925-market_tracking --> interface will be initialised --> + * calling of interface method won't be done here + */ + private var trackingScanInterface: TrackingScanInterface? = null + lateinit var e2eeDialogPresenter: E2EEDialogPresenter private set @@ -263,6 +274,8 @@ open class OCFileListFragment : "Created ${javaClass.simpleName}: folder=${currentFile?.remotePath}, " + "searchType=$currentSearchType, isSearchFragment=$isSearchFragment" ) + // NMC customization will be initialized in nmc/1925-market_tracking + trackingScanInterface = TrackingScanInterfaceImpl() } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -555,6 +568,10 @@ open class OCFileListFragment : Intent.createChooser(action, getString(R.string.upload_chooser_title)), FileDisplayActivity.REQUEST_CODE__SELECT_CONTENT_FROM_APPS ) + + // track event photo/video/any upload button click + AdjustSdkUtils.trackEvent(AdjustSdkUtils.EVENT_TOKEN_FAB_BOTTOM_PHOTO_VIDEO_UPLOAD, preferences) + TealiumSdkUtils.trackEvent(TealiumSdkUtils.EVENT_FAB_BOTTOM_PHOTO_VIDEO_UPLOAD, preferences) } override fun directCameraUpload() { @@ -571,6 +588,10 @@ open class OCFileListFragment : } showCameraUploadTypeDialog(fileDisplayActivity) + + // NMC: track event for camera upload button click + AdjustSdkUtils.trackEvent(AdjustSdkUtils.EVENT_TOKEN_FAB_BOTTOM_CAMERA_UPLOAD, preferences) + TealiumSdkUtils.trackEvent(TealiumSdkUtils.EVENT_FAB_BOTTOM_CAMERA_UPLOAD, preferences) } private fun showCameraUploadTypeDialog(fileDisplayActivity: FileDisplayActivity) { @@ -647,6 +668,10 @@ open class OCFileListFragment : return } + // track event for uploading files button click + AdjustSdkUtils.trackEvent(AdjustSdkUtils.EVENT_TOKEN_FAB_BOTTOM_FILE_UPLOAD, preferences) + TealiumSdkUtils.trackEvent(TealiumSdkUtils.EVENT_FAB_BOTTOM_FILE_UPLOAD, preferences) + UploadFilesActivity.startUploadActivityForResult( fileActivity, user, @@ -695,6 +720,14 @@ open class OCFileListFragment : //region List item interaction override fun onShareIconClick(file: OCFile) { containerActivity?.showDetails(file, SHARING_TAB) + + // track event on click of Share button + trackSharingClickEvent() + } + + private fun trackSharingClickEvent() { + AdjustSdkUtils.trackEvent(AdjustSdkUtils.EVENT_TOKEN_FILE_BROWSER_SHARING, preferences) + TealiumSdkUtils.trackEvent(TealiumSdkUtils.EVENT_FILE_BROWSER_SHARING, preferences) } override fun showShareDetailView(file: OCFile) { @@ -944,8 +977,11 @@ open class OCFileListFragment : private fun handleListAction(@IdRes itemId: Int, checkedFiles: Set): Boolean { when { - itemId == R.id.action_send_share_file && checkedFiles.size == SINGLE_SELECTION -> + itemId == R.id.action_send_share_file && checkedFiles.size == SINGLE_SELECTION -> { containerActivity?.showDetails(checkedFiles.first(), SHARING_TAB) + // track event on click of Share button + trackSharingClickEvent() + } itemId == R.id.action_see_details && checkedFiles.size == SINGLE_SELECTION -> showDetailsAndHideSortGroup(checkedFiles.first()) @@ -975,6 +1011,8 @@ open class OCFileListFragment : showDetails(file) showSortListGroup(false) } + // track event on click of Share button + trackSharingClickEvent() } private fun startMoveOrCopy(checkedFiles: Set) { diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/autoUpload/AutoUploadFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/autoUpload/AutoUploadFragment.kt index a83065469604..642b74b1df82 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/autoUpload/AutoUploadFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/autoUpload/AutoUploadFragment.kt @@ -22,6 +22,7 @@ import com.nextcloud.client.core.Clock import com.nextcloud.client.device.PowerManagementService import com.nextcloud.client.jobs.BackgroundJobManager import com.nextcloud.client.network.ConnectivityService +import com.nextcloud.client.preferences.AppPreferences import com.nextcloud.ui.component.UploadWarningCard import com.nextcloud.utils.extensions.getTypedActivity import com.nextcloud.utils.extensions.setVisibleIf @@ -63,6 +64,8 @@ class AutoUploadFragment : @Inject lateinit var syncedFolderProvider: SyncedFolderProvider + @Inject lateinit var preferences: AppPreferences + @Inject lateinit var userAccountManager: UserAccountManager @Inject lateinit var backgroundJobManager: BackgroundJobManager @@ -188,6 +191,7 @@ class AutoUploadFragment : persistence, dialogs, backgroundJobManager, + preferences = preferences, getAdapter = { adapter }, onItemVisibilityChanged = { emptyContent?.checkAndShowEmptyListContent() } ) diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/autoUpload/handler/AutoUploadClickHandler.kt b/app/src/main/java/com/owncloud/android/ui/fragment/autoUpload/handler/AutoUploadClickHandler.kt index d6f32116f547..a06d9d9f03d3 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/autoUpload/handler/AutoUploadClickHandler.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/autoUpload/handler/AutoUploadClickHandler.kt @@ -13,6 +13,9 @@ import androidx.lifecycle.lifecycleScope import com.nextcloud.client.account.User import com.nextcloud.client.jobs.BackgroundJobManager import com.nextcloud.client.jobs.upload.FileUploadHelper +import com.nextcloud.client.preferences.AppPreferences +import com.nmc.android.marketTracking.AdjustSdkUtils +import com.nmc.android.marketTracking.TealiumSdkUtils import com.owncloud.android.datamodel.MediaFolderType import com.owncloud.android.datamodel.SyncedFolder import com.owncloud.android.datamodel.SyncedFolderDisplayItem @@ -34,6 +37,7 @@ class AutoUploadClickHandler( private val persistence: AutoUploadPersistence, private val dialogs: AutoUploadDialogs, private val backgroundJobManager: BackgroundJobManager, + private val preferences: AppPreferences, private val getAdapter: () -> SyncedFolderAdapter, private val onItemVisibilityChanged: () -> Unit ) : SyncedFolderAdapter.ClickListener { @@ -69,6 +73,9 @@ class AutoUploadClickHandler( fragment.lifecycleScope.launch(Dispatchers.IO) { fileUploadHelper.removeEntityFromUploadEntities(item.id) } + + // NMC: track event when user enable/disable auto upload on/off + trackAutoUploadEvent(item.isEnabled) } override fun onSyncFolderSettingsClick(section: Int, syncedFolderDisplayItem: SyncedFolderDisplayItem?) { @@ -107,6 +114,22 @@ class AutoUploadClickHandler( if (isSaved && syncedFolder.isEnabled) { dialogs.showBatteryOptimizationDialogIfNeeded() } + + // NMC: track event when user enable/disable auto upload on/off + trackAutoUploadEvent(syncedFolder.isEnabled) + } + + private fun trackAutoUploadEvent(enabled: Boolean) { + AdjustSdkUtils.trackEvent( + if (enabled) AdjustSdkUtils.EVENT_TOKEN_SETTINGS_AUTO_UPLOAD_ON + else AdjustSdkUtils.EVENT_TOKEN_SETTINGS_AUTO_UPLOAD_OFF, + preferences + ) + TealiumSdkUtils.trackEvent( + if (enabled) TealiumSdkUtils.EVENT_SETTINGS_AUTO_UPLOAD_ON + else TealiumSdkUtils.EVENT_SETTINGS_AUTO_UPLOAD_OFF, + preferences + ) } fun onDeleteAutoUploadFolderPreference(syncedFolder: SyncedFolderParcelable?) { diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 1374fb3d4f05..27ec6466f6ae 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -305,8 +305,9 @@ Tägliche Sicherung Zu sichernde Daten Zugangsdaten falsch - Konto entfernen + Benutzer abmelden Einträge löschen + Achtung! Alles wird in den Ausgangszustand zurückgesetzt. Fortfahren? Link löschen Auswahl aufheben Ziel-Dateiname diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 02ad7b2f9b0c..6799c3e8ffe4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -299,7 +299,8 @@ Request account deletion Request deletion Request permanent deletion of account by service provider - Remove account + Logout user + Attention! Will be reset to initial state. Continue? Avatar Active user Upload from… diff --git a/gradle.properties b/gradle.properties index 91f3dedc1c10..efedea15ddec 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ NC_TEST_SERVER_PASSWORD=test android.useAndroidX=true android.nonTransitiveRClass=true #android.debug.obsoleteApi=true - +ADJUST_APP_TOKEN="54qd7rraqav4" # JVM arguments to optimize heap usage, enable heap dump on out-of-memory errors, and set the file encoding org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8 -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fb482855de0b..390edafa7948 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -50,6 +50,9 @@ kotlinxSerializationJson = "1.12.0" ksp = "2.3.12" leakcanary = "2.14" legacySupportV4Version = "1.0.0" +adjustAndroidVersion = "5.1.0" +installreferrerVersion = "2.2" +tealiumlibraryVersion = "5.8.0" libraryVersion = "1.3.0" lifecycleViewmodelKtxVersion = "2.11.0" loaderviewlibraryVersion = "3.0.0" @@ -68,6 +71,7 @@ playServicesBaseVersion = "18.11.1" prismVersion = "2.0.0" qrcodescannerVersion = "0.1.2.4" reviewKtxVersion = "2.0.2" +playServicesAdsIdentifierVersion = "18.0.1" roomVersion = "2.8.5" screengrabVersion = "2.1.1" sectionedRecyclerviewVersion = "0.6.1" @@ -134,6 +138,7 @@ firebase-messaging = { module = "com.google.firebase:firebase-messaging", versio gson = { module = "com.google.code.gson:gson", version.ref = "gsonVersion" } html = { module = "io.noties.markwon:html", version.ref = "markwonVersion" } ical4j = { module = "org.mnode.ical4j:ical4j", version.ref = "ical4jVersion" } +play-services-ads-identifier = { module = "com.google.android.gms:play-services-ads-identifier", version.ref = "playServicesAdsIdentifierVersion" } jackrabbit-webdav = { module = "org.apache.jackrabbit:jackrabbit-webdav", version.ref = "jackrabbitWebdavVersion" } json = { module = "org.json:json", version.ref = "jsonVersion" } juniversalchardet = { module = "com.github.albfernandez:juniversalchardet", version.ref = "juniversalchardetVersion" } @@ -167,6 +172,11 @@ lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", v lifecycle-service = { module = "androidx.lifecycle:lifecycle-service", version.ref = "lifecycleViewmodelKtxVersion" } lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtxVersion" } +# NMC Marketing +adjust-android = { module = "com.adjust.sdk:adjust-android", version.ref = "adjustAndroidVersion" } +installreferrer = { module = "com.android.installreferrer:installreferrer", version.ref = "installreferrerVersion" } +tealium-library = { module = "com.tealium:library", version.ref = "tealiumlibraryVersion" } + # ksp ksp = { module = "com.github.bumptech.glide:ksp", version.ref = "glide" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 26e4b55708b4..33678753a06c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -37,6 +37,8 @@ dependencyResolutionManagement { } } mavenCentral() + // NMC Customization: tealium + maven("https://maven.tealiumiq.com/android/releases/") maven("https://jitpack.io") } }