diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c9df76c2d2..828a76bab9 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -97,12 +97,12 @@
+ android:windowSoftInputMode="adjustNothing" />
+ android:windowSoftInputMode="adjustNothing" />
- userActivities
- .firstOrNull()
- ?.pins
- ?.let(StepikUtil::getCurrentStreak)
- .toMaybe()
- }
+ .flatMapSingleElement(userActivityRepository::getUserActivitySummary)
+ .map { it.recentStrike }
.subscribeOn(backgroundScheduler)
.observeOn(mainScheduler)
.subscribeBy(
diff --git a/app/src/main/java/org/stepic/droid/di/NotificationModule.kt b/app/src/main/java/org/stepic/droid/di/NotificationModule.kt
index e98925a84b..3fa3e9f5f2 100644
--- a/app/src/main/java/org/stepic/droid/di/NotificationModule.kt
+++ b/app/src/main/java/org/stepic/droid/di/NotificationModule.kt
@@ -19,6 +19,7 @@ import org.stepik.android.view.purchase_notification.notification.PurchaseNotifi
import org.stepik.android.view.splash.notification.RemindRegistrationNotificationDelegate
import org.stepik.android.view.splash.notification.RetentionNotificationDelegate
import org.stepik.android.view.streak.notification.StreakNotificationDelegate
+import org.stepik.android.view.streak.notification.StreakNotificationScheduler
@Module(includes = [CourseDataModule::class])
interface NotificationModule {
@@ -54,7 +55,10 @@ interface NotificationModule {
@IntoSet
fun provideStreakNotificationDelegate(streakNotificationDelegate: StreakNotificationDelegate): NotificationDelegate
+ @Binds
+ fun bindStreakNotificationScheduler(streakNotificationDelegate: StreakNotificationDelegate): StreakNotificationScheduler
+
@Binds
@IntoSet
fun providePurchaseNotificationDelegate(purchaseNotificationDelegate: PurchaseNotificationDelegate): NotificationDelegate
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/org/stepic/droid/ui/adapters/SocialAuthAdapter.java b/app/src/main/java/org/stepic/droid/ui/adapters/SocialAuthAdapter.java
index f93780b806..73e65cda2c 100644
--- a/app/src/main/java/org/stepic/droid/ui/adapters/SocialAuthAdapter.java
+++ b/app/src/main/java/org/stepic/droid/ui/adapters/SocialAuthAdapter.java
@@ -16,28 +16,12 @@
import kotlin.jvm.functions.Function1;
public class SocialAuthAdapter extends RecyclerView.Adapter implements OnItemClickListener {
- private SocialNetwork[] socialList;
- private Function1 onSocialItemClick;
+ private final SocialNetwork[] socialList;
+ private final Function1 onSocialItemClick;
- private State state;
-
- public enum State {
- EXPANDED(4), NORMAL(3);
-
- public final int multiplier;
- State(int multiplier) {
- this.multiplier = multiplier;
- }
- }
-
- public SocialAuthAdapter(Function1 onSocialItemClick, State state) {
+ public SocialAuthAdapter(Function1 onSocialItemClick) {
this.onSocialItemClick = onSocialItemClick;
socialList = SocialNetwork.values();
- if (state == null) {
- this.state = State.NORMAL;
- } else {
- this.state = state;
- }
}
@@ -56,7 +40,7 @@ public void onBindViewHolder(SocialViewHolder holder, int position) {
@Override
public int getItemCount() {
- return state.multiplier;
+ return socialList.length;
}
@Override
@@ -64,24 +48,6 @@ public void onItemClick(int position) {
onSocialItemClick.invoke(socialList[position]);
}
- public void showMore() {
- int start = getItemCount();
- state = State.EXPANDED;
- int end = getItemCount();
- notifyItemRangeInserted(start, end - start);
- }
-
- public void showLess() {
- int end = getItemCount();
- state = State.NORMAL;
- int start = getItemCount();
- notifyItemRangeRemoved(start, end - start);
- }
-
- public State getState() {
- return state;
- }
-
static class SocialViewHolder extends RecyclerView.ViewHolder {
private final ImageView imageView;
diff --git a/app/src/main/java/org/stepik/android/data/user_activity/repository/UserActivityRepositoryImpl.kt b/app/src/main/java/org/stepik/android/data/user_activity/repository/UserActivityRepositoryImpl.kt
index 833d23739f..9f83580a19 100644
--- a/app/src/main/java/org/stepik/android/data/user_activity/repository/UserActivityRepositoryImpl.kt
+++ b/app/src/main/java/org/stepik/android/data/user_activity/repository/UserActivityRepositoryImpl.kt
@@ -4,6 +4,7 @@ import io.reactivex.Single
import org.stepik.android.data.user_activity.source.UserActivityRemoteDataSource
import org.stepik.android.domain.user_activity.repository.UserActivityRepository
import org.stepik.android.model.user.UserActivity
+import org.stepik.android.model.user.UserActivitySummary
import javax.inject.Inject
class UserActivityRepositoryImpl
@@ -14,4 +15,7 @@ constructor(
override fun getUserActivities(userId: Long): Single> =
userActivityRemoteDataSource.getUserActivities(userId)
+
+ override fun getUserActivitySummary(userId: Long): Single =
+ userActivityRemoteDataSource.getUserActivitySummary(userId)
}
\ No newline at end of file
diff --git a/app/src/main/java/org/stepik/android/data/user_activity/source/UserActivityRemoteDataSource.kt b/app/src/main/java/org/stepik/android/data/user_activity/source/UserActivityRemoteDataSource.kt
index b5f81241c1..c919a95cef 100644
--- a/app/src/main/java/org/stepik/android/data/user_activity/source/UserActivityRemoteDataSource.kt
+++ b/app/src/main/java/org/stepik/android/data/user_activity/source/UserActivityRemoteDataSource.kt
@@ -2,7 +2,10 @@ package org.stepik.android.data.user_activity.source
import io.reactivex.Single
import org.stepik.android.model.user.UserActivity
+import org.stepik.android.model.user.UserActivitySummary
interface UserActivityRemoteDataSource {
fun getUserActivities(userId: Long): Single>
+
+ fun getUserActivitySummary(userId: Long): Single
}
\ No newline at end of file
diff --git a/app/src/main/java/org/stepik/android/domain/profile_activities/interactor/ProfileActivitiesInteractor.kt b/app/src/main/java/org/stepik/android/domain/profile_activities/interactor/ProfileActivitiesInteractor.kt
index 35cd0b1e34..64c9a0f9f7 100644
--- a/app/src/main/java/org/stepik/android/domain/profile_activities/interactor/ProfileActivitiesInteractor.kt
+++ b/app/src/main/java/org/stepik/android/domain/profile_activities/interactor/ProfileActivitiesInteractor.kt
@@ -3,10 +3,8 @@ package org.stepik.android.domain.profile_activities.interactor
import io.reactivex.Single
import org.stepik.android.domain.profile_activities.model.ProfileActivitiesData
import org.stepik.android.domain.user_activity.repository.UserActivityRepository
-import org.stepik.android.model.user.UserActivity
-import ru.nobird.android.domain.rx.first
+import org.stepik.android.model.user.UserActivitySummary
import javax.inject.Inject
-import kotlin.math.max
class ProfileActivitiesInteractor
@Inject
@@ -15,28 +13,14 @@ constructor(
) {
fun getProfileActivities(userId: Long): Single =
userActivityRepository
- .getUserActivities(userId)
- .first()
+ .getUserActivitySummary(userId)
.map(::mapToProfileActivities)
- private fun mapToProfileActivities(userActivity: UserActivity): ProfileActivitiesData {
- var streak = 0
- var maxStreak = 0
- var buffer = 0
-
- for (i in 0..userActivity.pins.size) {
- val pin = userActivity.pins.getOrElse(i) { 0 }
- if (pin > 0) {
- buffer++
- } else {
- maxStreak = max(maxStreak, buffer)
- if (buffer == i || buffer == i - 1) { // first is solved today, second not solved
- streak = buffer
- }
- buffer = 0
- }
- }
-
- return ProfileActivitiesData(userActivity.pins, streak, maxStreak, isSolvedToday = userActivity.pins[0] > 0)
- }
+ private fun mapToProfileActivities(userActivitySummary: UserActivitySummary): ProfileActivitiesData =
+ ProfileActivitiesData(
+ pins = userActivitySummary.pins,
+ streak = userActivitySummary.recentStrike,
+ maxStreak = userActivitySummary.maxStrike,
+ isSolvedToday = userActivitySummary.solvedToday > 0
+ )
}
\ No newline at end of file
diff --git a/app/src/main/java/org/stepik/android/domain/streak/interactor/StreakInteractor.kt b/app/src/main/java/org/stepik/android/domain/streak/interactor/StreakInteractor.kt
index f1a56cff4b..ebc3edb36c 100644
--- a/app/src/main/java/org/stepik/android/domain/streak/interactor/StreakInteractor.kt
+++ b/app/src/main/java/org/stepik/android/domain/streak/interactor/StreakInteractor.kt
@@ -2,10 +2,8 @@ package org.stepik.android.domain.streak.interactor
import io.reactivex.Maybe
import org.stepic.droid.preferences.SharedPreferenceHelper
-import org.stepic.droid.util.StepikUtil
-import ru.nobird.android.domain.rx.toMaybe
import org.stepik.android.domain.user_activity.repository.UserActivityRepository
-import org.stepik.android.view.streak.notification.StreakNotificationDelegate
+import org.stepik.android.view.streak.notification.StreakNotificationScheduler
import javax.inject.Inject
class StreakInteractor
@@ -13,7 +11,7 @@ class StreakInteractor
constructor(
private val userActivityRepository: UserActivityRepository,
private val sharedPreferenceHelper: SharedPreferenceHelper,
- private val streakNotificationDelegate: StreakNotificationDelegate
+ private val streakNotificationScheduler: StreakNotificationScheduler
) {
fun needShowStreakDialog(): Boolean =
@@ -24,14 +22,13 @@ constructor(
fun onNeedShowStreak(): Maybe =
Maybe
.fromCallable { sharedPreferenceHelper.profile?.id }
- .flatMapSingleElement { userActivityRepository.getUserActivities(it) }
- .flatMap { it.firstOrNull()?.pins.toMaybe() }
- .map { StepikUtil.getCurrentStreak(it) }
+ .flatMapSingleElement { userActivityRepository.getUserActivitySummary(it) }
+ .map { it.recentStrike }
fun setStreakTime(timeIntervalCode: Int) {
sharedPreferenceHelper.isStreakNotificationEnabled = true
sharedPreferenceHelper.timeNotificationCode = timeIntervalCode
- streakNotificationDelegate.scheduleStreakNotification()
+ streakNotificationScheduler.scheduleStreakNotification()
}
fun wasStreakDialogSeenOnHomeScreen(): Boolean =
@@ -49,4 +46,4 @@ constructor(
private fun isAuthResponseFromStore(): Boolean =
sharedPreferenceHelper.authResponseFromStore != null
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/org/stepik/android/domain/user_activity/repository/UserActivityRepository.kt b/app/src/main/java/org/stepik/android/domain/user_activity/repository/UserActivityRepository.kt
index cb9b854929..8a03bac2f8 100644
--- a/app/src/main/java/org/stepik/android/domain/user_activity/repository/UserActivityRepository.kt
+++ b/app/src/main/java/org/stepik/android/domain/user_activity/repository/UserActivityRepository.kt
@@ -2,7 +2,10 @@ package org.stepik.android.domain.user_activity.repository
import io.reactivex.Single
import org.stepik.android.model.user.UserActivity
+import org.stepik.android.model.user.UserActivitySummary
interface UserActivityRepository {
fun getUserActivities(userId: Long): Single>
+
+ fun getUserActivitySummary(userId: Long): Single
}
\ No newline at end of file
diff --git a/app/src/main/java/org/stepik/android/remote/user_activity/UserActivityRemoteDataSourceImpl.kt b/app/src/main/java/org/stepik/android/remote/user_activity/UserActivityRemoteDataSourceImpl.kt
index 0b4381d85d..d2fb9d2ee0 100644
--- a/app/src/main/java/org/stepik/android/remote/user_activity/UserActivityRemoteDataSourceImpl.kt
+++ b/app/src/main/java/org/stepik/android/remote/user_activity/UserActivityRemoteDataSourceImpl.kt
@@ -4,7 +4,9 @@ import io.reactivex.Single
import io.reactivex.functions.Function
import org.stepik.android.data.user_activity.source.UserActivityRemoteDataSource
import org.stepik.android.model.user.UserActivity
+import org.stepik.android.model.user.UserActivitySummary
import org.stepik.android.remote.user_activity.model.UserActivityResponse
+import org.stepik.android.remote.user_activity.model.UserActivitySummaryResponse
import org.stepik.android.remote.user_activity.service.UserActivityService
import javax.inject.Inject
@@ -16,8 +18,16 @@ constructor(
private val userActivityResponseMapper =
Function>(UserActivityResponse::userActivities)
+ private val userActivitySummaryResponseMapper =
+ Function { it.userActivitySummaries.first() }
+
override fun getUserActivities(userId: Long): Single> =
userActivityService
.getUserActivitiesReactive(userId)
.map(userActivityResponseMapper)
+
+ override fun getUserActivitySummary(userId: Long): Single =
+ userActivityService
+ .getUserActivitySummaryReactive(userId)
+ .map(userActivitySummaryResponseMapper)
}
\ No newline at end of file
diff --git a/app/src/main/java/org/stepik/android/remote/user_activity/model/UserActivitySummaryResponse.kt b/app/src/main/java/org/stepik/android/remote/user_activity/model/UserActivitySummaryResponse.kt
new file mode 100644
index 0000000000..00e698bcf3
--- /dev/null
+++ b/app/src/main/java/org/stepik/android/remote/user_activity/model/UserActivitySummaryResponse.kt
@@ -0,0 +1,13 @@
+package org.stepik.android.remote.user_activity.model
+
+import com.google.gson.annotations.SerializedName
+import org.stepik.android.model.Meta
+import org.stepik.android.model.user.UserActivitySummary
+import org.stepik.android.remote.base.model.MetaResponse
+
+class UserActivitySummaryResponse(
+ @SerializedName("meta")
+ override val meta: Meta,
+ @SerializedName("user-activity-summaries")
+ val userActivitySummaries: List
+) : MetaResponse
\ No newline at end of file
diff --git a/app/src/main/java/org/stepik/android/remote/user_activity/service/UserActivityService.kt b/app/src/main/java/org/stepik/android/remote/user_activity/service/UserActivityService.kt
index f49734d387..28190596e0 100644
--- a/app/src/main/java/org/stepik/android/remote/user_activity/service/UserActivityService.kt
+++ b/app/src/main/java/org/stepik/android/remote/user_activity/service/UserActivityService.kt
@@ -2,10 +2,14 @@ package org.stepik.android.remote.user_activity.service
import io.reactivex.Single
import org.stepik.android.remote.user_activity.model.UserActivityResponse
+import org.stepik.android.remote.user_activity.model.UserActivitySummaryResponse
import retrofit2.http.GET
import retrofit2.http.Path
interface UserActivityService {
@GET("api/user-activities/{userId}")
fun getUserActivitiesReactive(@Path("userId") userId: Long): Single
+
+ @GET("api/user-activity-summaries/{userId}")
+ fun getUserActivitySummaryReactive(@Path("userId") userId: Long): Single
}
\ No newline at end of file
diff --git a/app/src/main/java/org/stepik/android/view/auth/extension/AuthKeyboardInsetsAnimationExtension.kt b/app/src/main/java/org/stepik/android/view/auth/extension/AuthKeyboardInsetsAnimationExtension.kt
new file mode 100644
index 0000000000..50293132a1
--- /dev/null
+++ b/app/src/main/java/org/stepik/android/view/auth/extension/AuthKeyboardInsetsAnimationExtension.kt
@@ -0,0 +1,200 @@
+package org.stepik.android.view.auth.extension
+
+import android.view.View
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsAnimationCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.core.view.doOnLayout
+import org.stepik.android.view.auth.ui.view.AuthScrollView
+
+fun View.addAuthKeyboardInsetsAnimation(
+ contentView: View,
+ logoView: View,
+ titleView: View,
+ keyboardPinnedView: View? = null
+) {
+ AuthKeyboardInsetsAnimationDelegate(
+ rootView = this,
+ contentView = contentView,
+ logoView = logoView,
+ titleView = titleView,
+ keyboardPinnedView = keyboardPinnedView
+ ).bind()
+}
+
+private class AuthKeyboardInsetsAnimationDelegate(
+ private val rootView: View,
+ private val contentView: View,
+ private val logoView: View,
+ private val titleView: View,
+ private val keyboardPinnedView: View?
+) {
+ private var isImeAnimationRunning = false
+ private var lastImeBottomInset = 0
+ private var isImeVisible = false
+
+ private var isInitialStateCaptured = false
+
+ private var logoHiddenTranslationY = 0f
+ private var titleHiddenTranslationY = 0f
+ private var contentHiddenTranslationY = 0f
+
+ private var imeAnimationLowerBound = 0
+ private var imeAnimationUpperBound = 0
+
+ private var initialRootPaddingLeft = 0
+ private var initialRootPaddingTop = 0
+ private var initialRootPaddingRight = 0
+ private var initialRootPaddingBottom = 0
+ private var lastSystemBarsBottomInset = 0
+
+ fun bind() {
+ (rootView as? AuthScrollView)?.isFocusScrollEnabled = false
+
+ ViewCompat.setOnApplyWindowInsetsListener(rootView) { _, insets ->
+ val systemBarsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
+ val imeBottomInset = getImeBottomInset(insets)
+ isImeVisible = insets.isImeVisible()
+ lastImeBottomInset = imeBottomInset
+ lastSystemBarsBottomInset = systemBarsInsets.bottom
+
+ if (isInitialStateCaptured) {
+ applyInsetsPadding(systemBarsInsets)
+ }
+
+ if (!isImeAnimationRunning) {
+ setKeyboardProgress(if (isImeVisible) 1f else 0f)
+ }
+ insets
+ }
+
+ ViewCompat.setWindowInsetsAnimationCallback(
+ rootView,
+ object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) {
+ override fun onStart(
+ animation: WindowInsetsAnimationCompat,
+ bounds: WindowInsetsAnimationCompat.BoundsCompat
+ ): WindowInsetsAnimationCompat.BoundsCompat {
+ if (animation.isImeInsetsAnimation()) {
+ isImeAnimationRunning = true
+ captureHiddenTranslations()
+ imeAnimationLowerBound = bounds.lowerBound.bottom
+ imeAnimationUpperBound = bounds.upperBound.bottom
+ }
+ return bounds
+ }
+
+ override fun onProgress(
+ insets: WindowInsetsCompat,
+ runningAnimations: MutableList
+ ): WindowInsetsCompat {
+ val imeAnimation = runningAnimations.firstOrNull { it.isImeInsetsAnimation() }
+ if (imeAnimation != null) {
+ val systemBarsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
+ val imeBottomInset = getImeBottomInset(insets)
+ val animationProgress = imeAnimation.interpolatedFraction.coerceIn(0f, 1f)
+ lastImeBottomInset = imeBottomInset
+ lastSystemBarsBottomInset = systemBarsInsets.bottom
+
+ if (isInitialStateCaptured) {
+ applyInsetsPadding(systemBarsInsets)
+ }
+ setKeyboardProgress(resolveKeyboardProgress(imeBottomInset, animationProgress))
+ }
+
+ return insets
+ }
+
+ override fun onEnd(animation: WindowInsetsAnimationCompat) {
+ if (animation.isImeInsetsAnimation()) {
+ ViewCompat.getRootWindowInsets(rootView)?.let { insets ->
+ val systemBarsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
+ isImeVisible = insets.isImeVisible()
+ lastImeBottomInset = getImeBottomInset(insets)
+ lastSystemBarsBottomInset = systemBarsInsets.bottom
+ }
+ isImeAnimationRunning = false
+ setKeyboardProgress(if (isImeVisible) 1f else 0f)
+ ViewCompat.requestApplyInsets(rootView)
+ }
+ }
+ }
+ )
+
+ rootView.doOnLayout {
+ captureInitialState()
+ ViewCompat.requestApplyInsets(rootView)
+ }
+ }
+
+ private fun captureInitialState() {
+ if (isInitialStateCaptured) {
+ return
+ }
+
+ initialRootPaddingLeft = rootView.paddingLeft
+ initialRootPaddingTop = rootView.paddingTop
+ initialRootPaddingRight = rootView.paddingRight
+ initialRootPaddingBottom = rootView.paddingBottom
+
+ captureHiddenTranslations()
+
+ isInitialStateCaptured = true
+ }
+
+ private fun captureHiddenTranslations() {
+ logoHiddenTranslationY = -(logoView.top + logoView.height).toFloat()
+ titleHiddenTranslationY = -(titleView.top + titleView.height).toFloat()
+ contentHiddenTranslationY = -contentView.top.toFloat()
+ }
+
+ private fun setKeyboardProgress(progress: Float) {
+ if (!isInitialStateCaptured) {
+ return
+ }
+
+ val normalizedProgress = progress.coerceIn(0f, 1f)
+ val headerVisibilityProgress = 1f - normalizedProgress
+
+ logoView.alpha = headerVisibilityProgress
+ titleView.alpha = headerVisibilityProgress
+
+ logoView.visibility = View.VISIBLE
+ titleView.visibility = View.VISIBLE
+
+ logoView.translationY = logoHiddenTranslationY * normalizedProgress
+ titleView.translationY = titleHiddenTranslationY * normalizedProgress
+ contentView.translationY = contentHiddenTranslationY * normalizedProgress
+ keyboardPinnedView?.translationY = -(lastImeBottomInset - lastSystemBarsBottomInset).coerceAtLeast(0).toFloat()
+ }
+
+ private fun resolveKeyboardProgress(
+ imeBottomInset: Int,
+ fallbackAnimationProgress: Float
+ ): Float {
+ val imeAnimationRange = imeAnimationUpperBound - imeAnimationLowerBound
+ return if (imeAnimationRange > 0) {
+ ((imeBottomInset - imeAnimationLowerBound).toFloat() / imeAnimationRange).coerceIn(0f, 1f)
+ } else {
+ fallbackAnimationProgress.coerceIn(0f, 1f)
+ }
+ }
+
+ private fun applyInsetsPadding(systemBarsInsets: androidx.core.graphics.Insets) {
+ rootView.setPadding(
+ initialRootPaddingLeft + systemBarsInsets.left,
+ initialRootPaddingTop + systemBarsInsets.top,
+ initialRootPaddingRight + systemBarsInsets.right,
+ initialRootPaddingBottom + systemBarsInsets.bottom
+ )
+ }
+
+ private fun getImeBottomInset(insets: WindowInsetsCompat): Int =
+ insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
+
+ private fun WindowInsetsCompat.isImeVisible(): Boolean =
+ isVisible(WindowInsetsCompat.Type.ime()) || getImeBottomInset(this) > 0
+
+ private fun WindowInsetsAnimationCompat.isImeInsetsAnimation(): Boolean =
+ typeMask and WindowInsetsCompat.Type.ime() != 0
+}
diff --git a/app/src/main/java/org/stepik/android/view/auth/model/SocialNetwork.kt b/app/src/main/java/org/stepik/android/view/auth/model/SocialNetwork.kt
index fb59c57dcf..67aaa930f3 100644
--- a/app/src/main/java/org/stepik/android/view/auth/model/SocialNetwork.kt
+++ b/app/src/main/java/org/stepik/android/view/auth/model/SocialNetwork.kt
@@ -15,6 +15,5 @@ enum class SocialNetwork(
GOOGLE("google", R.drawable.ic_login_social_google),
VK("vk", R.drawable.ic_login_social_vk, isNeedUseAccessTokenInsteadOfCode = true),
// FACEBOOK("facebook", R.drawable.ic_login_social_fb, isNeedUseAccessTokenInsteadOfCode = true),
- TWITTER("twitter", R.drawable.ic_login_social_twitter),
GITHUB("github", R.drawable.ic_login_social_github)
}
\ No newline at end of file
diff --git a/app/src/main/java/org/stepik/android/view/auth/ui/activity/CredentialAuthActivity.kt b/app/src/main/java/org/stepik/android/view/auth/ui/activity/CredentialAuthActivity.kt
index 068051829e..39bf319669 100644
--- a/app/src/main/java/org/stepik/android/view/auth/ui/activity/CredentialAuthActivity.kt
+++ b/app/src/main/java/org/stepik/android/view/auth/ui/activity/CredentialAuthActivity.kt
@@ -12,6 +12,8 @@ import android.view.inputmethod.EditorInfo
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.res.ResourcesCompat
+import androidx.core.view.WindowCompat
+import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.ViewModelProvider
@@ -24,13 +26,13 @@ import org.stepic.droid.databinding.ActivityAuthCredentialBinding
import org.stepic.droid.model.Credentials
import org.stepic.droid.ui.activities.SmartLockActivityBase
import org.stepic.droid.ui.dialogs.LoadingProgressDialogFragment
-import org.stepic.droid.ui.util.setOnKeyboardOpenListener
import org.stepic.droid.util.ProgressHelper
import org.stepic.droid.util.toBundle
import org.stepik.android.domain.auth.model.LoginFailType
import org.stepik.android.model.Course
import org.stepik.android.presentation.auth.CredentialAuthPresenter
import org.stepik.android.presentation.auth.CredentialAuthView
+import org.stepik.android.view.auth.extension.addAuthKeyboardInsetsAnimation
import org.stepik.android.view.auth.extension.getMessageFor
import org.stepik.android.view.auth.model.AutoAuth
import org.stepik.android.view.base.ui.span.TypefaceSpanCompat
@@ -73,9 +75,11 @@ class CredentialAuthActivity : SmartLockActivityBase(), CredentialAuthView {
private val progressDialogFragment: DialogFragment =
LoadingProgressDialogFragment.newInstance()
+ private var keyboardTargetField: View? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ WindowCompat.setDecorFitsSystemWindows(window, false)
setContentView(R.layout.activity_auth_credential)
injectComponent()
@@ -149,13 +153,12 @@ class CredentialAuthActivity : SmartLockActivityBase(), CredentialAuthView {
initGoogleApiClient()
- setOnKeyboardOpenListener(binding.rootView, {
- binding.stepikLogo.isVisible = false
- binding.signInText.isVisible = false
- }, {
- binding.stepikLogo.isVisible = true
- binding.signInText.isVisible = true
- })
+ binding.loginRootView.addAuthKeyboardInsetsAnimation(
+ contentView = binding.container,
+ logoView = binding.stepikLogo,
+ titleView = binding.signInText,
+ keyboardPinnedView = binding.bottomButtons
+ )
if (savedInstanceState == null) {
setData(intent)
@@ -184,7 +187,7 @@ class CredentialAuthActivity : SmartLockActivityBase(), CredentialAuthView {
val signInWithPasswordSuffix = getString(R.string.sign_in_with_password_suffix)
val spannableSignIn = SpannableString(signInString + signInWithPasswordSuffix)
- val typeface = ResourcesCompat.getFont(this, R.font.roboto_medium)
+ val typeface = ResourcesCompat.getFont(this, R.font.roboto_bold)
spannableSignIn.setSpan(TypefaceSpanCompat(typeface), 0, signInString.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
@@ -196,6 +199,13 @@ class CredentialAuthActivity : SmartLockActivityBase(), CredentialAuthView {
setData(intent)
}
+ override fun onWindowFocusChanged(hasFocus: Boolean) {
+ super.onWindowFocusChanged(hasFocus)
+ if (hasFocus) {
+ showKeyboardForTargetField()
+ }
+ }
+
private fun setData(intent: Intent) {
val autoAuth = intent
.getSerializableExtra(EXTRA_AUTO_AUTH) as? AutoAuth
@@ -207,16 +217,39 @@ class CredentialAuthActivity : SmartLockActivityBase(), CredentialAuthView {
binding.loginField.setText(email)
binding.passwordField.setText(password)
- when {
+ val fieldToFocus = when {
email == null ->
- binding.loginField.requestFocus()
+ binding.loginField
password == null ->
- binding.passwordField.requestFocus()
+ binding.passwordField
+
+ else ->
+ null
}
+ fieldToFocus?.requestFocus()
if (autoAuth != AutoAuth.NONE) {
+ keyboardTargetField = null
submit(autoAuth)
+ } else {
+ keyboardTargetField = fieldToFocus
+ showKeyboardForTargetField()
+ }
+ }
+
+ private fun showKeyboardForTargetField() {
+ val targetField = keyboardTargetField ?: return
+ if (!window.decorView.hasWindowFocus()) {
+ return
+ }
+
+ targetField.post {
+ targetField.requestFocus()
+
+ keyboardTargetField = null
+ WindowCompat.getInsetsController(window, targetField)
+ .show(WindowInsetsCompat.Type.ime())
}
}
diff --git a/app/src/main/java/org/stepik/android/view/auth/ui/activity/RegistrationActivity.kt b/app/src/main/java/org/stepik/android/view/auth/ui/activity/RegistrationActivity.kt
index bd8cf71bf8..ab6bcd386d 100644
--- a/app/src/main/java/org/stepik/android/view/auth/ui/activity/RegistrationActivity.kt
+++ b/app/src/main/java/org/stepik/android/view/auth/ui/activity/RegistrationActivity.kt
@@ -14,6 +14,7 @@ import android.view.inputmethod.EditorInfo
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.res.ResourcesCompat
+import androidx.core.view.WindowCompat
import androidx.core.view.isVisible
import androidx.core.widget.CompoundButtonCompat
import androidx.fragment.app.DialogFragment
@@ -26,7 +27,6 @@ import org.stepic.droid.base.App
import org.stepic.droid.databinding.ActivityRegistrationBinding
import org.stepic.droid.ui.activities.SmartLockActivityBase
import org.stepic.droid.ui.dialogs.LoadingProgressDialogFragment
-import org.stepic.droid.ui.util.setOnKeyboardOpenListener
import org.stepic.droid.ui.util.snackbar
import org.stepic.droid.util.ProgressHelper
import org.stepic.droid.util.ValidatorUtil
@@ -41,6 +41,7 @@ import org.stepik.android.model.Course
import org.stepik.android.model.user.RegistrationCredentials
import org.stepik.android.presentation.auth.RegistrationPresenter
import org.stepik.android.presentation.auth.RegistrationView
+import org.stepik.android.view.auth.extension.addAuthKeyboardInsetsAnimation
import org.stepik.android.view.auth.model.AutoAuth
import org.stepik.android.view.base.ui.span.TypefaceSpanCompat
import ru.nobird.android.view.base.ui.extension.hideKeyboard
@@ -97,6 +98,7 @@ class RegistrationActivity : SmartLockActivityBase(), RegistrationView {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ WindowCompat.setDecorFitsSystemWindows(window, false)
setContentView(R.layout.activity_registration)
injectComponent()
@@ -187,13 +189,11 @@ class RegistrationActivity : SmartLockActivityBase(), RegistrationView {
setSignUpButtonState()
- setOnKeyboardOpenListener(binding.rootView, {
- binding.stepikLogo.isVisible = false
- binding.signUpText.isVisible = false
- }, {
- binding.stepikLogo.isVisible = true
- binding.signUpText.isVisible = true
- })
+ binding.registerRootView.addAuthKeyboardInsetsAnimation(
+ contentView = binding.container,
+ logoView = binding.stepikLogo,
+ titleView = binding.signUpText
+ )
}
private fun injectComponent() {
@@ -225,7 +225,7 @@ class RegistrationActivity : SmartLockActivityBase(), RegistrationView {
val signUpSuffix = getString(R.string.sign_up_with_email_suffix)
val spannableSignIn = SpannableString(signUpString + signUpSuffix)
- val typeface = ResourcesCompat.getFont(this, R.font.roboto_medium)
+ val typeface = ResourcesCompat.getFont(this, R.font.roboto_bold)
spannableSignIn.setSpan(TypefaceSpanCompat(typeface), 0, signUpString.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
diff --git a/app/src/main/java/org/stepik/android/view/auth/ui/activity/SocialAuthActivity.kt b/app/src/main/java/org/stepik/android/view/auth/ui/activity/SocialAuthActivity.kt
index 93b5e319c2..786a352292 100644
--- a/app/src/main/java/org/stepik/android/view/auth/ui/activity/SocialAuthActivity.kt
+++ b/app/src/main/java/org/stepik/android/view/auth/ui/activity/SocialAuthActivity.kt
@@ -11,7 +11,7 @@ import androidx.core.content.res.ResourcesCompat
import androidx.core.view.isVisible
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.ViewModelProvider
-import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.LinearLayoutManager
import dev.androidbroadcast.vbpd.viewBinding
import com.google.android.gms.auth.api.Auth
import com.google.android.gms.common.api.GoogleApiClient
@@ -20,7 +20,6 @@ import com.vk.api.sdk.auth.VKAccessToken
import com.vk.api.sdk.auth.VKAuthCallback
import com.vk.api.sdk.auth.VKScope
import com.vk.api.sdk.exceptions.VKApiCodes
-import jp.wasabeef.recyclerview.animators.FadeInDownAnimator
import org.stepic.droid.R
import org.stepic.droid.analytic.Analytic
import org.stepic.droid.analytic.experiments.DeferredAuthSplitTest
@@ -55,7 +54,6 @@ class SocialAuthActivity : SmartLockActivityBase(), SocialAuthView, SocialAuthCo
companion object {
private const val REQUEST_CODE_GOOGLE_SIGN_IN = 7007
- private const val KEY_SOCIAL_ADAPTER_STATE = "social_adapter_state_key"
private const val KEY_SELECTED_SOCIAL_TYPE = "selected_social_type"
private const val EXTRA_WAS_LOGOUT_KEY = "wasLogoutKey"
@@ -135,12 +133,7 @@ class SocialAuthActivity : SmartLockActivityBase(), SocialAuthView, SocialAuthCo
initGoogleApiClient(true) { showNetworkError() }
- val recyclerState = savedInstanceState?.getSerializable(KEY_SOCIAL_ADAPTER_STATE)
- if (recyclerState is SocialAuthAdapter.State) {
- initSocialRecycler(recyclerState)
- } else {
- initSocialRecycler()
- }
+ initSocialRecycler()
selectedSocialType = savedInstanceState?.getSerializable(KEY_SELECTED_SOCIAL_TYPE) as? SocialNetwork
@@ -148,7 +141,7 @@ class SocialAuthActivity : SmartLockActivityBase(), SocialAuthView, SocialAuthCo
val signInWithSocial = getString(R.string.sign_in_with_social_suffix)
val spannableSignIn = SpannableString(signInString + signInWithSocial)
- val typefaceSpan = TypefaceSpanCompat(ResourcesCompat.getFont(this, R.font.roboto_medium))
+ val typefaceSpan = TypefaceSpanCompat(ResourcesCompat.getFont(this, R.font.roboto_bold))
spannableSignIn.setSpan(typefaceSpan, 0, signInString.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
@@ -208,31 +201,10 @@ class SocialAuthActivity : SmartLockActivityBase(), SocialAuthView, SocialAuthCo
super.onPause()
}
- private fun initSocialRecycler(state: SocialAuthAdapter.State = SocialAuthAdapter.State.NORMAL) {
- binding.socialListRecyclerView.layoutManager = GridLayoutManager(this, 3)
-
- binding.socialListRecyclerView.itemAnimator = FadeInDownAnimator()
- .apply {
- removeDuration = 0
- }
-
- val adapter = SocialAuthAdapter(this::onSocialItemClicked, state)
- binding.showMore.setOnClickListener {
- binding.showMore.isVisible = false
- binding.showLess.isVisible = true
- adapter.showMore()
- }
-
- binding.showLess.setOnClickListener {
- binding.showLess.isVisible = false
- binding.showMore.isVisible = true
- adapter.showLess()
- }
+ private fun initSocialRecycler() {
+ binding.socialListRecyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
- binding.showLess.isVisible = state == SocialAuthAdapter.State.EXPANDED
- binding.showMore.isVisible = state == SocialAuthAdapter.State.NORMAL
-
- binding.socialListRecyclerView.adapter = adapter
+ binding.socialListRecyclerView.adapter = SocialAuthAdapter(this::onSocialItemClicked)
}
private fun onSocialItemClicked(type: SocialNetwork) {
@@ -391,10 +363,6 @@ class SocialAuthActivity : SmartLockActivityBase(), SocialAuthView, SocialAuthCo
}
override fun onSaveInstanceState(outState: Bundle) {
- val adapter = binding.socialListRecyclerView.adapter
- if (adapter is SocialAuthAdapter) {
- outState.putSerializable(KEY_SOCIAL_ADAPTER_STATE, adapter.state)
- }
selectedSocialType?.let { outState.putSerializable(KEY_SELECTED_SOCIAL_TYPE, it) }
super.onSaveInstanceState(outState)
}
diff --git a/app/src/main/java/org/stepik/android/view/auth/ui/view/AuthScrollView.kt b/app/src/main/java/org/stepik/android/view/auth/ui/view/AuthScrollView.kt
new file mode 100644
index 0000000000..e950e91dc7
--- /dev/null
+++ b/app/src/main/java/org/stepik/android/view/auth/ui/view/AuthScrollView.kt
@@ -0,0 +1,23 @@
+package org.stepik.android.view.auth.ui.view
+
+import android.content.Context
+import android.graphics.Rect
+import android.util.AttributeSet
+import android.widget.ScrollView
+
+class AuthScrollView
+@JvmOverloads
+constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : ScrollView(context, attrs, defStyleAttr) {
+ var isFocusScrollEnabled = true
+
+ override fun computeScrollDeltaToGetChildRectOnScreen(rect: Rect): Int =
+ if (isFocusScrollEnabled) {
+ super.computeScrollDeltaToGetChildRectOnScreen(rect)
+ } else {
+ 0
+ }
+}
diff --git a/app/src/main/java/org/stepik/android/view/streak/notification/StreakNotificationDelegate.kt b/app/src/main/java/org/stepik/android/view/streak/notification/StreakNotificationDelegate.kt
index 5d7599686e..ad2bdedb11 100644
--- a/app/src/main/java/org/stepik/android/view/streak/notification/StreakNotificationDelegate.kt
+++ b/app/src/main/java/org/stepik/android/view/streak/notification/StreakNotificationDelegate.kt
@@ -9,7 +9,6 @@ import org.stepic.droid.core.ScreenManager
import org.stepic.droid.preferences.SharedPreferenceHelper
import org.stepic.droid.util.AppConstants
import org.stepic.droid.util.DateTimeHelper
-import org.stepic.droid.util.StepikUtil
import org.stepik.android.domain.base.analytic.BUNDLEABLE_ANALYTIC_EVENT
import org.stepik.android.domain.base.analytic.toBundle
import org.stepik.android.domain.streak.analytic.StreakNotificationClicked
@@ -36,7 +35,7 @@ constructor(
private val sharedPreferenceHelper: SharedPreferenceHelper,
private val notificationHelper: NotificationHelper,
stepikNotificationManager: StepikNotificationManager
-) : NotificationDelegate("show_streak_notification", stepikNotificationManager) {
+) : NotificationDelegate("show_streak_notification", stepikNotificationManager), StreakNotificationScheduler {
companion object {
const val STREAK_NOTIFICATION_CLICKED = "streak_notification_clicked"
private const val STREAK_NOTIFICATION_ID = 3214L
@@ -48,23 +47,35 @@ constructor(
val numberOfStreakNotifications = sharedPreferenceHelper.numberOfStreakNotifications
if (numberOfStreakNotifications < AppConstants.MAX_NUMBER_OF_NOTIFICATION_STREAK) {
try {
- val pins: ArrayList = userActivityRepository.getUserActivities(sharedPreferenceHelper.profile?.id ?: throw Exception("User is not auth"))
+ val userId = sharedPreferenceHelper.profile?.id
+ ?: throw Exception("User is not auth")
+ val userActivitySummary = userActivityRepository
+ .getUserActivitySummary(userId)
.blockingGet()
- .firstOrNull()
- ?.pins!!
- val (currentStreak, isSolvedToday) = StepikUtil.getCurrentStreakExtended(pins)
- if (currentStreak <= 0) {
- analytic.reportEvent(Analytic.Streak.GET_ZERO_STREAK_NOTIFICATION)
- showNotificationWithoutStreakInfo(StreakNotificationType.ZERO)
- } else {
- // if current streak is > 0 -> streaks works! -> continue send it
- // it will reset before sending, after sending it will be incremented
- sharedPreferenceHelper.resetNumberOfStreakNotifications()
- if (isSolvedToday) {
- showNotificationStreakImprovement(currentStreak)
- } else {
- showNotificationWithStreakCallToAction(currentStreak)
+
+ val notificationType = getStreakNotificationType(
+ userActivitySummary.recentStrike,
+ userActivitySummary.solvedToday
+ )
+
+ when (notificationType) {
+ StreakNotificationType.ZERO -> {
+ analytic.reportEvent(Analytic.Streak.GET_ZERO_STREAK_NOTIFICATION)
+ showNotificationWithoutStreakInfo(StreakNotificationType.ZERO)
}
+
+ StreakNotificationType.SOLVED_TODAY -> {
+ sharedPreferenceHelper.resetNumberOfStreakNotifications()
+ showNotificationStreakImprovement(userActivitySummary.recentStrike)
+ }
+
+ StreakNotificationType.NOT_SOLVED_TODAY -> {
+ sharedPreferenceHelper.resetNumberOfStreakNotifications()
+ showNotificationWithStreakCallToAction(userActivitySummary.recentStrike)
+ }
+
+ StreakNotificationType.NO_INTERNET ->
+ Unit
}
} catch (exception: Exception) {
// no internet || cant get streaks -> show some notification without streak information.
@@ -81,7 +92,7 @@ constructor(
}
}
- fun scheduleStreakNotification() {
+ override fun scheduleStreakNotification() {
if (sharedPreferenceHelper.isStreakNotificationEnabled) {
// plan new alarm
val hour = sharedPreferenceHelper.timeNotificationCode
@@ -157,4 +168,14 @@ constructor(
.createStreakNotificationIntent(context, StreakNotificationDismissed(notificationType.type).toBundle())
return PendingIntentCompat.getBroadcast(context, 0, deleteIntent, PendingIntent.FLAG_CANCEL_CURRENT)
}
-}
\ No newline at end of file
+}
+
+internal fun getStreakNotificationType(recentStrike: Int, solvedToday: Int): StreakNotificationType =
+ when {
+ recentStrike <= 0 ->
+ StreakNotificationType.ZERO
+ solvedToday > 0 ->
+ StreakNotificationType.SOLVED_TODAY
+ else ->
+ StreakNotificationType.NOT_SOLVED_TODAY
+ }
diff --git a/app/src/main/java/org/stepik/android/view/streak/notification/StreakNotificationScheduler.kt b/app/src/main/java/org/stepik/android/view/streak/notification/StreakNotificationScheduler.kt
new file mode 100644
index 0000000000..63a1c68e3c
--- /dev/null
+++ b/app/src/main/java/org/stepik/android/view/streak/notification/StreakNotificationScheduler.kt
@@ -0,0 +1,5 @@
+package org.stepik.android.view.streak.notification
+
+interface StreakNotificationScheduler {
+ fun scheduleStreakNotification()
+}
diff --git a/app/src/main/res/drawable/ic_login_social_twitter.xml b/app/src/main/res/drawable/ic_login_social_twitter.xml
deleted file mode 100644
index b415802f33..0000000000
--- a/app/src/main/res/drawable/ic_login_social_twitter.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable/ic_login_social_vk.xml b/app/src/main/res/drawable/ic_login_social_vk.xml
index 537a2fa567..6c7ea7dfe4 100644
--- a/app/src/main/res/drawable/ic_login_social_vk.xml
+++ b/app/src/main/res/drawable/ic_login_social_vk.xml
@@ -1,6 +1,15 @@
-
-
+
+
+
+
diff --git a/app/src/main/res/layout/activity_auth_credential.xml b/app/src/main/res/layout/activity_auth_credential.xml
index 7eee035810..6f89e1836a 100644
--- a/app/src/main/res/layout/activity_auth_credential.xml
+++ b/app/src/main/res/layout/activity_auth_credential.xml
@@ -1,5 +1,5 @@
-
+ app:layout_constraintTop_toBottomOf="@id/stepikLogo"
+ app:layout_constraintBottom_toTopOf="@id/bottomButtons"
+ app:layout_constraintVertical_bias="0"
+ android:layout_marginTop="32dp"
+ android:layout_marginBottom="16dp">
@@ -185,4 +185,4 @@
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/activity_auth_social.xml b/app/src/main/res/layout/activity_auth_social.xml
index 4273f96c62..929d9785d6 100644
--- a/app/src/main/res/layout/activity_auth_social.xml
+++ b/app/src/main/res/layout/activity_auth_social.xml
@@ -44,6 +44,16 @@
android:focusableInTouchMode="false"
android:src="@drawable/ic_stepik_logotype_square_black" />
+
+
+ app:layout_constraintTop_toBottomOf="@id/authTopSpacing">
@@ -114,31 +120,11 @@
android:layout_height="wrap_content"
android:overScrollMode="never"
android:layout_gravity="center_horizontal"
+ android:layout_marginTop="8dp"
tools:listitem="@layout/item_social"
- app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
tools:itemCount="3" />
-
-
-
-
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/activity_registration.xml b/app/src/main/res/layout/activity_registration.xml
index 4b0c0fcc90..bb3c993b2c 100644
--- a/app/src/main/res/layout/activity_registration.xml
+++ b/app/src/main/res/layout/activity_registration.xml
@@ -1,5 +1,5 @@
-
+ android:paddingBottom="24dp"
+ android:layout_marginTop="32dp">
@@ -222,4 +220,4 @@
-
+
diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml
index 2228aff81d..1d57356d0b 100644
--- a/app/src/main/res/values-be/strings.xml
+++ b/app/src/main/res/values-be/strings.xml
@@ -374,8 +374,6 @@
Паглядзець усе
" праз сацыяльныя сеткі"
Уваход праз сацсеткі
- Яшчэ
- Менш
Імя
" з e-mail"
Адкрыйце для сябе бясплатныя онлайн курсы
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index bbd84264c8..19f8b13b5f 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -402,8 +402,6 @@
" через социальные сети"
Вход через соцсети
- Ещё
- Меньше
Имя
" с e-mail"
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 8751a46a37..ecd426ced4 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -128,7 +128,7 @@
@dimen/min_ux_touchable_size
-1px
- 36dp
+ 24dp
72dp
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 3e3659cd50..6f74a5453f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -412,8 +412,6 @@
" with social accounts"
Sign in with social
- More
- Less
Name
" with e-mail"
diff --git a/app/src/test/java/org/stepic/droid/core/presenters/HomeStreakPresenterTest.kt b/app/src/test/java/org/stepic/droid/core/presenters/HomeStreakPresenterTest.kt
new file mode 100644
index 0000000000..973b745329
--- /dev/null
+++ b/app/src/test/java/org/stepic/droid/core/presenters/HomeStreakPresenterTest.kt
@@ -0,0 +1,76 @@
+package org.stepic.droid.core.presenters
+
+import io.reactivex.Single
+import io.reactivex.schedulers.Schedulers
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Test
+import org.stepic.droid.core.presenters.contracts.HomeStreakView
+import org.stepic.droid.preferences.SharedPreferenceHelper
+import org.stepik.android.domain.user_activity.repository.UserActivityRepository
+import org.stepik.android.model.user.Profile
+import org.stepik.android.model.user.UserActivity
+import org.stepik.android.model.user.UserActivitySummary
+
+class HomeStreakPresenterTest {
+ @Test
+ fun `home streak displays recent strike above pins limit`() {
+ val view = FakeHomeStreakView()
+ val presenter = HomeStreakPresenter(
+ backgroundScheduler = Schedulers.trampoline(),
+ mainScheduler = Schedulers.trampoline(),
+ userActivityRepository = FakeUserActivityRepository(
+ UserActivitySummary(
+ recentStrike = RECENT_STRIKE,
+ solvedToday = 0,
+ pins = listOf(0L, 0L, 0L, 0L, 0L, 0L, 0L)
+ )
+ ),
+ sharedPreferences = TestSharedPreferenceHelper(Profile(id = USER_ID))
+ )
+
+ presenter.attachView(view)
+ presenter.onNeedShowStreak()
+
+ assertEquals(RECENT_STRIKE, view.shownStreak)
+ assertFalse(view.isEmptyStreakShown)
+ }
+
+ private class FakeHomeStreakView : HomeStreakView {
+ var shownStreak: Int? = null
+ private set
+
+ var isEmptyStreakShown: Boolean = false
+ private set
+
+ override fun showStreak(streak: Int) {
+ shownStreak = streak
+ }
+
+ override fun onEmptyStreak() {
+ isEmptyStreakShown = true
+ }
+ }
+
+ private class FakeUserActivityRepository(
+ private val summary: UserActivitySummary
+ ) : UserActivityRepository {
+ override fun getUserActivities(userId: Long): Single> =
+ Single.error(UnsupportedOperationException("Legacy user activities should not be used"))
+
+ override fun getUserActivitySummary(userId: Long): Single =
+ Single.just(summary)
+ }
+
+ private class TestSharedPreferenceHelper(
+ private val profile: Profile
+ ) : SharedPreferenceHelper(null, null, null, null) {
+ override fun getProfile(): Profile =
+ profile
+ }
+
+ private companion object {
+ const val USER_ID = 1L
+ const val RECENT_STRIKE = 380
+ }
+}
diff --git a/app/src/test/java/org/stepik/android/domain/auth/mapper/SocialConsentMapperTest.kt b/app/src/test/java/org/stepik/android/domain/auth/mapper/SocialConsentMapperTest.kt
index 73f70ce06b..cb82cf37b5 100644
--- a/app/src/test/java/org/stepik/android/domain/auth/mapper/SocialConsentMapperTest.kt
+++ b/app/src/test/java/org/stepik/android/domain/auth/mapper/SocialConsentMapperTest.kt
@@ -63,9 +63,9 @@ class SocialConsentMapperTest {
isMarketingVisible = true,
isMarketingChecked = true
)
- val result = mapper.mapConsent(state, SocialNetwork.TWITTER)
+ val result = mapper.mapConsent(state, SocialNetwork.GITHUB)
assertTrue(result is SocialConsentResult.Valid)
- assertEquals(SocialNetwork.TWITTER, (result as SocialConsentResult.Valid).provider)
+ assertEquals(SocialNetwork.GITHUB, (result as SocialConsentResult.Valid).provider)
}
// AC3: Marketing Hidden Clears Pending Consent
@@ -134,13 +134,6 @@ class SocialConsentMapperTest {
assertEquals(SocialNetwork.VK, result.provider)
}
- @Test
- fun `twitter provider is preserved in valid result`() {
- val state = validConsentState()
- val result = mapper.mapConsent(state, SocialNetwork.TWITTER) as SocialConsentResult.Valid
- assertEquals(SocialNetwork.TWITTER, result.provider)
- }
-
@Test
fun `github provider is preserved in valid result`() {
val state = validConsentState()
diff --git a/app/src/test/java/org/stepik/android/domain/profile_activities/interactor/ProfileActivitiesInteractorTest.kt b/app/src/test/java/org/stepik/android/domain/profile_activities/interactor/ProfileActivitiesInteractorTest.kt
new file mode 100644
index 0000000000..1a7bde010f
--- /dev/null
+++ b/app/src/test/java/org/stepik/android/domain/profile_activities/interactor/ProfileActivitiesInteractorTest.kt
@@ -0,0 +1,89 @@
+package org.stepik.android.domain.profile_activities.interactor
+
+import com.nhaarman.mockitokotlin2.doReturn
+import com.nhaarman.mockitokotlin2.verify
+import com.nhaarman.mockitokotlin2.whenever
+import io.reactivex.Single
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnitRunner
+import org.stepik.android.domain.profile_activities.model.ProfileActivitiesData
+import org.stepik.android.domain.user_activity.repository.UserActivityRepository
+import org.stepik.android.model.user.UserActivitySummary
+
+@RunWith(MockitoJUnitRunner::class)
+class ProfileActivitiesInteractorTest {
+ @Mock
+ private lateinit var userActivityRepository: UserActivityRepository
+
+ @Test
+ fun `summary without solved today maps to continue streak data`() {
+ val pins = listOf(0L, 2L, 1L, 3L, 0L, 1L, 4L)
+ val summary = UserActivitySummary(
+ recentStrike = 377,
+ solvedToday = 0,
+ maxStrike = 412,
+ pins = pins
+ )
+ whenever(userActivityRepository.getUserActivitySummary(USER_ID)) doReturn Single.just(summary)
+
+ ProfileActivitiesInteractor(userActivityRepository)
+ .getProfileActivities(USER_ID)
+ .test()
+ .assertComplete()
+ .assertResult(
+ ProfileActivitiesData(pins, streak = 377, maxStreak = 412, isSolvedToday = false)
+ )
+
+ verify(userActivityRepository).getUserActivitySummary(USER_ID)
+ }
+
+ @Test
+ fun `summary with solved today maps to active streak data`() {
+ val pins = listOf(0L, 0L, 0L, 0L, 0L, 0L, 0L)
+ val summary = UserActivitySummary(
+ recentStrike = 378,
+ solvedToday = 2,
+ maxStrike = 412,
+ pins = pins
+ )
+ whenever(userActivityRepository.getUserActivitySummary(USER_ID)) doReturn Single.just(summary)
+
+ ProfileActivitiesInteractor(userActivityRepository)
+ .getProfileActivities(USER_ID)
+ .test()
+ .assertComplete()
+ .assertResult(
+ ProfileActivitiesData(pins, streak = 378, maxStreak = 412, isSolvedToday = true)
+ )
+
+ verify(userActivityRepository).getUserActivitySummary(USER_ID)
+ }
+
+ @Test
+ fun `zero summary maps to start streak data`() {
+ val pins = listOf(0L, 0L, 0L, 0L, 0L, 0L, 0L)
+ val summary = UserActivitySummary(
+ recentStrike = 0,
+ solvedToday = 0,
+ maxStrike = 0,
+ pins = pins
+ )
+ whenever(userActivityRepository.getUserActivitySummary(USER_ID)) doReturn Single.just(summary)
+
+ ProfileActivitiesInteractor(userActivityRepository)
+ .getProfileActivities(USER_ID)
+ .test()
+ .assertComplete()
+ .assertResult(
+ ProfileActivitiesData(pins, streak = 0, maxStreak = 0, isSolvedToday = false)
+ )
+
+ verify(userActivityRepository).getUserActivitySummary(USER_ID)
+ }
+
+ private companion object {
+ const val USER_ID = 1L
+ }
+}
diff --git a/app/src/test/java/org/stepik/android/domain/streak/interactor/StreakInteractorTest.kt b/app/src/test/java/org/stepik/android/domain/streak/interactor/StreakInteractorTest.kt
new file mode 100644
index 0000000000..580822ee8c
--- /dev/null
+++ b/app/src/test/java/org/stepik/android/domain/streak/interactor/StreakInteractorTest.kt
@@ -0,0 +1,57 @@
+package org.stepik.android.domain.streak.interactor
+
+import io.reactivex.Single
+import org.junit.Test
+import org.stepic.droid.preferences.SharedPreferenceHelper
+import org.stepik.android.domain.user_activity.repository.UserActivityRepository
+import org.stepik.android.model.user.Profile
+import org.stepik.android.model.user.UserActivity
+import org.stepik.android.model.user.UserActivitySummary
+import org.stepik.android.view.streak.notification.StreakNotificationScheduler
+
+class StreakInteractorTest {
+ @Test
+ fun `on need show streak returns recent strike above pins limit`() {
+ StreakInteractor(
+ userActivityRepository = FakeUserActivityRepository(
+ UserActivitySummary(
+ recentStrike = RECENT_STRIKE,
+ solvedToday = 0,
+ pins = listOf(0L, 0L, 0L, 0L, 0L, 0L, 0L)
+ )
+ ),
+ sharedPreferenceHelper = TestSharedPreferenceHelper(Profile(id = USER_ID)),
+ streakNotificationScheduler = FakeStreakNotificationScheduler()
+ )
+ .onNeedShowStreak()
+ .test()
+ .assertComplete()
+ .assertResult(RECENT_STRIKE)
+ }
+
+ private class FakeUserActivityRepository(
+ private val summary: UserActivitySummary
+ ) : UserActivityRepository {
+ override fun getUserActivities(userId: Long): Single> =
+ Single.error(UnsupportedOperationException("Legacy user activities should not be used"))
+
+ override fun getUserActivitySummary(userId: Long): Single =
+ Single.just(summary)
+ }
+
+ private class TestSharedPreferenceHelper(
+ private val profile: Profile
+ ) : SharedPreferenceHelper(null, null, null, null) {
+ override fun getProfile(): Profile =
+ profile
+ }
+
+ private class FakeStreakNotificationScheduler : StreakNotificationScheduler {
+ override fun scheduleStreakNotification() {}
+ }
+
+ private companion object {
+ const val USER_ID = 1L
+ const val RECENT_STRIKE = 380
+ }
+}
diff --git a/app/src/test/java/org/stepik/android/view/streak/notification/StreakNotificationDelegateTest.kt b/app/src/test/java/org/stepik/android/view/streak/notification/StreakNotificationDelegateTest.kt
new file mode 100644
index 0000000000..72841e86a4
--- /dev/null
+++ b/app/src/test/java/org/stepik/android/view/streak/notification/StreakNotificationDelegateTest.kt
@@ -0,0 +1,35 @@
+package org.stepik.android.view.streak.notification
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.stepik.android.view.streak.model.StreakNotificationType
+
+class StreakNotificationDelegateTest {
+ @Test
+ fun `recent strike less than or equal to zero selects zero streak notification`() {
+ assertEquals(
+ StreakNotificationType.ZERO,
+ getStreakNotificationType(recentStrike = 0, solvedToday = 0)
+ )
+ assertEquals(
+ StreakNotificationType.ZERO,
+ getStreakNotificationType(recentStrike = -1, solvedToday = 1)
+ )
+ }
+
+ @Test
+ fun `positive recent strike above pins limit with solved today selects improvement notification`() {
+ assertEquals(
+ StreakNotificationType.SOLVED_TODAY,
+ getStreakNotificationType(recentStrike = 381, solvedToday = 1)
+ )
+ }
+
+ @Test
+ fun `positive recent strike above pins limit without solved today selects call to action notification`() {
+ assertEquals(
+ StreakNotificationType.NOT_SOLVED_TODAY,
+ getStreakNotificationType(recentStrike = 380, solvedToday = 0)
+ )
+ }
+}
diff --git a/model/src/main/java/org/stepik/android/model/user/UserActivitySummary.kt b/model/src/main/java/org/stepik/android/model/user/UserActivitySummary.kt
new file mode 100644
index 0000000000..e105416cc7
--- /dev/null
+++ b/model/src/main/java/org/stepik/android/model/user/UserActivitySummary.kt
@@ -0,0 +1,20 @@
+package org.stepik.android.model.user
+
+import com.google.gson.annotations.SerializedName
+
+data class UserActivitySummary(
+ @SerializedName("id")
+ val id: Long = -1,
+ @SerializedName("recent_strike")
+ val recentStrike: Int = 0,
+ @SerializedName("solved_today")
+ val solvedToday: Int = 0,
+ @SerializedName("max_strike")
+ val maxStrike: Int = 0,
+ @SerializedName("solved")
+ val solved: Int = 0,
+ @SerializedName("days")
+ val days: Int = 0,
+ @SerializedName("pins")
+ val pins: List = emptyList()
+)
\ No newline at end of file