Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stable-3.28] Onboarding functionality #141

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.nmc.android.onboarding

import androidx.test.espresso.Espresso.*
import androidx.test.espresso.action.ViewActions.swipeLeft
import androidx.test.espresso.action.ViewActions.swipeRight
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isClickable
import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.nextcloud.client.onboarding.FirstRunActivity
import com.owncloud.android.AbstractIT
import com.owncloud.android.R
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class OnBoardingIT : AbstractIT() {

@get:Rule
var activityRule = ActivityScenarioRule(FirstRunActivity::class.java)

@Test
fun runAllOnboardingTests() {
verifyUIElements()

shortSleep()

verifyOnBoardingSwipe()
}

private fun verifyUIElements() {
onView(withId(R.id.contentPanel)).check(matches(isCompletelyDisplayed()))
onView(withId(R.id.progressIndicator)).check(matches(isCompletelyDisplayed()))
onView(withId(R.id.login)).check(matches(isCompletelyDisplayed()))
onView(withId(R.id.login)).check(matches(isClickable()))
}

private fun verifyOnBoardingSwipe() {
onView(withId(R.id.contentPanel)).perform(swipeLeft())
onView(withId(R.id.contentPanel)).perform(swipeLeft())
onView(withId(R.id.contentPanel)).perform(swipeLeft())

onView(withId(R.id.contentPanel)).perform(swipeRight())
onView(withId(R.id.contentPanel)).perform(swipeRight())
}
}
142 changes: 60 additions & 82 deletions app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@
package com.nextcloud.client.onboarding

import android.accounts.AccountManager
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.view.ViewGroup.MarginLayoutParams
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher
Expand All @@ -40,14 +40,15 @@ import com.nextcloud.client.account.UserAccountManager
import com.nextcloud.client.appinfo.AppInfo
import com.nextcloud.client.di.Injectable
import com.nextcloud.client.preferences.AppPreferences
import com.nmc.android.helper.OnBoardingPagerAdapter
import com.nmc.android.helper.OnBoardingUtils.Companion.getOnBoardingItems
import com.nmc.android.utils.DisplayUtils.isLandscapeOrientation
import com.owncloud.android.BuildConfig
import com.owncloud.android.R
import com.owncloud.android.authentication.AuthenticatorActivity
import com.owncloud.android.databinding.FirstRunActivityBinding
import com.owncloud.android.features.FeatureItem
import com.owncloud.android.ui.activity.BaseActivity
import com.owncloud.android.ui.activity.FileDisplayActivity
import com.owncloud.android.ui.adapter.FeaturesViewAdapter
import com.owncloud.android.utils.DisplayUtils
import com.owncloud.android.utils.theme.ViewThemeUtils
import javax.inject.Inject
Expand Down Expand Up @@ -82,25 +83,31 @@ class FirstRunActivity : BaseActivity(), ViewPager.OnPageChangeListener, Injecta
private lateinit var binding: FirstRunActivityBinding
private var defaultViewThemeUtils: ViewThemeUtils? = null

private var selectedPosition = 0

@SuppressLint("SourceLockedOrientationActivity")
override fun onCreate(savedInstanceState: Bundle?) {
enableAccountHandling = false

super.onCreate(savedInstanceState)

applyDefaultTheme()

// NMC Customization
// if device is not tablet then we have to lock it to Portrait mode
// as we don't have images for that
if (!com.nmc.android.utils.DisplayUtils.isTablet()) {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}

binding = FirstRunActivityBinding.inflate(layoutInflater)
setContentView(binding.root)

val isProviderOrOwnInstallationVisible = resources.getBoolean(R.bool.show_provider_or_own_installation)
setSlideshowSize(resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE)

registerActivityResult()
setupLoginButton()
setupSignupButton(isProviderOrOwnInstallationVisible)
setupHostOwnServerTextView(isProviderOrOwnInstallationVisible)
deleteAccountAtFirstLaunch()
setupFeaturesViewAdapter()
updateLoginButtonMargin()
updateOnBoardingPager(selectedPosition)
handleOnBackPressed()
}

Expand Down Expand Up @@ -139,57 +146,52 @@ class FirstRunActivity : BaseActivity(), ViewPager.OnPageChangeListener, Injecta
val authenticatorActivityIntent = getAuthenticatorActivityIntent(false)
activityResult?.launch(authenticatorActivityIntent)
} else {
preferences?.onBoardingComplete = true
finish()
}
}
}

