Skip to content

Commit

Permalink
Merge pull request #1674 from Infomaniak/OnBoarding-notif
Browse files Browse the repository at this point in the history
Permissions On boarding
  • Loading branch information
LunarX authored Jan 30, 2024
2 parents e819913 + 4dc6c3b commit 3b4ea69
Show file tree
Hide file tree
Showing 29 changed files with 2,453 additions and 156 deletions.
237 changes: 106 additions & 131 deletions .idea/navEditor.xml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class LocalSettings private constructor(context: Context) : SharedValues {
var showSyncDiscoveryBottomSheet by sharedValue("showSyncDiscoveryBottomSheetKey", true)
var appReviewLaunches by sharedValue("appReviewLaunchesKey", DEFAULT_APP_REVIEW_LAUNCHES)
var showAppReviewDialog by sharedValue("showAppReviewDialogKey", true)
var showPermissionsOnboarding by sharedValue("showPermissionsOnboardingKey", true)

fun removeSettings() = sharedPreferences.transaction { clear() }

Expand Down
28 changes: 22 additions & 6 deletions app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
*/
package com.infomaniak.mail.ui

import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
Expand Down Expand Up @@ -45,6 +44,7 @@ import com.infomaniak.lib.core.utils.SentryLog
import com.infomaniak.lib.core.utils.Utils
import com.infomaniak.lib.core.utils.Utils.toEnumOrThrow
import com.infomaniak.lib.core.utils.UtilsUi.openUrl
import com.infomaniak.lib.core.utils.hasPermissions
import com.infomaniak.lib.core.utils.year
import com.infomaniak.lib.stores.StoreUtils.checkUpdateIsAvailable
import com.infomaniak.lib.stores.StoreUtils.initAppUpdateManager
Expand All @@ -69,6 +69,7 @@ import com.infomaniak.mail.ui.alertDialogs.TitleAlertDialog
import com.infomaniak.mail.ui.main.SnackbarManager
import com.infomaniak.mail.ui.main.folder.TwoPaneFragment
import com.infomaniak.mail.ui.main.menu.MenuDrawerFragment
import com.infomaniak.mail.ui.main.onboarding.PermissionsOnboardingPagerFragment
import com.infomaniak.mail.ui.newMessage.NewMessageActivity
import com.infomaniak.mail.ui.sync.SyncAutoConfigActivity
import com.infomaniak.mail.utils.*
Expand Down Expand Up @@ -206,7 +207,7 @@ class MainActivity : BaseActivity() {

loadCurrentMailbox()

permissionUtils.requestMainPermissionsIfNeeded()
managePermissionsRequesting()

initAppUpdateManager()
}
Expand Down Expand Up @@ -368,8 +369,11 @@ class MainActivity : BaseActivity() {
}

fun popBack() {
val fragment = currentFragment
if (fragment is TwoPaneFragment) fragment.handleOnBackPressed() else navController.popBackStack()
when (val fragment = currentFragment) {
is TwoPaneFragment -> fragment.handleOnBackPressed()
is PermissionsOnboardingPagerFragment -> fragment.leaveOnboarding()
else -> navController.popBackStack()
}
}

onBackPressedDispatcher.addCallback(this@MainActivity) {
Expand Down Expand Up @@ -422,7 +426,7 @@ class MainActivity : BaseActivity() {

private fun registerMainPermissions() {
permissionUtils.registerMainPermissions { permissionsResults ->
if (permissionsResults[Manifest.permission.READ_CONTACTS] == true) mainViewModel.updateUserInfo()
if (permissionsResults[PermissionUtils.READ_CONTACTS_PERMISSION] == true) mainViewModel.updateUserInfo()
}
}

Expand Down Expand Up @@ -483,6 +487,18 @@ class MainActivity : BaseActivity() {
)
}

private fun managePermissionsRequesting() {
if (hasPermissions(PermissionUtils.getMainPermissions(mustRequireNotification = true))) return

if (localSettings.showPermissionsOnboarding) {
if (currentFragment !is PermissionsOnboardingPagerFragment) {
navController.navigate(R.id.permissionsOnboardingPagerFragment)
}
} else {
permissionUtils.requestMainPermissionsIfNeeded()
}
}

private fun initAppUpdateManager() {
initAppUpdateManager(
context = this,
Expand All @@ -508,7 +524,7 @@ class MainActivity : BaseActivity() {
}

private fun showSyncDiscovery() = with(localSettings) {
if (showSyncDiscoveryBottomSheet && appLaunches > 1 && !isUserAlreadySynchronized()) {
if (!showPermissionsOnboarding && showSyncDiscoveryBottomSheet && appLaunches > 1 && !isUserAlreadySynchronized()) {
showSyncDiscoveryBottomSheet = false
navController.navigate(R.id.syncDiscoveryBottomSheetDialog)
}
Expand Down
11 changes: 2 additions & 9 deletions app/src/main/java/com/infomaniak/mail/ui/login/LoginFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package com.infomaniak.mail.ui.login

import android.content.res.ColorStateList
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
Expand All @@ -30,7 +29,6 @@ import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.infomaniak.lib.core.R
import com.infomaniak.lib.core.utils.*
Expand All @@ -42,6 +40,7 @@ import com.infomaniak.mail.di.IoDispatcher
import com.infomaniak.mail.di.MainDispatcher
import com.infomaniak.mail.utils.LoginUtils
import com.infomaniak.mail.utils.UiUtils
import com.infomaniak.mail.utils.removeOverScrollForApiBelow31
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineDispatcher
import javax.inject.Inject
Expand Down Expand Up @@ -99,9 +98,7 @@ class LoginFragment : Fragment() {
}
})

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
removeOverScroll()
}
removeOverScrollForApiBelow31()
}

dotsIndicator.apply {
Expand Down Expand Up @@ -193,10 +190,6 @@ class LoginFragment : Fragment() {
signInButton.isEnabled = true
}

private fun ViewPager2.removeOverScroll() {
(getChildAt(0) as? RecyclerView)?.overScrollMode = View.OVER_SCROLL_NEVER
}

private fun getViewPagerCurrentItem(): Int = binding.introViewpager.currentItem

private fun goBackAPage() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Infomaniak Mail - Android
* Copyright (C) 2024 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.mail.ui.main.onboarding

import android.content.res.ColorStateList
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.navArgs
import com.infomaniak.lib.core.utils.safeBinding
import com.infomaniak.mail.R
import com.infomaniak.mail.data.LocalSettings
import com.infomaniak.mail.data.LocalSettings.*
import com.infomaniak.mail.databinding.FragmentPermissionsOnboardingBinding
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

@AndroidEntryPoint
class PermissionsOnboardingFragment : Fragment() {

private var binding: FragmentPermissionsOnboardingBinding by safeBinding()
private val navigationArgs: PermissionsOnboardingFragmentArgs by navArgs()

@Inject
lateinit var localSettings: LocalSettings

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return FragmentPermissionsOnboardingBinding.inflate(inflater, container, false).also { binding = it }.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?): Unit = with(binding) {
super.onViewCreated(view, savedInstanceState)
setPermissionUi()
}

private fun setPermissionUi() = with(binding) {
val permission = if (navigationArgs.position == 0) PermissionType.CONTACTS else PermissionType.NOTIFICATIONS
iconLayout.setImageResource(getIconResWithAccentColor(permission))
title.setText(permission.titleRes)
description.setText(permission.descritionRes)
waveBackground.apply {
setImageResource(permission.waveRes)
imageTintList = ColorStateList.valueOf(localSettings.accentColor.getOnboardingSecondaryBackground(context))
}
}

private fun getIconResWithAccentColor(permission: PermissionType) = when (localSettings.accentColor) {
AccentColor.PINK -> permission.pinkIconRes
AccentColor.BLUE -> permission.blueIconRes
AccentColor.SYSTEM -> permission.systemIconRes
}

enum class PermissionType(
@DrawableRes val pinkIconRes: Int,
@DrawableRes val blueIconRes: Int,
@DrawableRes val systemIconRes: Int,
@StringRes val titleRes: Int,
@StringRes val descritionRes: Int,
@DrawableRes val waveRes: Int,
) {
CONTACTS(
pinkIconRes = R.drawable.illustration_onboarding_contacts,
blueIconRes = R.drawable.illustration_onboarding_contacts_blue,
systemIconRes = R.drawable.illustration_onboarding_contacts_material,
titleRes = R.string.onBoardingContactsTitle,
descritionRes = R.string.onBoardingContactsDescription,
waveRes = R.drawable.ic_back_wave_1,
),
NOTIFICATIONS(
pinkIconRes = R.drawable.illustration_onboarding_notifications,
blueIconRes = R.drawable.illustration_onboarding_notifications_blue,
systemIconRes = R.drawable.illustration_onboarding_notifications_material,
titleRes = R.string.onBoardingNotificationsTitle,
descritionRes = R.string.onBoardingNotificationsDescription,
waveRes = R.drawable.ic_back_wave_2,
),
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Infomaniak Mail - Android
* Copyright (C) 2024 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.mail.ui.main.onboarding

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.viewpager2.widget.ViewPager2
import com.infomaniak.lib.core.utils.context
import com.infomaniak.lib.core.utils.safeBinding
import com.infomaniak.lib.core.utils.safeNavigate
import com.infomaniak.mail.R
import com.infomaniak.mail.data.LocalSettings
import com.infomaniak.mail.databinding.FragmentPermissionsOnboardingPagerBinding
import com.infomaniak.mail.ui.MainViewModel
import com.infomaniak.mail.utils.PermissionUtils
import com.infomaniak.mail.utils.removeOverScrollForApiBelow31
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

@AndroidEntryPoint
class PermissionsOnboardingPagerFragment : Fragment() {

private var binding: FragmentPermissionsOnboardingPagerBinding by safeBinding()
private val mainViewModel: MainViewModel by activityViewModels()
private var currentPosition = 0

@Inject
lateinit var localSettings: LocalSettings

@Inject
lateinit var permissionUtils: PermissionUtils

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return FragmentPermissionsOnboardingPagerBinding.inflate(inflater, container, false).also { binding = it }.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?): Unit = with(binding) {
super.onViewCreated(view, savedInstanceState)

requireActivity().window.statusBarColor = localSettings.accentColor.getOnboardingSecondaryBackground(context)

permissionUtils.apply {
registerReadContactsPermission(fragment = this@PermissionsOnboardingPagerFragment)
registerNotificationsPermissionIfNeeded(fragment = this@PermissionsOnboardingPagerFragment)
}

permissionsViewpager.apply {
adapter = PermissionsPagerAdapter(childFragmentManager, viewLifecycleOwner.lifecycle)
isUserInputEnabled = false
savedInstanceState?.getInt(VIEW_PAGER_POSITION_KEY)?.let { setCurrentItem(it, false) }
removeOverScrollForApiBelow31()
}

continueButton.setOnClickListener {
when (permissionsViewpager.currentItem) {
0 -> {
permissionUtils.requestReadContactsPermission { hasPermission ->
if (hasPermission) mainViewModel.updateUserInfo()
if (permissionsViewpager.isLastPage()) {
leaveOnboarding()
} else {
currentPosition += 1
permissionsViewpager.currentItem += 1
}
}
}
1 -> permissionUtils.requestNotificationsPermissionIfNeeded { leaveOnboarding() }
}
}
}

override fun onSaveInstanceState(outState: Bundle) {
outState.putInt(VIEW_PAGER_POSITION_KEY, currentPosition)
super.onSaveInstanceState(outState)
}

private fun ViewPager2.isLastPage() = adapter?.let { currentItem == it.itemCount - 1 } ?: true

fun leaveOnboarding() {
localSettings.showPermissionsOnboarding = false
safeNavigate(R.id.threadListFragment)
}

companion object {
private const val VIEW_PAGER_POSITION_KEY = "viewPagerPositionKey"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Infomaniak Mail - Android
* Copyright (C) 2024 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.mail.ui.main.onboarding

import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.infomaniak.mail.utils.PermissionUtils

class PermissionsPagerAdapter(manager: FragmentManager, lifecycle: Lifecycle) : FragmentStateAdapter(manager, lifecycle) {

override fun getItemCount() = PermissionUtils.getMainPermissions(mustRequireNotification = true).count()

override fun createFragment(position: Int) = PermissionsOnboardingFragment().apply {
arguments = PermissionsOnboardingFragmentArgs(position).toBundle()
}
}
8 changes: 8 additions & 0 deletions app/src/main/java/com/infomaniak/mail/utils/Extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ import androidx.lifecycle.*
import androidx.navigation.NavDirections
import androidx.navigation.NavOptions
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.airbnb.lottie.LottieAnimationView
import com.airbnb.lottie.LottieProperty
import com.airbnb.lottie.SimpleColorFilter
Expand Down Expand Up @@ -625,3 +627,9 @@ fun ShapeableImageView.setInnerStrokeWidth(strokeWidth: Float) {
val halfStrokeWidth = (strokeWidth / 2.0f).roundToInt()
setPaddingRelative(halfStrokeWidth, halfStrokeWidth, halfStrokeWidth, halfStrokeWidth)
}

fun ViewPager2.removeOverScrollForApiBelow31() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
(getChildAt(0) as? RecyclerView)?.overScrollMode = View.OVER_SCROLL_NEVER
}
}
Loading

0 comments on commit 3b4ea69

Please sign in to comment.