Skip to content

Commit

Permalink
feat: add analytics opt in in welcome screen
Browse files Browse the repository at this point in the history
  • Loading branch information
VaiTon committed Jun 28, 2021
1 parent 87c8c2f commit 94ae339
Show file tree
Hide file tree
Showing 13 changed files with 194 additions and 99 deletions.
Expand Up @@ -17,26 +17,31 @@ package openfoodfacts.github.scrachx.openfood.features.welcome

import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.ActivityInfo
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.view.View
import android.view.Window
import android.view.WindowManager
import android.widget.TextView
import androidx.core.content.edit
import androidx.core.content.res.ResourcesCompat
import androidx.core.text.HtmlCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.viewpager.widget.ViewPager
import androidx.viewpager.widget.ViewPager.OnPageChangeListener
import dagger.hilt.android.AndroidEntryPoint
import openfoodfacts.github.scrachx.openfood.R
import openfoodfacts.github.scrachx.openfood.analytics.MatomoAnalytics
import openfoodfacts.github.scrachx.openfood.databinding.ActivityWelcomeBinding
import openfoodfacts.github.scrachx.openfood.features.MainActivity
import openfoodfacts.github.scrachx.openfood.features.shared.BaseActivity
import openfoodfacts.github.scrachx.openfood.utils.PrefManager
import openfoodfacts.github.scrachx.openfood.utils.darken
import openfoodfacts.github.scrachx.openfood.utils.lighten
import javax.inject.Inject

/**
* This is the on boarding activity shown on first-run.
Expand All @@ -52,48 +57,45 @@ class WelcomeActivity : BaseActivity() {
private var _binding: ActivityWelcomeBinding? = null
private val binding get() = _binding!!

private val layouts = intArrayOf(
R.layout.welcome_slide1,
R.layout.welcome_slide2,
R.layout.welcome_slide3,
R.layout.welcome_slide4
)
private lateinit var prefManager: PrefManager
private var lastPage = false

private val screens = WelcomeScreen.values()


@Inject
lateinit var matomoAnalytics: MatomoAnalytics

@Inject
lateinit var sharedPreferences: SharedPreferences

private val viewPagerPageChangeListener = object : OnPageChangeListener {
private var currentState = 0
override fun onPageSelected(position: Int) {
refreshBottomDots(position)
if (position == layouts.lastIndex) {
binding.btnNext.text = getString(R.string.start)
binding.btnSkip.visibility = View.GONE
lastPage = true
} else {
binding.btnNext.text = getString(R.string.next)
binding.btnSkip.visibility = View.VISIBLE
lastPage = false
}
}

/**
* If user is on the last page and tries to swipe towards the next page on right then the value of
* positionOffset returned is always 0. On the other hand if the user tries to swipe towards the
* previous page on the left then the value of positionOffset returned is 0.999 and decreases as the
* user continues to swipe in the same direction. Also whenever a user tries to swipe in any
* direction the state is changed from idle to dragging and onPageScrollStateChanged is called.
* Therefore if the user is on the last page and the value of positionOffset is 0 and state is
* dragging it means that the user is trying to go to the next page on right from the last page and
* hence MainActivity is started in this case.
*/
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
if (lastPage && positionOffset == 0f && currentState == ViewPager.SCROLL_STATE_DRAGGING) {
launchHomeScreen()
}
}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) = Unit
override fun onPageScrollStateChanged(state: Int) = Unit

override fun onPageScrollStateChanged(state: Int) {
currentState = state
override fun onPageSelected(position: Int) {
refreshBottomBar(position)

when (WelcomeScreen[position]) {
WelcomeScreen.MATOMO -> {
binding.btnNext.setText(R.string.preference_analytics_bottom_sheet_grant_button)
binding.btnSkip.setText(R.string.preference_analytics_bottom_sheet_decline_button)

binding.btnNext.setTextColor(ResourcesCompat.getColor(resources, android.R.color.black, theme))
binding.btnSkip.setTextColor(ResourcesCompat.getColor(resources, android.R.color.black, theme))

binding.btnNext.setOnClickListener { saveThenLaunchHome(true) }
binding.btnSkip.setOnClickListener { saveThenLaunchHome(false) }
}
else -> {
binding.btnNext.setTextColor(ResourcesCompat.getColor(resources, android.R.color.white, theme))
binding.btnSkip.setTextColor(ResourcesCompat.getColor(resources, android.R.color.white, theme))

binding.btnNext.setText(R.string.next)
binding.btnSkip.setText(R.string.skip)
setOnClicks()
}
}
}
}