private fun setupSignupButton(isProviderOrOwnInstallationVisible: Boolean) {
defaultViewThemeUtils?.material?.colorMaterialButtonOutlinedOnPrimary(binding.signup)
binding.signup.visibility = if (isProviderOrOwnInstallationVisible) View.VISIBLE else View.GONE
binding.signup.setOnClickListener {
val authenticatorActivityIntent = getAuthenticatorActivityIntent(true)

if (intent.getBooleanExtra(EXTRA_ALLOW_CLOSE, false)) {
activityResult?.launch(authenticatorActivityIntent)
} else {
authenticatorActivityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(authenticatorActivityIntent)
}
}
}

private fun getAuthenticatorActivityIntent(extraUseProviderAsWebLogin: Boolean): Intent {
val intent = Intent(this, AuthenticatorActivity::class.java)
intent.putExtra(AuthenticatorActivity.EXTRA_USE_PROVIDER_AS_WEBLOGIN, extraUseProviderAsWebLogin)
return intent
}

private fun setupHostOwnServerTextView(isProviderOrOwnInstallationVisible: Boolean) {
defaultViewThemeUtils?.platform?.colorTextView(binding.hostOwnServer, ColorRole.ON_PRIMARY)
binding.hostOwnServer.visibility = if (isProviderOrOwnInstallationVisible) View.VISIBLE else View.GONE
if (isProviderOrOwnInstallationVisible) {
binding.hostOwnServer.setOnClickListener {
DisplayUtils.startLinkIntent(
this,
R.string.url_server_install
)
}
}
}

// Sometimes, accounts are not deleted when you uninstall the application so we'll do it now
private fun deleteAccountAtFirstLaunch() {
if (onboarding?.isFirstRun == true) {
userAccountManager?.removeAllAccounts()
}
}

@Suppress("SpreadOperator")
private fun setupFeaturesViewAdapter() {
val featuresViewAdapter = FeaturesViewAdapter(supportFragmentManager, *firstRun)
private fun updateLoginButtonMargin() {
if (isLandscapeOrientation()) {
if (binding.login.layoutParams is MarginLayoutParams) {
(binding.login.layoutParams as MarginLayoutParams).setMargins(
0, 0, 0, resources.getDimensionPixelOffset(
R.dimen.login_btn_bottom_margin_land
)
)
binding.login.requestLayout()
}
} else {
if (binding.login.layoutParams is MarginLayoutParams) {
(binding.login.layoutParams as MarginLayoutParams).setMargins(
0, 0, 0, resources.getDimensionPixelOffset(
R.dimen.login_btn_bottom_margin
)
)
binding.login.requestLayout()
}
}
}

private fun updateOnBoardingPager(selectedPosition: Int) {
val featuresViewAdapter = OnBoardingPagerAdapter(this, getOnBoardingItems())
binding.progressIndicator.setNumberOfSteps(featuresViewAdapter.count)
binding.contentPanel.adapter = featuresViewAdapter
binding.contentPanel.currentItem = selectedPosition
binding.contentPanel.addOnPageChangeListener(this)
}

Expand All @@ -200,47 +202,27 @@ class FirstRunActivity : BaseActivity(), ViewPager.OnPageChangeListener, Injecta
override fun handleOnBackPressed() {
val isFromAddAccount = intent.getBooleanExtra(EXTRA_ALLOW_CLOSE, false)

val destination: Intent = if (isFromAddAccount) {
Intent(applicationContext, FileDisplayActivity::class.java)
// NMC Customization -> Modified the condition for readability
if (isFromAddAccount) {
val destination = Intent(applicationContext, FileDisplayActivity::class.java)
destination.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
startActivity(destination)
finish()
} else {
Intent(applicationContext, AuthenticatorActivity::class.java)
}

if (!isFromAddAccount) {
destination.putExtra(EXTRA_EXIT, true)
// NMC Customization -> No redirection to AuthenticatorActivity is required
// just close the app
finishAffinity()
}

destination.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
startActivity(destination)
finish()
}
}
)
}

