diff --git a/app/build.gradle b/app/build.gradle index ba7f71b0..4b01d59c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -187,6 +187,7 @@ dependencies { androidTestImplementation "androidx.test:rules:$test_rule_version" androidTestImplementation "androidx.test.espresso:espresso-core:$espresso_core_version" implementation "com.github.wseemann:FFmpegMediaMetadataRetriever:$ffmeg_media_retriever_version" + implementation "com.github.matomo-org:matomo-sdk-android:$matomo_analytics_version" implementation "org.apache.tika:tika-core:$tika_core" implementation "com.airbnb.android:lottie:$lottie_version" diff --git a/app/src/main/java/dev/arkbuilders/navigator/analytics/AnalyticsModule.kt b/app/src/main/java/dev/arkbuilders/navigator/analytics/AnalyticsModule.kt new file mode 100644 index 00000000..29821cfa --- /dev/null +++ b/app/src/main/java/dev/arkbuilders/navigator/analytics/AnalyticsModule.kt @@ -0,0 +1,45 @@ +package dev.arkbuilders.navigator.analytics + +import android.content.Context +import dagger.Module +import dagger.Provides +import dev.arkbuilders.navigator.analytics.folders.FoldersAnalytics +import dev.arkbuilders.navigator.analytics.folders.FoldersAnalyticsImpl +import dev.arkbuilders.navigator.analytics.gallery.GalleryAnalytics +import dev.arkbuilders.navigator.analytics.gallery.GalleryAnalyticsImpl +import dev.arkbuilders.navigator.analytics.resources.ResourcesAnalytics +import dev.arkbuilders.navigator.analytics.resources.ResourcesAnalyticsImpl +import dev.arkbuilders.navigator.analytics.settings.SettingsAnalytics +import dev.arkbuilders.navigator.analytics.settings.SettingsAnalyticsImpl +import org.matomo.sdk.Tracker +import javax.inject.Singleton + +@Module +class AnalyticsModule { + + @Singleton + @Provides + fun provideFolderAnalytics( + matomoTracker: Tracker, + context: Context + ): FoldersAnalytics = + FoldersAnalyticsImpl(matomoTracker = matomoTracker, context = context) + + @Singleton + @Provides + fun provideResourcesAnalytics( + matomoTracker: Tracker + ): ResourcesAnalytics = ResourcesAnalyticsImpl(matomoTracker) + + @Singleton + @Provides + fun provideGalleryAnalytics( + matomoTracker: Tracker, + ): GalleryAnalytics = GalleryAnalyticsImpl(matomoTracker) + + @Singleton + @Provides + fun provideSettingsAnalytics( + matomoTracker: Tracker + ): SettingsAnalytics = SettingsAnalyticsImpl(matomoTracker) +} diff --git a/app/src/main/java/dev/arkbuilders/navigator/analytics/Utils.kt b/app/src/main/java/dev/arkbuilders/navigator/analytics/Utils.kt new file mode 100644 index 00000000..166ba836 --- /dev/null +++ b/app/src/main/java/dev/arkbuilders/navigator/analytics/Utils.kt @@ -0,0 +1,14 @@ +package dev.arkbuilders.navigator.analytics + +import org.matomo.sdk.Tracker +import org.matomo.sdk.extra.TrackHelper + +fun Tracker.trackScreen(build: TrackHelper.() -> TrackHelper.Screen) { + val matomoTracker = this + build(TrackHelper.track()).with(matomoTracker) +} + +fun Tracker.trackEvent(build: TrackHelper.() -> TrackHelper.EventBuilder) { + val matomoTracker = this + build(TrackHelper.track()).with(matomoTracker) +} diff --git a/app/src/main/java/dev/arkbuilders/navigator/analytics/folders/FoldersAnalytics.kt b/app/src/main/java/dev/arkbuilders/navigator/analytics/folders/FoldersAnalytics.kt new file mode 100644 index 00000000..057eec7c --- /dev/null +++ b/app/src/main/java/dev/arkbuilders/navigator/analytics/folders/FoldersAnalytics.kt @@ -0,0 +1,9 @@ +package dev.arkbuilders.navigator.analytics.folders + +interface FoldersAnalytics { + fun trackScreen() + fun trackRootOpen() + fun trackFavOpen() + fun trackRootAdded() + fun trackFavAdded() +} diff --git a/app/src/main/java/dev/arkbuilders/navigator/analytics/folders/FoldersAnalyticsImpl.kt b/app/src/main/java/dev/arkbuilders/navigator/analytics/folders/FoldersAnalyticsImpl.kt new file mode 100644 index 00000000..dcc47101 --- /dev/null +++ b/app/src/main/java/dev/arkbuilders/navigator/analytics/folders/FoldersAnalyticsImpl.kt @@ -0,0 +1,32 @@ +package dev.arkbuilders.navigator.analytics.folders + +import android.content.Context +import dev.arkbuilders.navigator.analytics.trackEvent +import dev.arkbuilders.navigator.analytics.trackScreen +import org.matomo.sdk.Tracker +import javax.inject.Inject + +class FoldersAnalyticsImpl @Inject constructor( + private val matomoTracker: Tracker, + private val context: Context +) : FoldersAnalytics { + + override fun trackScreen() = matomoTracker + .trackScreen { screen(SCREEN_NAME) } + + override fun trackRootOpen() = matomoTracker.trackScreenEvent("Root opened") + + override fun trackFavOpen() = matomoTracker.trackScreenEvent("Fav opened") + + override fun trackRootAdded() = matomoTracker.trackScreenEvent("Root added") + + override fun trackFavAdded() = matomoTracker.trackScreenEvent("Fav added") + + private fun Tracker.trackScreenEvent(action: String) = this.trackEvent { + event(SCREEN_NAME, action) + } + + companion object { + private const val SCREEN_NAME = "Folders screen" + } +} diff --git a/app/src/main/java/dev/arkbuilders/navigator/analytics/gallery/GalleryAnalytics.kt b/app/src/main/java/dev/arkbuilders/navigator/analytics/gallery/GalleryAnalytics.kt new file mode 100644 index 00000000..a075161f --- /dev/null +++ b/app/src/main/java/dev/arkbuilders/navigator/analytics/gallery/GalleryAnalytics.kt @@ -0,0 +1,13 @@ +package dev.arkbuilders.navigator.analytics.gallery + +interface GalleryAnalytics { + fun trackScreen() + fun trackResOpen() + fun trackResShare() + fun trackResInfo() + fun trackResEdit() + fun trackResRemove() + fun trackTagSelect() + fun trackTagRemove() + fun trackTagsEdit() +} diff --git a/app/src/main/java/dev/arkbuilders/navigator/analytics/gallery/GalleryAnalyticsImpl.kt b/app/src/main/java/dev/arkbuilders/navigator/analytics/gallery/GalleryAnalyticsImpl.kt new file mode 100644 index 00000000..3860fd00 --- /dev/null +++ b/app/src/main/java/dev/arkbuilders/navigator/analytics/gallery/GalleryAnalyticsImpl.kt @@ -0,0 +1,37 @@ +package dev.arkbuilders.navigator.analytics.gallery + +import dev.arkbuilders.navigator.analytics.trackEvent +import dev.arkbuilders.navigator.analytics.trackScreen +import org.matomo.sdk.Tracker + +class GalleryAnalyticsImpl( + private val matomoTracker: Tracker +) : GalleryAnalytics { + override fun trackScreen() = matomoTracker.trackScreen { + screen(SCREEN_NAME) + } + + override fun trackResOpen() = matomoTracker.trackScreenEvent("Resource open") + + override fun trackResShare() = matomoTracker.trackScreenEvent("Resource share") + + override fun trackResInfo() = matomoTracker.trackScreenEvent("Resource info") + + override fun trackResEdit() = matomoTracker.trackScreenEvent("Resource edit") + + override fun trackResRemove() = matomoTracker.trackScreenEvent("Resource remove") + + override fun trackTagSelect() = matomoTracker.trackScreenEvent("Tag select") + + override fun trackTagRemove() = matomoTracker.trackScreenEvent("Tag remove") + + override fun trackTagsEdit() = matomoTracker.trackScreenEvent("Tags edit") + + private fun Tracker.trackScreenEvent(action: String) = this.trackEvent { + event(SCREEN_NAME, action) + } + + companion object { + private const val SCREEN_NAME = "Gallery screen" + } +} diff --git a/app/src/main/java/dev/arkbuilders/navigator/analytics/resources/ResourcesAnalytics.kt b/app/src/main/java/dev/arkbuilders/navigator/analytics/resources/ResourcesAnalytics.kt new file mode 100644 index 00000000..ec32450a --- /dev/null +++ b/app/src/main/java/dev/arkbuilders/navigator/analytics/resources/ResourcesAnalytics.kt @@ -0,0 +1,20 @@ +package dev.arkbuilders.navigator.analytics.resources + +import dev.arkbuilders.arklib.data.storage.StorageException +import dev.arkbuilders.components.tagselector.QueryMode +import dev.arkbuilders.components.tagselector.TagsSorting +import dev.arkbuilders.navigator.data.utils.Sorting + +interface ResourcesAnalytics { + fun trackScreen() + fun trackResClick() + fun trackMoveSelectedRes() + fun trackCopySelectedRes() + fun trackRemoveSelectedRes() + fun trackShareSelectedRes() + fun trackResShuffle() + fun trackTagSortCriteria(tagsSorting: TagsSorting) + fun trackResSortCriteria(sorting: Sorting) + fun trackQueryModeChanged(queryMode: QueryMode) + fun trackStorageProvideException(exception: StorageException) +} diff --git a/app/src/main/java/dev/arkbuilders/navigator/analytics/resources/ResourcesAnalyticsImpl.kt b/app/src/main/java/dev/arkbuilders/navigator/analytics/resources/ResourcesAnalyticsImpl.kt new file mode 100644 index 00000000..d07de657 --- /dev/null +++ b/app/src/main/java/dev/arkbuilders/navigator/analytics/resources/ResourcesAnalyticsImpl.kt @@ -0,0 +1,59 @@ +package dev.arkbuilders.navigator.analytics.resources + +import dev.arkbuilders.arklib.data.storage.StorageException +import dev.arkbuilders.components.tagselector.QueryMode +import dev.arkbuilders.components.tagselector.TagsSorting +import dev.arkbuilders.navigator.analytics.trackEvent +import dev.arkbuilders.navigator.analytics.trackScreen +import dev.arkbuilders.navigator.data.utils.Sorting +import org.matomo.sdk.Tracker +import org.matomo.sdk.extra.TrackHelper + +class ResourcesAnalyticsImpl( + private val matomoTracker: Tracker +) : ResourcesAnalytics { + override fun trackScreen() = matomoTracker.trackScreen { screen(SCREEN_NAME) } + + override fun trackResClick() = + matomoTracker.trackScreenEvent("Resource Click") + + override fun trackMoveSelectedRes() = + matomoTracker.trackScreenEvent("Move selected resources") + + override fun trackCopySelectedRes() = + matomoTracker.trackScreenEvent("Copy selected resources") + + override fun trackRemoveSelectedRes() = + matomoTracker.trackScreenEvent("Remove selected resources") + + override fun trackShareSelectedRes() = + matomoTracker.trackScreenEvent("Share selected resources") + + override fun trackResShuffle() = + matomoTracker.trackScreenEvent("Shuffle resources") + + override fun trackTagSortCriteria(tagsSorting: TagsSorting) = + matomoTracker.trackScreenEvent("Tag sorting criteria: ${tagsSorting.name}") + + override fun trackResSortCriteria(sorting: Sorting) = + matomoTracker.trackScreenEvent("Resources sorting criteria: ${sorting.name}") + + override fun trackQueryModeChanged(queryMode: QueryMode) = + matomoTracker.trackScreenEvent("Query mode: ${queryMode.name}") + + override fun trackStorageProvideException(exception: StorageException) = + TrackHelper + .track() + .exception(exception) + .description("Storage provide") + .fatal(false) + .with(matomoTracker) + + private fun Tracker.trackScreenEvent(action: String) = this.trackEvent { + event(SCREEN_NAME, action) + } + + companion object { + private const val SCREEN_NAME = "Resources screen" + } +} diff --git a/app/src/main/java/dev/arkbuilders/navigator/analytics/settings/SettingsAnalytics.kt b/app/src/main/java/dev/arkbuilders/navigator/analytics/settings/SettingsAnalytics.kt new file mode 100644 index 00000000..533f0128 --- /dev/null +++ b/app/src/main/java/dev/arkbuilders/navigator/analytics/settings/SettingsAnalytics.kt @@ -0,0 +1,6 @@ +package dev.arkbuilders.navigator.analytics.settings + +interface SettingsAnalytics { + fun trackScreen() + fun trackBooleanPref(name: String, enabled: Boolean) +} diff --git a/app/src/main/java/dev/arkbuilders/navigator/analytics/settings/SettingsAnalyticsImpl.kt b/app/src/main/java/dev/arkbuilders/navigator/analytics/settings/SettingsAnalyticsImpl.kt new file mode 100644 index 00000000..e384806b --- /dev/null +++ b/app/src/main/java/dev/arkbuilders/navigator/analytics/settings/SettingsAnalyticsImpl.kt @@ -0,0 +1,21 @@ +package dev.arkbuilders.navigator.analytics.settings + +import dev.arkbuilders.navigator.analytics.trackEvent +import dev.arkbuilders.navigator.analytics.trackScreen +import org.matomo.sdk.Tracker + +class SettingsAnalyticsImpl( + private val matomoTracker: Tracker +) : SettingsAnalytics { + override fun trackScreen() = matomoTracker.trackScreen { screen(SCREEN_NAME) } + + override fun trackBooleanPref(name: String, enabled: Boolean) { + val enabledStr = if (enabled) "enabled" else "disabled" + matomoTracker + .trackEvent { event(SCREEN_NAME, "$name is $enabledStr") } + } + + companion object { + private const val SCREEN_NAME = "Settings screen" + } +} diff --git a/app/src/main/java/dev/arkbuilders/navigator/di/AppComponent.kt b/app/src/main/java/dev/arkbuilders/navigator/di/AppComponent.kt index 21f20dda..5ac3daff 100644 --- a/app/src/main/java/dev/arkbuilders/navigator/di/AppComponent.kt +++ b/app/src/main/java/dev/arkbuilders/navigator/di/AppComponent.kt @@ -26,6 +26,7 @@ import dev.arkbuilders.navigator.presentation.screen.resources.adapter.FileItemV import dev.arkbuilders.navigator.presentation.screen.resources.adapter.ResourcesGridPresenter import dev.arkbuilders.navigator.presentation.screen.settings.SettingsFragment import dev.arkbuilders.arkfilepicker.folders.FoldersRepo +import dev.arkbuilders.navigator.analytics.AnalyticsModule import dev.arkbuilders.navigator.di.modules.DispatcherModule import javax.inject.Singleton @@ -36,6 +37,7 @@ import javax.inject.Singleton CiceroneModule::class, RepoModule::class, DispatcherModule::class, + AnalyticsModule::class, ] ) diff --git a/app/src/main/java/dev/arkbuilders/navigator/di/modules/AppModule.kt b/app/src/main/java/dev/arkbuilders/navigator/di/modules/AppModule.kt index 80ec96df..65f21c5c 100644 --- a/app/src/main/java/dev/arkbuilders/navigator/di/modules/AppModule.kt +++ b/app/src/main/java/dev/arkbuilders/navigator/di/modules/AppModule.kt @@ -9,6 +9,9 @@ import dev.arkbuilders.navigator.data.utils.DevicePathsExtractor import dev.arkbuilders.navigator.data.utils.DevicePathsExtractorImpl import dev.arkbuilders.navigator.presentation.App import dev.arkbuilders.navigator.presentation.utils.StringProvider +import org.matomo.sdk.Matomo +import org.matomo.sdk.Tracker +import org.matomo.sdk.TrackerBuilder import javax.inject.Singleton @Module @@ -29,4 +32,12 @@ class AppModule { @Singleton fun provideDevicePathsExtractor(application: App): DevicePathsExtractor = DevicePathsExtractorImpl(application) + + @Provides + @Singleton + fun provideMatomoAnalytics(ctx: Context): Tracker = + TrackerBuilder.createDefault( + "https://ark-builders.matomo.cloud/matomo.php", + 2 + ).build(Matomo.getInstance(ctx)) } diff --git a/app/src/main/java/dev/arkbuilders/navigator/di/modules/RepoModule.kt b/app/src/main/java/dev/arkbuilders/navigator/di/modules/RepoModule.kt index 14c45247..20e07512 100644 --- a/app/src/main/java/dev/arkbuilders/navigator/di/modules/RepoModule.kt +++ b/app/src/main/java/dev/arkbuilders/navigator/di/modules/RepoModule.kt @@ -1,6 +1,5 @@ package dev.arkbuilders.navigator.di.modules -import android.util.Log import dagger.Module import dagger.Provides import kotlinx.coroutines.CoroutineScope @@ -16,6 +15,7 @@ import dev.arkbuilders.arklib.user.tags.TagsStorageRepo import dev.arkbuilders.navigator.data.preferences.Preferences import dev.arkbuilders.navigator.data.stats.StatsStorageRepo import dev.arkbuilders.navigator.data.utils.LogTags.MAIN +import timber.log.Timber import javax.inject.Named import javax.inject.Singleton @@ -37,7 +37,7 @@ class RepoModule { fun resourceIndexRepo( foldersRepo: FoldersRepo ): ResourceIndexRepo { - Log.d(MAIN, "creating ResourceIndexRepo") + Timber.d(MAIN, "creating ResourceIndexRepo") return ResourceIndexRepo(foldersRepo) } diff --git a/app/src/main/java/dev/arkbuilders/navigator/presentation/App.kt b/app/src/main/java/dev/arkbuilders/navigator/presentation/App.kt index da53a48d..32cbf108 100644 --- a/app/src/main/java/dev/arkbuilders/navigator/presentation/App.kt +++ b/app/src/main/java/dev/arkbuilders/navigator/presentation/App.kt @@ -2,11 +2,14 @@ package dev.arkbuilders.navigator.presentation import android.app.Application import android.os.StrictMode +import dev.arkbuilders.arkfilepicker.folders.FoldersRepo +import dev.arkbuilders.arklib.initArkLib +import dev.arkbuilders.arklib.initRustLogger import dev.arkbuilders.navigator.BuildConfig import dev.arkbuilders.navigator.R +import dev.arkbuilders.navigator.data.preferences.PreferenceKey import dev.arkbuilders.navigator.di.AppComponent import dev.arkbuilders.navigator.di.DaggerAppComponent -import dev.arkbuilders.navigator.data.preferences.PreferenceKey import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -15,9 +18,6 @@ import org.acra.config.httpSender import org.acra.data.StringFormat import org.acra.ktx.initAcra import org.acra.sender.HttpSender -import dev.arkbuilders.arkfilepicker.folders.FoldersRepo -import dev.arkbuilders.arklib.initArkLib -import dev.arkbuilders.arklib.initRustLogger import timber.log.Timber class App : Application() { @@ -37,7 +37,6 @@ class App : Application() { override fun onCreate() { super.onCreate() - System.loadLibrary("arklib") FoldersRepo.init(this) initArkLib() diff --git a/app/src/main/java/dev/arkbuilders/navigator/presentation/dialog/rootsscan/RootsScanDialogPresenter.kt b/app/src/main/java/dev/arkbuilders/navigator/presentation/dialog/rootsscan/RootsScanDialogPresenter.kt index 8cbfa6be..7886c2c0 100644 --- a/app/src/main/java/dev/arkbuilders/navigator/presentation/dialog/rootsscan/RootsScanDialogPresenter.kt +++ b/app/src/main/java/dev/arkbuilders/navigator/presentation/dialog/rootsscan/RootsScanDialogPresenter.kt @@ -1,6 +1,5 @@ package dev.arkbuilders.navigator.presentation.dialog.rootsscan -import android.util.Log import dev.arkbuilders.navigator.data.utils.DevicePathsExtractor import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ensureActive @@ -10,6 +9,7 @@ import moxy.MvpPresenter import moxy.presenterScope import dev.arkbuilders.arklib.arkFolder import dev.arkbuilders.navigator.data.utils.LogTags +import timber.log.Timber import java.nio.file.Path import java.util.LinkedList import java.util.Queue @@ -61,7 +61,7 @@ class RootsScanDialogPresenter : MvpPresenter() { } else queue.addAll(folder.listDirectoryEntries().filter(Path::isDirectory)) } catch (e: Exception) { - Log.w(LogTags.FILES, "Can't scan $folder due to $e") + Timber.w(LogTags.FILES, "Can't scan $folder due to $e") withContext(Dispatchers.Main) { viewState.toastFolderSkip(folder) } diff --git a/app/src/main/java/dev/arkbuilders/navigator/presentation/dialog/sort/SortDialogFragment.kt b/app/src/main/java/dev/arkbuilders/navigator/presentation/dialog/sort/SortDialogFragment.kt index bc52ea35..e8e0fdcc 100644 --- a/app/src/main/java/dev/arkbuilders/navigator/presentation/dialog/sort/SortDialogFragment.kt +++ b/app/src/main/java/dev/arkbuilders/navigator/presentation/dialog/sort/SortDialogFragment.kt @@ -3,7 +3,6 @@ package dev.arkbuilders.navigator.presentation.dialog.sort import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -15,6 +14,7 @@ import dev.arkbuilders.navigator.data.utils.LogTags.RESOURCES_SCREEN import dev.arkbuilders.navigator.data.utils.Sorting import moxy.MvpAppCompatDialogFragment import moxy.ktx.moxyPresenter +import timber.log.Timber class SortDialogFragment : MvpAppCompatDialogFragment(), SortDialogView { @@ -54,7 +54,7 @@ class SortDialogFragment : MvpAppCompatDialogFragment(), SortDialogView { val newSorting = sortingCategorySelected(checkedId) - Log.d( + Timber.d( RESOURCES_SCREEN, "sorting criteria changed, sorting = $newSorting" ) diff --git a/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/folders/FoldersFragment.kt b/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/folders/FoldersFragment.kt index 344030d4..e4f9ac77 100644 --- a/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/folders/FoldersFragment.kt +++ b/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/folders/FoldersFragment.kt @@ -2,7 +2,6 @@ package dev.arkbuilders.navigator.presentation.screen.folders import android.content.Context import android.os.Bundle -import android.util.Log import android.view.View import androidx.activity.addCallback import androidx.core.os.bundleOf @@ -26,6 +25,7 @@ import dev.arkbuilders.arkfilepicker.presentation.folderstree.FolderNode import dev.arkbuilders.arkfilepicker.presentation.folderstree.FolderTreeView import dev.arkbuilders.arkfilepicker.presentation.folderstree.RootNode import dev.arkbuilders.navigator.R +import dev.arkbuilders.navigator.analytics.folders.FoldersAnalytics import dev.arkbuilders.navigator.data.utils.LogTags.FOLDERS_SCREEN import dev.arkbuilders.navigator.databinding.FragmentFoldersBinding import dev.arkbuilders.navigator.presentation.App @@ -42,6 +42,7 @@ import dev.arkbuilders.navigator.presentation.utils.toast import dev.arkbuilders.navigator.presentation.utils.toastFailedPaths import dev.arkbuilders.navigator.presentation.view.StackedToasts import org.orbitmvi.orbit.viewmodel.observe +import timber.log.Timber import java.nio.file.Path import javax.inject.Inject import kotlin.io.path.Path @@ -59,6 +60,9 @@ class FoldersFragment : Fragment(R.layout.fragment_folders) { @Inject lateinit var router: AppRouter + @Inject + lateinit var folderAnalytics: FoldersAnalytics + private lateinit var stackedToasts: StackedToasts private var foldersTree: FolderTreeView? = null @@ -68,7 +72,7 @@ class FoldersFragment : Fragment(R.layout.fragment_folders) { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - Log.d(FOLDERS_SCREEN, "view created in FoldersFragment") + Timber.d(FOLDERS_SCREEN, "view created in FoldersFragment") super.onViewCreated(view, savedInstanceState) FullscreenHelper.setStatusBarVisibility(true, requireActivity().window) @@ -77,7 +81,7 @@ class FoldersFragment : Fragment(R.layout.fragment_folders) { } fun init() { - Log.d(FOLDERS_SCREEN, "initializing FoldersFragment") + Timber.d(FOLDERS_SCREEN, "initializing FoldersFragment") (activity as MainActivity).setSelectedTab(R.id.page_roots) stackedToasts = StackedToasts(binding.rvToasts, lifecycleScope) binding.rvRoots.layoutManager = LinearLayoutManager(context) @@ -92,7 +96,7 @@ class FoldersFragment : Fragment(R.layout.fragment_folders) { initResultListeners() requireActivity().onBackPressedDispatcher.addCallback(this) { - Log.d(FOLDERS_SCREEN, "[back] clicked") + Timber.d(FOLDERS_SCREEN, "[back] clicked") router.exit() } @@ -202,6 +206,7 @@ class FoldersFragment : Fragment(R.layout.fragment_folders) { RootAndFav(node.path.toString(), null) ) ) + folderAnalytics.trackRootOpen() } is FavoriteNode -> { @@ -210,6 +215,7 @@ class FoldersFragment : Fragment(R.layout.fragment_folders) { RootAndFav(node.root.toString(), node.path.toString()) ) ) + folderAnalytics.trackFavOpen() } } } diff --git a/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/folders/FoldersViewModel.kt b/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/folders/FoldersViewModel.kt index 27178f0f..fcb96afd 100644 --- a/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/folders/FoldersViewModel.kt +++ b/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/folders/FoldersViewModel.kt @@ -1,6 +1,5 @@ package dev.arkbuilders.navigator.presentation.screen.folders -import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope @@ -9,6 +8,7 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import dev.arkbuilders.arkfilepicker.folders.FoldersRepo import dev.arkbuilders.arklib.data.index.ResourceIndexRepo +import dev.arkbuilders.navigator.analytics.folders.FoldersAnalytics import dev.arkbuilders.navigator.data.PermissionsHelper import dev.arkbuilders.navigator.data.preferences.PreferenceKey import dev.arkbuilders.navigator.data.preferences.Preferences @@ -23,6 +23,7 @@ import org.orbitmvi.orbit.syntax.simple.intent import org.orbitmvi.orbit.syntax.simple.postSideEffect import org.orbitmvi.orbit.syntax.simple.reduce import org.orbitmvi.orbit.viewmodel.container +import timber.log.Timber import java.nio.file.Path import javax.inject.Inject @@ -61,7 +62,8 @@ class FoldersViewModel @Inject constructor( private val preferences: Preferences, private val permsHelper: PermissionsHelper, private val devicePathsExtractor: DevicePathsExtractor, - private val defaultDispatcher: CoroutineDispatcher = Dispatchers.IO + private val defaultDispatcher: CoroutineDispatcher = Dispatchers.IO, + private val foldersAnalytics: FoldersAnalytics, ) : ViewModel(), ContainerHost { override val container: Container = container( @@ -71,6 +73,7 @@ class FoldersViewModel @Inject constructor( private lateinit var devices: List init { + foldersAnalytics.trackScreen() intent { if (!permsHelper.isWritePermissionGranted()) { var granted = @@ -125,13 +128,13 @@ class FoldersViewModel @Inject constructor( } if (deleteFromMemory) { - Log.d( + Timber.d( LogTags.FOLDERS_TREE, "forgetting and deleting root folder $root" ) foldersRepo.deleteRoot(root) } else { - Log.d( + Timber.d( LogTags.FOLDERS_TREE, "forgetting root folder $root" ) @@ -158,13 +161,13 @@ class FoldersViewModel @Inject constructor( ) } if (deleteFromMemory) { - Log.d( + Timber.d( LogTags.FOLDERS_TREE, "forgetting and deleting favorite $favorite" ) foldersRepo.deleteFavorite(root, favorite) } else { - Log.d( + Timber.d( LogTags.FOLDERS_TREE, "forgetting favorite $favorite" ) @@ -205,7 +208,7 @@ class FoldersViewModel @Inject constructor( ) } - Log.d(LogTags.FOLDERS_SCREEN, "root $root added in RootsPresenter") + Timber.d(LogTags.FOLDERS_SCREEN, "root $root added in RootsPresenter") val path = withContext(defaultDispatcher) { root.toRealPath() } @@ -215,6 +218,7 @@ class FoldersViewModel @Inject constructor( throw AssertionError("Path must be checked in RootPicker") } + foldersAnalytics.trackRootAdded() foldersRepo.addRoot(path) postSideEffect(FoldersSideEffect.ToastIndexingCanTakeMinutes) @@ -245,7 +249,7 @@ class FoldersViewModel @Inject constructor( ProgressWithText(true, "Adding favorite folder") ) } - Log.d( + Timber.d( LogTags.FOLDERS_SCREEN, "favorite $favorite added in RootsPresenter" ) @@ -264,6 +268,7 @@ class FoldersViewModel @Inject constructor( throw AssertionError("Path must be checked in RootPicker") } + foldersAnalytics.trackFavAdded() foldersRepo.addFavorite(root, relative) folders = foldersRepo.provideFolders() reduce { @@ -298,15 +303,17 @@ class FoldersViewModelFactory @AssistedInject constructor( private val preferences: Preferences, private val permsHelper: PermissionsHelper, private val devicePathsExtractor: DevicePathsExtractor, + private val foldersAnalytics: FoldersAnalytics ) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { return FoldersViewModel( - rescanRoots, - foldersRepo, - resourcesIndexRepo, - preferences, - permsHelper, - devicePathsExtractor + rescanRoots = rescanRoots, + foldersRepo = foldersRepo, + resourcesIndexRepo = resourcesIndexRepo, + preferences = preferences, + permsHelper = permsHelper, + devicePathsExtractor = devicePathsExtractor, + foldersAnalytics = foldersAnalytics, ) as T } diff --git a/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/gallery/GalleryFragment.kt b/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/gallery/GalleryFragment.kt index d01b4e44..bb8b2767 100644 --- a/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/gallery/GalleryFragment.kt +++ b/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/gallery/GalleryFragment.kt @@ -5,7 +5,6 @@ import android.content.ActivityNotFoundException import android.content.Intent import android.net.Uri import android.os.Bundle -import android.util.Log import android.util.TypedValue import android.view.View import android.widget.Toast @@ -74,7 +73,7 @@ class GalleryFragment : ) .toMutableList(), ).apply { - Log.d(GALLERY_SCREEN, "creating GalleryPresenter") + Timber.d(GALLERY_SCREEN, "creating GalleryPresenter") App.instance.appComponent.inject(this) } } @@ -85,7 +84,7 @@ class GalleryFragment : } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - Log.d(GALLERY_SCREEN, "view created in GalleryFragment") + Timber.d(GALLERY_SCREEN, "view created in GalleryFragment") super.onViewCreated(view, savedInstanceState) App.instance.appComponent.inject(this) } @@ -96,7 +95,7 @@ class GalleryFragment : } override fun init() { - Log.d(GALLERY_SCREEN, "currentItem = ${binding.viewPager.currentItem}") + Timber.d(GALLERY_SCREEN, "currentItem = ${binding.viewPager.currentItem}") animatePagerAppearance() initResultListener() @@ -306,7 +305,7 @@ class GalleryFragment : override fun displayPreviewTags(resource: ResourceId, tags: Tags) { lifecycleScope.launch { - Log.d( + Timber.d( GALLERY_SCREEN, "displaying tags of resource $resource for preview" ) @@ -329,7 +328,7 @@ class GalleryFragment : override fun showEditTagsDialog( resource: ResourceId ) { - Log.d( + Timber.d( GALLERY_SCREEN, "showing [edit-tags] dialog for resource $resource" ) @@ -501,9 +500,12 @@ class GalleryFragment : actionType: String, detachProcess: Boolean ) { - Log.i(GALLERY_SCREEN, "Opening resource in an external application") - Log.i(GALLERY_SCREEN, "path: $resourcePath") - Log.i(GALLERY_SCREEN, "action: $actionType") + Timber.i( + GALLERY_SCREEN, + "Opening resource in an external application " + + "path: $resourcePath" + + "action: $actionType" + ) val intent = getExternalAppIntent(resourcePath, actionType, detachProcess) val title = when (actionType) { @@ -544,8 +546,10 @@ class GalleryFragment : addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } } - Log.d(GALLERY_SCREEN, "URI: ${intent.data}") - Log.d(GALLERY_SCREEN, "MIME: ${intent.type}") + Timber.d( + GALLERY_SCREEN, + "URI: ${intent.data}" + "MIME: ${intent.type}" + ) return intent } @@ -570,7 +574,7 @@ class GalleryFragment : setOnClickListener { val position = binding.viewPager.currentItem - Log.d( + Timber.d( GALLERY_SCREEN, "[edit_tags] clicked at position $position" ) diff --git a/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/gallery/GalleryPresenter.kt b/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/gallery/GalleryPresenter.kt index 31aade20..58255197 100644 --- a/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/gallery/GalleryPresenter.kt +++ b/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/gallery/GalleryPresenter.kt @@ -1,6 +1,5 @@ package dev.arkbuilders.navigator.presentation.screen.gallery -import android.util.Log import androidx.recyclerview.widget.DiffUtil import dev.arkbuilders.arkfilepicker.folders.RootAndFav import dev.arkbuilders.arklib.ResourceId @@ -25,6 +24,7 @@ import dev.arkbuilders.arklib.user.tags.TagsStorageRepo import dev.arkbuilders.arklib.utils.ImageUtils import dev.arkbuilders.arklib.utils.extension import dev.arkbuilders.components.scorewidget.ScoreWidgetController +import dev.arkbuilders.navigator.analytics.gallery.GalleryAnalytics import dev.arkbuilders.navigator.data.preferences.PreferenceKey import dev.arkbuilders.navigator.data.preferences.Preferences import dev.arkbuilders.navigator.data.stats.StatsStorage @@ -59,7 +59,7 @@ class GalleryPresenter( startAt: Int, private var selectingEnabled: Boolean, private val selectedResources: MutableList, - private val defaultDispatcher: CoroutineDispatcher = Dispatchers.IO + private val defaultDispatcher: CoroutineDispatcher = Dispatchers.IO, ) : MvpPresenter() { val scoreWidgetController = ScoreWidgetController( @@ -129,8 +129,12 @@ class GalleryPresenter( lateinit var handleGalleryExternalChangesUseCase: HandleGalleryExternalChangesUseCase + @Inject + lateinit var analytics: GalleryAnalytics + override fun onFirstViewAttach() { - Log.d(GALLERY_SCREEN, "first view attached in GalleryPresenter") + analytics.trackScreen() + Timber.d(GALLERY_SCREEN, "first view attached in GalleryPresenter") super.onFirstViewAttach() presenterScope.launch { viewState.init() @@ -223,7 +227,8 @@ class GalleryPresenter( } fun onOpenFabClick() = presenterScope.launch { - Log.d(GALLERY_SCREEN, "[open_resource] clicked at position $currentPos") + analytics.trackResOpen() + Timber.d(GALLERY_SCREEN, "[open_resource] clicked at position $currentPos") val id = currentItem.id() val path = index.getPath(id)!! @@ -239,14 +244,16 @@ class GalleryPresenter( } fun onInfoFabClick() = presenterScope.launch { - Log.d(GALLERY_SCREEN, "[info_resource] clicked at position $currentPos") + analytics.trackResInfo() + Timber.d(GALLERY_SCREEN, "[info_resource] clicked at position $currentPos") val path = index.getPath(currentItem.id())!! viewState.showInfoAlert(path, currentItem.resource, currentItem.metadata) } fun onShareFabClick() = presenterScope.launch { - Log.d(GALLERY_SCREEN, "[share_resource] clicked at position $currentPos") + analytics.trackResShare() + Timber.d(GALLERY_SCREEN, "[share_resource] clicked at position $currentPos") val path = index.getPath(currentItem.id())!! if (currentItem.metadata is Metadata.Link) { @@ -259,13 +266,15 @@ class GalleryPresenter( } fun onEditFabClick() = presenterScope.launch { - Log.d(GALLERY_SCREEN, "[edit_resource] clicked at position $currentPos") + analytics.trackResEdit() + Timber.d(GALLERY_SCREEN, "[edit_resource] clicked at position $currentPos") val path = index.getPath(currentItem.id())!! viewState.editResource(path) } fun onRemoveFabClick() = presenterScope.launch(NonCancellable) { - Log.d(GALLERY_SCREEN, "[remove_resource] clicked at position $currentPos") + analytics.trackResRemove() + Timber.d(GALLERY_SCREEN, "[remove_resource] clicked at position $currentPos") deleteResource(currentItem.id()) galleryItems.removeAt(currentPos) @@ -279,6 +288,7 @@ class GalleryPresenter( } fun onTagSelected(tag: Tag) { + analytics.trackTagSelect() router.navigateTo( Screens.ResourcesScreenWithSelectedTag( rootAndFav, tag @@ -287,6 +297,7 @@ class GalleryPresenter( } fun onTagRemove(tag: Tag) = presenterScope.launch(NonCancellable) { + analytics.trackTagRemove() val id = currentItem.id() val tags = tagsStorage.getTags(id) @@ -299,7 +310,7 @@ class GalleryPresenter( ) ) - Log.d(GALLERY_SCREEN, "setting new tags $newTags to $currentItem") + Timber.d(GALLERY_SCREEN, "setting new tags $newTags to $currentItem") tagsStorage.setTags(id, newTags) tagsStorage.persist() @@ -325,6 +336,7 @@ class GalleryPresenter( } fun onEditTagsDialogBtnClick() { + analytics.trackTagsEdit() viewState.showEditTagsDialog(currentItem.id()) } @@ -357,7 +369,7 @@ class GalleryPresenter( } private suspend fun deleteResource(resource: ResourceId) { - Log.d(GALLERY_SCREEN, "deleting resource $resource") + Timber.d(GALLERY_SCREEN, "deleting resource $resource") val path = index.getPath(resource) @@ -404,7 +416,7 @@ class GalleryPresenter( } fun onBackClick() { - Log.d(GALLERY_SCREEN, "quitting from GalleryPresenter") + Timber.d(GALLERY_SCREEN, "quitting from GalleryPresenter") viewState.notifySelectedChanged(selectedResources) viewState.exitFullscreen() router.exit() diff --git a/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/main/MainActivity.kt b/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/main/MainActivity.kt index 8858540a..16bdc0d6 100644 --- a/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/main/MainActivity.kt +++ b/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/main/MainActivity.kt @@ -1,7 +1,6 @@ package dev.arkbuilders.navigator.presentation.screen.main import android.os.Bundle -import android.util.Log import androidx.appcompat.app.AppCompatActivity import androidx.core.view.forEach import androidx.core.view.isVisible @@ -20,6 +19,7 @@ import kotlinx.coroutines.launch import ru.terrakok.cicerone.NavigatorHolder import dev.arkbuilders.arkfilepicker.folders.FoldersRepo import dev.arkbuilders.arkfilepicker.folders.RootAndFav +import timber.log.Timber import javax.inject.Inject class MainActivity : AppCompatActivity(R.layout.activity_main) { @@ -50,30 +50,30 @@ class MainActivity : AppCompatActivity(R.layout.activity_main) { } fun init() { - Log.d(MAIN, "initializing") + Timber.d(MAIN, "initializing") binding.bottomNavigation.setOnApplyWindowInsetsListener(null) binding.bottomNavigation.setOnItemSelectedListener { item -> when (item.itemId) { R.id.page_settings -> { - Log.d(MAIN, "switching to Settings screen") + Timber.d(MAIN, "switching to Settings screen") router.newRootScreen(Screens.SettingsScreen()) true } R.id.page_roots -> { - Log.d(MAIN, "switching to Folders screen") + Timber.d(MAIN, "switching to Folders screen") router.replaceScreen(Screens.FoldersScreen()) true } R.id.page_tags -> { - Log.d(MAIN, "switching to Resources screen") + Timber.d(MAIN, "switching to Resources screen") lifecycleScope.launch { val folders = foldersRepo.provideFolders() if (folders.isEmpty()) { enterResourceFragmentFailed() } else { - Log.d(MAIN, "switching to Resources screen") + Timber.d(MAIN, "switching to Resources screen") router.newRootScreen( Screens.ResourcesScreen( RootAndFav( @@ -89,7 +89,7 @@ class MainActivity : AppCompatActivity(R.layout.activity_main) { } else -> { - Log.w(MAIN, "no handler found") + Timber.w(MAIN, "no handler found") true } } @@ -109,7 +109,7 @@ class MainActivity : AppCompatActivity(R.layout.activity_main) { fun setSelectedTab(menuItemID: Int) { binding.bottomNavigation.apply { - Log.d( + Timber.d( MAIN, "tab with id $menuItemID selected," + "title: ${menu.findItem(menuItemID).title}" @@ -125,13 +125,13 @@ class MainActivity : AppCompatActivity(R.layout.activity_main) { } override fun onResumeFragments() { - Log.d(MAIN, "resuming fragments in MainActivity") + Timber.d(MAIN, "resuming fragments in MainActivity") super.onResumeFragments() navigatorHolder.setNavigator(navigator) } override fun onPause() { - Log.d(MAIN, "pausing MainActivity") + Timber.d(MAIN, "pausing MainActivity") super.onPause() navigatorHolder.removeNavigator() } diff --git a/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/resources/ResourcesFragment.kt b/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/resources/ResourcesFragment.kt index fda2e743..1a7cf4f3 100644 --- a/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/resources/ResourcesFragment.kt +++ b/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/resources/ResourcesFragment.kt @@ -3,7 +3,6 @@ package dev.arkbuilders.navigator.presentation.screen.resources import android.content.Intent import android.os.Bundle import android.os.SystemClock -import android.util.Log import android.view.MotionEvent import android.view.View import androidx.activity.addCallback @@ -45,6 +44,7 @@ import dev.arkbuilders.arkfilepicker.folders.RootAndFav import dev.arkbuilders.arkfilepicker.presentation.onArkPathPicked import dev.arkbuilders.arklib.ResourceId import dev.arkbuilders.arklib.user.tags.Tag +import timber.log.Timber import java.nio.file.Path import javax.inject.Inject import kotlin.io.path.Path @@ -70,7 +70,7 @@ class ResourcesFragment : requireArguments()[ROOT_AND_FAV_KEY] as RootAndFav, requireArguments().getString(SELECTED_TAG_KEY) ).apply { - Log.d(RESOURCES_SCREEN, "creating ResourcesPresenter") + Timber.d(RESOURCES_SCREEN, "creating ResourcesPresenter") App.instance.appComponent.inject(this) } } @@ -106,7 +106,7 @@ class ResourcesFragment : private var isAscending = true override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - Log.d(RESOURCES_SCREEN, "view created in ResourcesFragment") + Timber.d(RESOURCES_SCREEN, "view created in ResourcesFragment") super.onViewCreated(view, savedInstanceState) tagsLayoutBinding = TagSelectorTagsLayoutBinding.bind(binding.layoutTags) dragHandlerBinding = @@ -122,7 +122,7 @@ class ResourcesFragment : override fun init(ascending: Boolean, sortByScoresEnabled: Boolean) = with(binding) { - Log.d(RESOURCES_SCREEN, "initializing ResourcesFragment") + Timber.d(RESOURCES_SCREEN, "initializing ResourcesFragment") initResultListeners() initMenuListeners() @@ -150,7 +150,7 @@ class ResourcesFragment : } dragHandlerBinding .switchScores.setOnCheckedChangeListener { _, isChecked -> - Log.d( + Timber.d( RESOURCES_SCREEN, "sorting by scores " + "${if (isChecked) "enabled" else "disabled"}" @@ -177,7 +177,7 @@ class ResourcesFragment : } override fun onResume() { - Log.d(RESOURCES_SCREEN, "resuming in ResourcesFragment") + Timber.d(RESOURCES_SCREEN, "resuming in ResourcesFragment") super.onResume() updateDragHandlerBias() } @@ -462,13 +462,13 @@ class ResourcesFragment : SystemClock.uptimeMillis() - selectorDragStartTime val travelDelta = selectorDragStartBias - (1f - selectorHeight) val travelSpeed = 100f * travelDelta / (travelTime / 1000f) - Log.d( + Timber.d( RESOURCES_SCREEN, - "draggable bar of tags selector was moved:" + "draggable bar of tags selector was moved:\n" + + "delta=${100f * travelDelta}%" + + "time=${travelTime}ms" + + "speed=$travelSpeed%/sec" ) - Log.d(RESOURCES_SCREEN, "delta=${100f * travelDelta}%") - Log.d(RESOURCES_SCREEN, "time=${travelTime}ms") - Log.d(RESOURCES_SCREEN, "speed=$travelSpeed%/sec") if (travelTime > DRAG_TRAVEL_TIME_THRESHOLD && abs(travelDelta) > DRAG_TRAVEL_DELTA_THRESHOLD && diff --git a/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/resources/ResourcesPresenter.kt b/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/resources/ResourcesPresenter.kt index f2c87e01..5c6434b8 100644 --- a/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/resources/ResourcesPresenter.kt +++ b/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/resources/ResourcesPresenter.kt @@ -1,6 +1,5 @@ package dev.arkbuilders.navigator.presentation.screen.resources -import android.util.Log import dev.arkbuilders.arkfilepicker.folders.FoldersRepo import dev.arkbuilders.arkfilepicker.folders.RootAndFav import dev.arkbuilders.arklib.ResourceId @@ -20,6 +19,7 @@ import dev.arkbuilders.arklib.user.tags.TagsStorageRepo import dev.arkbuilders.components.tagselector.QueryMode import dev.arkbuilders.components.tagselector.TagSelectorController import dev.arkbuilders.components.tagselector.TagsSorting +import dev.arkbuilders.navigator.analytics.resources.ResourcesAnalytics import dev.arkbuilders.navigator.data.preferences.PreferenceKey import dev.arkbuilders.navigator.data.preferences.Preferences import dev.arkbuilders.navigator.data.stats.StatsStorage @@ -97,6 +97,9 @@ class ResourcesPresenter( @DefaultDispatcher lateinit var defaultDispatcher: CoroutineDispatcher + @Inject + lateinit var analytics: ResourcesAnalytics + private val messageFlow: MutableSharedFlow = MutableSharedFlow() lateinit var index: ResourceIndex @@ -119,6 +122,7 @@ class ResourcesPresenter( presenterScope, kindToString = { stringProvider.kindToString(it) }, tagSortCriteria = { tagsSorting -> + analytics.trackTagSortCriteria(tagsSorting) when (tagsSorting) { TagsSorting.POPULARITY -> error("TagsSorting.POPULARITY must be handled before") @@ -153,6 +157,7 @@ class ResourcesPresenter( preferences.set(PreferenceKey.ShowKinds, it) }, onQueryModeChangedCB = { + analytics.trackQueryModeChanged(it) presenterScope.launch(mainDispatcher) { viewState.updateMenu(it) } @@ -160,7 +165,8 @@ class ResourcesPresenter( ) override fun onFirstViewAttach() { - Log.d(RESOURCES_SCREEN, "first view attached in ResourcesPresenter") + analytics.trackScreen() + Timber.d(RESOURCES_SCREEN, "first view attached in ResourcesPresenter") super.onFirstViewAttach() presenterScope.launch { @@ -207,6 +213,7 @@ class ResourcesPresenter( tagStorage = tagsStorageRepo.provide(index) scoreStorage = scoreStorageRepo.provide(index) } catch (e: StorageException) { + analytics.trackStorageProvideException(e) viewState.displayStorageException( e.label, e.msg @@ -262,6 +269,7 @@ class ResourcesPresenter( fun onMoveSelectedResourcesClicked( directoryToMove: Path ) = presenterScope.launch(ioDispatcher) { + analytics.trackMoveSelectedRes() withContext(mainDispatcher) { viewState.setProgressVisibility(true, "Moving selected resources") } @@ -294,6 +302,7 @@ class ResourcesPresenter( fun onCopySelectedResourcesClicked( directoryToCopy: Path ) = presenterScope.launch(mainDispatcher) { + analytics.trackCopySelectedRes() val resourcesToCopy = gridPresenter .resources .filter { it.isSelected } @@ -324,6 +333,7 @@ class ResourcesPresenter( } fun onShuffleSwitchedOn() = presenterScope.launch(defaultDispatcher) { + analytics.trackResShuffle() gridPresenter.shuffleResources() } @@ -348,6 +358,7 @@ class ResourcesPresenter( fun allowResettingScores() = gridPresenter.allowResettingScores() fun onShareSelectedResourcesClicked() = presenterScope.launch { + analytics.trackShareSelectedRes() val selected = gridPresenter .resources .filter { it.isSelected } @@ -357,6 +368,7 @@ class ResourcesPresenter( } fun onRemoveSelectedResourcesClicked() = presenterScope.launch(ioDispatcher) { + analytics.trackRemoveSelectedRes() withContext(mainDispatcher) { viewState.setProgressVisibility(true, "Removing selected resources") } diff --git a/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/resources/adapter/ResourcesGridPresenter.kt b/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/resources/adapter/ResourcesGridPresenter.kt index 640679c5..c3fb6eeb 100644 --- a/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/resources/adapter/ResourcesGridPresenter.kt +++ b/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/resources/adapter/ResourcesGridPresenter.kt @@ -1,14 +1,26 @@ package dev.arkbuilders.navigator.presentation.screen.resources.adapter -import android.util.Log +import dev.arkbuilders.arkfilepicker.folders.RootAndFav +import dev.arkbuilders.arklib.ResourceId +import dev.arkbuilders.arklib.data.index.Resource +import dev.arkbuilders.arklib.data.index.ResourceIndex +import dev.arkbuilders.arklib.data.meta.MetadataProcessor +import dev.arkbuilders.arklib.data.preview.PreviewProcessor +import dev.arkbuilders.arklib.user.score.ScoreStorage +import dev.arkbuilders.arklib.user.tags.TagStorage +import dev.arkbuilders.navigator.analytics.resources.ResourcesAnalytics import dev.arkbuilders.navigator.data.preferences.PreferenceKey import dev.arkbuilders.navigator.data.preferences.Preferences import dev.arkbuilders.navigator.data.utils.LogTags.RESOURCES_SCREEN import dev.arkbuilders.navigator.data.utils.Sorting +import dev.arkbuilders.navigator.di.modules.DefaultDispatcher +import dev.arkbuilders.navigator.di.modules.IoDispatcher +import dev.arkbuilders.navigator.di.modules.MainDispatcher import dev.arkbuilders.navigator.presentation.navigation.AppRouter import dev.arkbuilders.navigator.presentation.navigation.Screens import dev.arkbuilders.navigator.presentation.screen.resources.ResourcesPresenter import dev.arkbuilders.navigator.presentation.screen.resources.ResourcesView +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.launchIn @@ -17,18 +29,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.plus import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext -import dev.arkbuilders.arkfilepicker.folders.RootAndFav -import dev.arkbuilders.arklib.ResourceId -import dev.arkbuilders.arklib.data.index.Resource -import dev.arkbuilders.arklib.data.index.ResourceIndex -import dev.arkbuilders.arklib.data.meta.MetadataProcessor -import dev.arkbuilders.arklib.data.preview.PreviewProcessor -import dev.arkbuilders.arklib.user.score.ScoreStorage -import dev.arkbuilders.arklib.user.tags.TagStorage -import dev.arkbuilders.navigator.di.modules.DefaultDispatcher -import dev.arkbuilders.navigator.di.modules.IoDispatcher -import dev.arkbuilders.navigator.di.modules.MainDispatcher -import kotlinx.coroutines.CoroutineDispatcher +import timber.log.Timber import java.nio.file.Files import javax.inject.Inject import kotlin.io.path.notExists @@ -63,6 +64,9 @@ class ResourcesGridPresenter( @DefaultDispatcher lateinit var defaultDispatcher: CoroutineDispatcher + @Inject + lateinit var analytics: ResourcesAnalytics + var resources = listOf() private set private var selection = listOf() @@ -90,7 +94,7 @@ class ResourcesGridPresenter( fun bindView(view: FileItemViewHolder) = runBlocking { val item = selection[view.position()] - Log.d(RESOURCES_SCREEN, "binding view for resource ${item.id()}") + Timber.d(RESOURCES_SCREEN, "binding view for resource ${item.id()}") val path = index.getPath(item.id())!! val score = scoreStorage.getScore(item.id()) @@ -98,7 +102,7 @@ class ResourcesGridPresenter( view.reset(selectingEnabled, item.isSelected) view.setText(path.fileName.toString(), shortFileNames) view.displayScore(sortByScores, score) - Log.d( + Timber.d( RESOURCES_SCREEN, "binding score $score for resource ${item.id()}" ) @@ -127,6 +131,7 @@ class ResourcesGridPresenter( } fun onItemClick(pos: Int) = scope.launch { + analytics.trackResClick() val allPaths = index.allPaths() val containsNotExistingResource = selection.any { item -> allPaths[item.id()]?.notExists() == true @@ -193,6 +198,7 @@ class ResourcesGridPresenter( preferences.flow(PreferenceKey.Sorting).onEach { intSorting -> val newSorting = Sorting.values()[intSorting] + analytics.trackResSortCriteria(newSorting) if (sorting != newSorting) updateSorting(newSorting) }.launchIn(scope + Dispatchers.IO) @@ -237,7 +243,7 @@ class ResourcesGridPresenter( suspend fun shuffleResources() = withContext(defaultDispatcher) { selection = selection.shuffled() - Log.d( + Timber.d( RESOURCES_SCREEN, "reordering resources randomly" ) @@ -249,7 +255,7 @@ class ResourcesGridPresenter( suspend fun unShuffleResources() = withContext(defaultDispatcher) { sortAllResources() sortSelectionAndUpdateAdapter() - Log.d( + Timber.d( RESOURCES_SCREEN, "reordering resources back from random order" ) @@ -376,7 +382,7 @@ class ResourcesGridPresenter( resources = resources.reversed() } } - Log.d( + Timber.d( RESOURCES_SCREEN, "sorting by $sorting of ${resources.size} " + "resources took $sortTime milliseconds" diff --git a/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/settings/SettingsFragment.kt b/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/settings/SettingsFragment.kt index 0cc70eea..f5efe6a5 100644 --- a/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/settings/SettingsFragment.kt +++ b/app/src/main/java/dev/arkbuilders/navigator/presentation/screen/settings/SettingsFragment.kt @@ -2,7 +2,6 @@ package dev.arkbuilders.navigator.presentation.screen.settings import android.content.Context import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -16,20 +15,22 @@ import com.mikepenz.fastadapter.FastAdapter import com.mikepenz.fastadapter.adapters.ItemAdapter import com.mikepenz.fastadapter.binding.AbstractBindingItem import dev.arkbuilders.navigator.R -import dev.arkbuilders.navigator.databinding.FragmentSettingsBinding -import dev.arkbuilders.navigator.databinding.ItemBooleanPreferenceBinding +import dev.arkbuilders.navigator.analytics.settings.SettingsAnalytics import dev.arkbuilders.navigator.data.preferences.PreferenceKey import dev.arkbuilders.navigator.data.preferences.Preferences +import dev.arkbuilders.navigator.data.utils.LogTags.SETTINGS_SCREEN +import dev.arkbuilders.navigator.databinding.FragmentSettingsBinding +import dev.arkbuilders.navigator.databinding.ItemBooleanPreferenceBinding import dev.arkbuilders.navigator.presentation.App -import dev.arkbuilders.navigator.presentation.screen.main.MainActivity -import dev.arkbuilders.navigator.presentation.navigation.AppRouter -import dev.arkbuilders.navigator.presentation.navigation.Screens import dev.arkbuilders.navigator.presentation.dialog.ConfirmationDialogFragment import dev.arkbuilders.navigator.presentation.dialog.InfoDialogFragment +import dev.arkbuilders.navigator.presentation.navigation.AppRouter +import dev.arkbuilders.navigator.presentation.navigation.Screens +import dev.arkbuilders.navigator.presentation.screen.main.MainActivity import dev.arkbuilders.navigator.presentation.utils.toast -import dev.arkbuilders.navigator.data.utils.LogTags.SETTINGS_SCREEN import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import timber.log.Timber import javax.inject.Inject class SettingsFragment : Fragment(R.layout.fragment_settings) { @@ -43,6 +44,9 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) { @Inject lateinit var router: AppRouter + @Inject + lateinit var settingsAnalytics: SettingsAnalytics + //region booleanPreferenceModels private val booleanPreferenceModels = listOf( @@ -113,6 +117,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + settingsAnalytics.trackScreen() (activity as MainActivity).setSelectedTab(R.id.page_settings) (requireActivity() as MainActivity).setBottomNavigationVisibility(true) @@ -128,7 +133,8 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) { requireContext(), childFragmentManager, lifecycleScope, - preferences + preferences, + settingsAnalytics ) } ) @@ -164,7 +170,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) { companion object { fun newInstance() = SettingsFragment().apply { - Log.d(SETTINGS_SCREEN, "creating new instance") + Timber.d(SETTINGS_SCREEN, "creating new instance") } } } @@ -183,7 +189,8 @@ private class BooleanPreferenceItem( val ctx: Context, val fragmentManager: FragmentManager, val lifecycleScope: CoroutineScope, - val preferences: Preferences + val preferences: Preferences, + val settingsAnalytics: SettingsAnalytics ) : AbstractBindingItem() { override val type = R.id.fastadapter_item private var preferenceEnabled = false @@ -220,6 +227,10 @@ private class BooleanPreferenceItem( if (preferenceEnabled) model.toastEnabled else model.toastDisabled ) + settingsAnalytics.trackBooleanPref( + ctx.getString(model.name), + preferenceEnabled + ) lifecycleScope.launch { preferences.set(model.key, preferenceEnabled) } diff --git a/app/src/main/java/dev/arkbuilders/navigator/presentation/view/UserSwitchMaterial.kt b/app/src/main/java/dev/arkbuilders/navigator/presentation/view/UserSwitchMaterial.kt index 66fd0c7d..a2c8a58c 100644 --- a/app/src/main/java/dev/arkbuilders/navigator/presentation/view/UserSwitchMaterial.kt +++ b/app/src/main/java/dev/arkbuilders/navigator/presentation/view/UserSwitchMaterial.kt @@ -2,10 +2,10 @@ package dev.arkbuilders.navigator.presentation.view import android.content.Context import android.util.AttributeSet -import android.util.Log import android.widget.CompoundButton import com.google.android.material.switchmaterial.SwitchMaterial import dev.arkbuilders.navigator.data.utils.LogTags.SETTINGS_SCREEN +import timber.log.Timber class UserSwitchMaterial( context: Context, @@ -17,7 +17,7 @@ class UserSwitchMaterial( fun setOnUserCheckedChangeListener( callback: (isChecked: Boolean) -> Unit ) { - Log.d( + Timber.d( SETTINGS_SCREEN, "setOnUserCheckedChangeListener: ${this.id}, " + "$isChecked" ) diff --git a/build.gradle b/build.gradle index 47f1eb50..ab4a9803 100644 --- a/build.gradle +++ b/build.gradle @@ -43,6 +43,7 @@ buildscript { core_testing_version = "2.1.0" ffmeg_media_retriever_version = "1.0.14" tika_core = "2.4.0" + matomo_analytics_version = "4.1.4" } repositories { google() diff --git a/gradle.properties b/gradle.properties index ed9a0498..11b28ac9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,3 +22,4 @@ kotlin.code.style=official android.defaults.buildfeatures.buildconfig=true android.nonTransitiveRClass=false android.nonFinalResIds=false +android.enableD8.desugaring = true