Expand All @@ -103,58 +105,68 @@ class WelcomeActivity : BaseActivity() {
if (resources.getBoolean(R.bool.portrait_only)) {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}

_binding = ActivityWelcomeBinding.inflate(layoutInflater)
setContentView(binding.root)

prefManager = PrefManager(this)
if (!prefManager.isFirstTimeLaunch) {
launchHomeScreen()
launchHome()
finish()
}

WindowCompat.setDecorFitsSystemWindows(window, false)
WindowInsetsControllerCompat(window, binding.root).hide(WindowInsetsCompat.Type.statusBars())

refreshBottomDots(0)
refreshBottomBar(0)
changeStatusBarColor()

binding.viewPager.adapter = WelcomePageAdapter(layoutInflater, layouts)
binding.viewPager.adapter = WelcomePageAdapter(layoutInflater)
binding.viewPager.addOnPageChangeListener(viewPagerPageChangeListener)
binding.btnSkip.setOnClickListener { launchHomeScreen() }

binding.btnNext.setOnClickListener {
if (nextItem < layouts.size) {
binding.viewPager.currentItem = nextItem
} else {
launchHomeScreen()
}
}
setOnClicks()
}

override fun onDestroy() {
_binding = null
super.onDestroy()
}

private fun refreshBottomDots(currentPage: Int) {
val colorsActive = resources.getIntArray(R.array.array_dot_active)
val colorsInactive = resources.getIntArray(R.array.array_dot_inactive)
private fun setOnClicks() {
binding.btnSkip.setOnClickListener { binding.viewPager.currentItem = screens.size - 1 }
binding.btnNext.setOnClickListener { binding.viewPager.currentItem = nextItem }
}

private fun saveThenLaunchHome(grant: Boolean) {
saveAnalyticsReportingPref(grant)
matomoAnalytics.onAnalyticsEnabledToggled(grant)
launchHome()
}

private fun saveAnalyticsReportingPref(value: Boolean) {
sharedPreferences.edit {
putBoolean(getString(R.string.pref_analytics_reporting_key), value)
}
}

private fun launchHome() {
prefManager.isFirstTimeLaunch = false
startActivity(Intent(this@WelcomeActivity, MainActivity::class.java))
finish()
}

private fun refreshBottomBar(currentPage: Int) {

binding.layoutDots.removeAllViews()
val dots = (1..layouts.size).map {
val dots = (0..screens.lastIndex).map {
TextView(this).apply {
text = HtmlCompat.fromHtml("&#8226;", HtmlCompat.FROM_HTML_MODE_COMPACT)
textSize = 35f
setTextColor(colorsInactive[currentPage])
setTextColor(WelcomeScreen[currentPage].color.lighten(0.85f))
binding.layoutDots.addView(this)
}
}
dots[currentPage].setTextColor(colorsActive[currentPage])
}

private fun launchHomeScreen() {
prefManager.isFirstTimeLaunch = false
startActivity(Intent(this@WelcomeActivity, MainActivity::class.java))
finish()
dots[currentPage].setTextColor(WelcomeScreen[currentPage].color.darken(0.1f))
}

private val nextItem get() = binding.viewPager.currentItem + 1
Expand Down
Expand Up @@ -3,20 +3,19 @@ package openfoodfacts.github.scrachx.openfood.features.welcome
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.StringRes
import androidx.viewpager.widget.PagerAdapter

class WelcomePageAdapter(
private val layoutInflater: LayoutInflater,
@param:StringRes private val layouts: IntArray
) : PagerAdapter() {
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val view = layoutInflater.inflate(layouts[position], container, false)
val layout = WelcomeScreen.values()[position].layout
val view = layoutInflater.inflate(layout, container, false)
container.addView(view)
return view
}

override fun getCount() = layouts.size
override fun getCount() = WelcomeScreen.values().size

override fun isViewFromObject(view: View, obj: Any) = view === obj

Expand Down
@@ -0,0 +1,23 @@
package openfoodfacts.github.scrachx.openfood.features.welcome

import androidx.annotation.ColorRes
import androidx.annotation.StringRes
import openfoodfacts.github.scrachx.openfood.R

internal enum class WelcomeScreen(@StringRes layoutRes: Int, @ColorRes colorRes: Int) {
INTRO(R.layout.welcome_slide_intro, R.color.bg_welcome_intro),
NUTRISCORE(R.layout.welcome_slide_nutriscore, R.color.bg_welcome_nutriscore),
NOVA(R.layout.welcome_slide_nova, R.color.bg_welcome_nova),
ECOSCORE(R.layout.welcome_slide_ecoscore, R.color.bg_welcome_ecoscore),
MATOMO(R.layout.welcome_slide_matomo, R.color.bg_welcome_matomo);

@StringRes
val layout = layoutRes

@ColorRes
val color = colorRes

companion object {
operator fun get(position: Int) = values()[position]
}
}
Expand Up @@ -111,7 +111,7 @@ interface ProductsAPI {
suspend fun getSuggestions(
@Query("tagtype") tagType: String,
@Query("term") term: String
): List<String>
): ArrayList<String>


@GET("brand/{brand}/{page}.json")
Expand Down Expand Up @@ -332,7 +332,7 @@ interface ProductsAPI {
* This method gives the news in all languages
*/
@GET("/files/tagline/tagline-$FLAVOR_versionCode.json")
fun getTagline(@Header("User-Agent") header: String): List<TagLineLanguage>
fun getTagline(@Header("User-Agent") header: String): ArrayList<TagLineLanguage>

/**
* Returns images for the current product
Expand Down
Expand Up @@ -11,24 +11,18 @@ class PrefManager(context: Context) {
var isFirstTimeLaunch: Boolean
get() {
val actualTime = System.currentTimeMillis()
// First time launch
if (pref.getLong(FIRST_TIME_LAUNCH_TIME, actualTime) == actualTime) {
// First time launch

// Save first launch time
pref.edit { putLong(FIRST_TIME_LAUNCH_TIME, actualTime) }
}
return pref.getBoolean(IS_FIRST_TIME_LAUNCH, true)
}
// Save first launch time
set(isFirstTime) {
pref.edit { putBoolean(IS_FIRST_TIME_LAUNCH, isFirstTime) }
}
set(isFirstTime) = pref.edit { putBoolean(IS_FIRST_TIME_LAUNCH, isFirstTime) }

var userAskedToRate: Boolean
get() = pref.getBoolean(USER_ASKED_TO_RATE, false)
set(userAskedToRate) {
pref.edit { putBoolean(USER_ASKED_TO_RATE, userAskedToRate) }
}
set(userAskedToRate) = pref.edit { putBoolean(USER_ASKED_TO_RATE, userAskedToRate) }

val firstTimeLaunchTime: Long
get() = pref.getLong(FIRST_TIME_LAUNCH_TIME, System.currentTimeMillis())
Expand Down
Expand Up @@ -24,6 +24,7 @@ import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Color
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.net.Uri
Expand All @@ -36,10 +37,12 @@ import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.ImageView
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import androidx.core.net.toUri
import androidx.core.text.inSpans
import androidx.core.view.children
Expand Down Expand Up @@ -343,4 +346,8 @@ internal fun RequestCreator.into(target: ImageView, onSuccess: () -> Unit) {
override fun onSuccess() = onSuccess()
override fun onError(e: Exception) = throw e
})
}
}


fun @receiver:ColorInt Int.darken(ratio: Float) = ColorUtils.blendARGB(this, Color.BLACK, ratio)
fun @receiver:ColorInt Int.lighten(ratio: Float) = ColorUtils.blendARGB(this, Color.WHITE, ratio)
10 changes: 10 additions & 0 deletions app/src/main/res/drawable/ic_analytics_white_24.xml
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#FFFFFF"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM9,17L7,17v-5h2v5zM13,17h-2v-3h2v3zM13,12h-2v-2h2v2zM17,17h-2L15,7h2v10z" />
</vector>
Expand Up @@ -3,16 +3,16 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/brand_green_dark"
android:background="@color/bg_welcome_ecoscore"
android:gravity="center">

<ImageView
android:id="@+id/slide_4_image"
android:layout_width="@dimen/img_width_height"
android:layout_height="@dimen/img_width_height"
android:layout_centerHorizontal="true"
app:srcCompat="@drawable/ic_ecoscore_b"
android:contentDescription="TODO" />
android:contentDescription="TODO"
app:srcCompat="@drawable/ic_ecoscore_b" />

<TextView
android:id="@+id/slide_4_title"
Expand Down
Expand Up @@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_screen1"
android:background="@color/bg_welcome_intro"
android:gravity="center">

<ImageView
Expand Down

0 comments on commit 94ae339

Please sign in to comment.