private fun setSlideshowSize(isLandscape: Boolean) {
val isProviderOrOwnInstallationVisible = resources.getBoolean(R.bool.show_provider_or_own_installation)
binding.buttonLayout.orientation = if (isLandscape) LinearLayout.HORIZONTAL else LinearLayout.VERTICAL

val layoutParams: LinearLayout.LayoutParams = if (isProviderOrOwnInstallationVisible) {
LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
} else {
@Suppress("MagicNumber")
LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
DisplayUtils.convertDpToPixel(if (isLandscape) 100f else 150f, this)
)
}

binding.bottomLayout.layoutParams = layoutParams
}

override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
setSlideshowSize(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
updateLoginButtonMargin()
updateOnBoardingPager(selectedPosition)
}

private fun onFinish() {
Expand All @@ -257,7 +239,11 @@ class FirstRunActivity : BaseActivity(), ViewPager.OnPageChangeListener, Injecta
}

override fun onPageSelected(position: Int) {
binding.progressIndicator.animateToStep(position + 1)
//-1 to position because this position doesn't start from 0
selectedPosition = position - 1

//pass directly the position here because this position will doesn't start from 0
binding.progressIndicator.animateToStep(position)
}

override fun onPageScrollStateChanged(state: Int) {
Expand All @@ -267,13 +253,5 @@ class FirstRunActivity : BaseActivity(), ViewPager.OnPageChangeListener, Injecta
companion object {
const val EXTRA_ALLOW_CLOSE = "ALLOW_CLOSE"
const val EXTRA_EXIT = "EXIT"

val firstRun: Array<FeatureItem>
get() = arrayOf(
FeatureItem(R.drawable.logo, R.string.first_run_1_text, R.string.empty, true, false),
FeatureItem(R.drawable.first_run_files, R.string.first_run_2_text, R.string.empty, true, false),
FeatureItem(R.drawable.first_run_groupware, R.string.first_run_3_text, R.string.empty, true, false),
FeatureItem(R.drawable.first_run_talk, R.string.first_run_4_text, R.string.empty, true, false)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@ internal class OnboardingServiceImpl constructor(
}

override fun launchFirstRunIfNeeded(activity: Activity): Boolean {
val isProviderOrOwnInstallationVisible = resources.getBoolean(R.bool.show_provider_or_own_installation)
val canLaunch = isProviderOrOwnInstallationVisible && isFirstRun && activity is AuthenticatorActivity
val canLaunch = !preferences.onBoardingComplete && activity is AuthenticatorActivity
if (canLaunch) {
val intent = Intent(activity, FirstRunActivity::class.java)
activity.startActivityForResult(intent, AuthenticatorActivity.REQUEST_CODE_FIRST_RUN)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,10 @@ default void onDarkThemeModeChanged(DarkMode mode) {

void setCalendarLastBackup(long timestamp);

void setOnBoardingComplete(boolean isCompleted);

boolean getOnBoardingComplete();

void setPdfZoomTipShownCount(int count);

int getPdfZoomTipShownCount();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ public final class AppPreferencesImpl implements AppPreferences {
private static final String PREF__FOLDER_SORT_ORDER = "folder_sort_order";
private static final String PREF__FOLDER_LAYOUT = "folder_layout";

private static final String PREF__ON_BOARDING_COMPLETE = "on_boarding_complete";

private static final String PREF__LOCK_TIMESTAMP = "lock_timestamp";
private static final String PREF__SHOW_MEDIA_SCAN_NOTIFICATIONS = "show_media_scan_notifications";
private static final String PREF__LOCK = SettingsActivity.PREFERENCE_LOCK;
Expand Down Expand Up @@ -741,6 +743,16 @@ public void setCalendarLastBackup(long timestamp) {
preferences.edit().putLong(PREF__CALENDAR_LAST_BACKUP, timestamp).apply();
}

@Override
public void setOnBoardingComplete(boolean isCompleted) {
preferences.edit().putBoolean(PREF__ON_BOARDING_COMPLETE, isCompleted).apply();
}

@Override
public boolean getOnBoardingComplete() {
return preferences.getBoolean(PREF__ON_BOARDING_COMPLETE, false);
}

@Override
public void setPdfZoomTipShownCount(int count) {
preferences.edit().putInt(PREF__PDF_ZOOM_TIP_SHOWN, count).apply();
Expand Down
Loading