Skip to content

Commit

Permalink
Merge pull request #50 from techbeloved/47-feature-discovery
Browse files Browse the repository at this point in the history
Add feature discovery using tapTargetView library
Add analytics
  • Loading branch information
odifek committed Sep 19, 2022
2 parents e420baa + 14d3490 commit 9c9429f
Show file tree
Hide file tree
Showing 10 changed files with 128 additions and 12 deletions.
6 changes: 4 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ android {
applicationId "com.techbeloved.hymnbook"
minSdkVersion 21
targetSdkVersion 32
versionCode 24
versionName "2.3.1"
versionCode 25
versionName "2.3.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

vectorDrawables.useSupportLibrary true
Expand Down Expand Up @@ -237,6 +237,8 @@ dependencies {
implementation "androidx.media:media:1.6.0"

implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2'

implementation 'com.getkeepsafe.taptargetview:taptargetview:1.13.3'
}
repositories {
mavenCentral()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.techbeloved.hymnbook.data

import com.techbeloved.hymnbook.data.model.NewFeature
import com.techbeloved.hymnbook.data.model.NightMode
import io.reactivex.Completable
import io.reactivex.Observable
import io.reactivex.Single

Expand Down Expand Up @@ -66,4 +68,8 @@ interface SharedPreferencesRepo {
*/
fun updatePreferSheetMusic(value: Boolean)

fun newFeatures(): Observable<List<NewFeature>>

fun shown(feature: NewFeature): Completable

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package com.techbeloved.hymnbook.data
import android.content.res.Resources
import com.f2prateek.rx.preferences2.RxSharedPreferences
import com.techbeloved.hymnbook.R
import com.techbeloved.hymnbook.data.model.NewFeature
import com.techbeloved.hymnbook.data.model.NightMode
import io.reactivex.Completable
import io.reactivex.Observable
import io.reactivex.Single
import javax.inject.Inject
Expand Down Expand Up @@ -91,4 +93,23 @@ class SharedPreferencesRepoImp @Inject constructor(
rxPreferences.getBoolean(resources.getString(R.string.pref_key_prefer_sheet_music))
preferSheetMusicPref.set(value)
}

override fun newFeatures(): Observable<List<NewFeature>> {
return newFeaturesPreference()
.asObservable()
.map { features ->
val shownFeatures = features.map(NewFeature::valueOf)
NewFeature.values().filterNot { shownFeatures.contains(it) }
}
}

private fun newFeaturesPreference() = rxPreferences.getStringSet("NewFeaturesShown", emptySet())

override fun shown(feature: NewFeature): Completable = Completable.create { emitter ->
val pref = newFeaturesPreference()
val current = pref.get().map(NewFeature::valueOf)
val newSet = (current + feature).map(NewFeature::name).toSet()
pref.set(newSet)
emitter.onComplete()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.techbeloved.hymnbook.data.analytics

import androidx.core.os.bundleOf
import com.google.firebase.analytics.FirebaseAnalytics
import javax.inject.Inject

class AppAnalytics @Inject constructor(private val firebaseAnalytics: FirebaseAnalytics) {

fun logEvent(name: String = "userEvent", data: List<Pair<String, Any>>) {
val bundle = bundleOf(
*data.toTypedArray()
)
firebaseAnalytics.logEvent(name, bundle)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.techbeloved.hymnbook.data.model

enum class NewFeature {
SheetMusic
}
4 changes: 4 additions & 0 deletions app/src/main/java/com/techbeloved/hymnbook/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.techbeloved.hymnbook.di

import android.content.Context
import androidx.work.WorkManager
import com.google.firebase.analytics.FirebaseAnalytics
import com.techbeloved.hymnbook.utils.SchedulerProvider
import dagger.Module
import dagger.Provides
Expand Down Expand Up @@ -40,4 +41,7 @@ object AppModule {
ignoreUnknownKeys = true
isLenient = true
}

@Provides
fun provideFirebaseAnalytics(@ApplicationContext context: Context): FirebaseAnalytics = FirebaseAnalytics.getInstance(context)
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ import androidx.preference.PreferenceManager
import androidx.viewpager.widget.ViewPager
import com.f2prateek.rx.preferences2.Preference
import com.f2prateek.rx.preferences2.RxSharedPreferences
import com.getkeepsafe.taptargetview.TapTarget
import com.getkeepsafe.taptargetview.TapTargetView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.snackbar.Snackbar
import com.techbeloved.hymnbook.R
import com.techbeloved.hymnbook.data.SharedPreferencesRepo
import com.techbeloved.hymnbook.data.model.NewFeature
import com.techbeloved.hymnbook.data.model.NightMode
import com.techbeloved.hymnbook.databinding.DialogTempoSelectorBinding
import com.techbeloved.hymnbook.databinding.FragmentDetailPagerBinding
Expand Down Expand Up @@ -371,8 +374,6 @@ abstract class BaseDetailPagerFragment : Fragment() {
}

private fun showQuickSettingsBottomSheet() {
//val quickSettingsFragment = QuickSettingsFragment()
//fragmentManager?.let { quickSettingsFragment.show(it, quickSettingsFragment.tag) }
if (quickSettingsSheet.state != BottomSheetBehavior.STATE_EXPANDED) {
quickSettingsSheet.state = BottomSheetBehavior.STATE_EXPANDED
}
Expand All @@ -394,10 +395,37 @@ abstract class BaseDetailPagerFragment : Fragment() {
_currentHymnId = value
}

protected fun showNewFeatureHighlight(feature: NewFeature, title: String) {
TapTargetView.showFor(
requireActivity(),
TapTarget.forView(
binding.toolbarDetail.findViewById(R.id.menu_detail_quick_settings),
title
)
.outerCircleColor(R.color.primary)
.outerCircleAlpha(0.96f)
.targetCircleColor(R.color.primary_white)
.titleTextSize(20)
.descriptionTextSize(12)
.tintTarget(true)
.cancelable(true)
.targetRadius(60),
object : TapTargetView.Listener() {
override fun onTargetClick(view: TapTargetView?) {
super.onTargetClick(view)
newFeatureShown(feature)
showQuickSettingsBottomSheet()
}
}
)
}

/**
* Called upon to request sharing of hymn item
*/
abstract fun initiateContentSharing()

abstract fun newFeatureShown(feature: NewFeature)
}

const val EXTRA_CURRENT_ITEM_ID = "currentItemId"
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import androidx.viewpager.widget.PagerAdapter
import com.google.android.material.snackbar.Snackbar
import com.techbeloved.hymnbook.R
import com.techbeloved.hymnbook.data.model.HymnNumber
import com.techbeloved.hymnbook.data.model.NewFeature
import com.techbeloved.hymnbook.sheetmusic.SheetMusicDetailFragment
import com.techbeloved.hymnbook.usecases.Lce
import com.techbeloved.hymnbook.utils.DepthPageTransformer
Expand Down Expand Up @@ -76,6 +77,16 @@ class DetailPagerFragment : BaseDetailPagerFragment() {
}
}
}

viewModel.newFeatures.observe(viewLifecycleOwner) { feature ->
when (feature) {
NewFeature.SheetMusic ->
showNewFeatureHighlight(feature, getString(R.string.sheet_music_discovery))
null -> {
// Nothing to do
}
}
}
}

private fun showShareError(error: Throwable) {
Expand Down Expand Up @@ -137,6 +148,10 @@ class DetailPagerFragment : BaseDetailPagerFragment() {
)
}

override fun newFeatureShown(feature: NewFeature) {
viewModel.newFeatureShown(feature)
}

@SuppressLint("WrongConstant")
inner class DetailPagerAdapter(fm: FragmentManager) :
FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
Expand All @@ -147,11 +162,10 @@ class DetailPagerFragment : BaseDetailPagerFragment() {

override fun getItem(position: Int): Fragment {
val item = hymnIndices[position]
val hymnToShow = if (position < hymnIndices.size) item.number else 1
return if (preferSheetMusic && item.hasSheetMusic) {
SheetMusicDetailFragment().apply { init(hymnToShow) }
SheetMusicDetailFragment().apply { init(item.number) }
} else {
DetailFragment().apply { init(hymnToShow) }
DetailFragment().apply { init(item.number) }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
import com.techbeloved.hymnbook.data.ShareLinkProvider
import com.techbeloved.hymnbook.data.SharedPreferencesRepo
import com.techbeloved.hymnbook.data.analytics.AppAnalytics
import com.techbeloved.hymnbook.data.model.HymnNumber
import com.techbeloved.hymnbook.data.model.NewFeature
import com.techbeloved.hymnbook.data.repo.HymnsRepository
import com.techbeloved.hymnbook.playlists.PlaylistsRepo
import com.techbeloved.hymnbook.usecases.Lce
Expand All @@ -35,7 +37,8 @@ class HymnPagerViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val schedulerProvider: SchedulerProvider,
private val shareLinkProvider: ShareLinkProvider,
private val preferencesRepo: SharedPreferencesRepo
private val preferencesRepo: SharedPreferencesRepo,
private val analytics: AppAnalytics,
) : ViewModel() {

private val detailArgs = DetailPagerFragmentArgs.fromSavedStateHandle(savedStateHandle)
Expand All @@ -59,12 +62,33 @@ class HymnPagerViewModel @Inject constructor(
_hymnIndicesLiveData.value = Lce.Error("Failed to load indices of hymns")
}

val newFeatures = MutableLiveData<NewFeature?>()


private val compositeDisposable = CompositeDisposable()

init {
setupHymnDisplayPreference()
loadHymnIndices()
getCategoryHeader()
checkNewFeatures()
}

private fun checkNewFeatures() {
preferencesRepo.newFeatures().subscribeOn(schedulerProvider.io())
.observeOn(schedulerProvider.ui())
.subscribe({ features ->
newFeatures.value = features.firstOrNull()
}, Timber::w)
.let(compositeDisposable::add)
}

fun newFeatureShown(feature: NewFeature) {
preferencesRepo.shown(feature)
.subscribeOn(schedulerProvider.io())
.observeOn(schedulerProvider.ui())
.subscribe({}, Timber::w).let(compositeDisposable::add)
analytics.logEvent(data = listOf("NewFeatureShown" to feature.name))
}

fun loadHymnIndices(@SortBy sortBy: Int = BY_NUMBER) {
Expand Down Expand Up @@ -168,10 +192,6 @@ class HymnPagerViewModel @Inject constructor(
.subscribe({ _preferSheetMusic.value = it }, { Timber.w(it) })
.also { compositeDisposable.add(it) }
}

companion object {
const val CATEGORY_URI_ARG = "categoryUriArgument"
}
}

const val BY_TITLE = 12
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@
<string name="pref_key_hymn_midi_files_ready" translatable="false">hymn_midi_files_ready</string>
<string name="pref_key_hymn_catalog_download_status" translatable="false">hymn_catalog_download_status</string>
<string name="pref_key_hymn_catalog_download_id" translatable="false">hymn_catalog_download_id</string>
<string name="sheet_music_discovery">Looking for sheet music? Tap here</string>

<string-array name="tempo_values">
<item>0.5×</item>
Expand Down

0 comments on commit 9c9429f

Please sign in to comment.