diff --git a/app/build.gradle b/app/build.gradle index 15dadf8982..d3e08b102d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,7 +1,7 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-parcelize' apply plugin: 'com.google.firebase.crashlytics' apply from: '../code_quality_tools/jacoco.gradle' //apply from: '../code_quality_tools/findbugs.gradle' @@ -156,10 +156,6 @@ android { namespace 'org.stepic.droid' } -androidExtensions { - experimental = true -} - configurations { androidTestImplementation { // accessibility-test-framework pulls the full checker jar, which duplicates checker-qual from Guava. diff --git a/app/src/debug/java/org/stepik/android/view/debug/ui/adapter/delegate/InAppPurchaseAdapterDelegate.kt b/app/src/debug/java/org/stepik/android/view/debug/ui/adapter/delegate/InAppPurchaseAdapterDelegate.kt index 7dc934bf2a..a40ae08576 100644 --- a/app/src/debug/java/org/stepik/android/view/debug/ui/adapter/delegate/InAppPurchaseAdapterDelegate.kt +++ b/app/src/debug/java/org/stepik/android/view/debug/ui/adapter/delegate/InAppPurchaseAdapterDelegate.kt @@ -1,14 +1,12 @@ package org.stepik.android.view.debug.ui.adapter.delegate -import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible +import by.kirich1409.viewbindingdelegate.viewBinding import com.android.billingclient.api.Purchase -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.debug.item_in_app_purchase.* import org.stepic.droid.R +import org.stepic.droid.databinding.ItemInAppPurchaseBinding import org.stepic.droid.util.DateTimeHelper -import org.stepic.droid.util.toObject import org.stepik.android.domain.course.model.CoursePurchasePayload import ru.nobird.android.ui.adapterdelegates.AdapterDelegate import ru.nobird.android.ui.adapterdelegates.DelegateViewHolder @@ -24,17 +22,21 @@ class InAppPurchaseAdapterDelegate( override fun onCreateViewHolder(parent: ViewGroup): DelegateViewHolder = ViewHolder(createView(parent, R.layout.item_in_app_purchase)) - private inner class ViewHolder(override val containerView: View) : DelegateViewHolder(containerView), LayoutContainer { + private inner class ViewHolder( + containerView: android.view.View + ) : DelegateViewHolder(containerView) { + private val viewBinding: ItemInAppPurchaseBinding by viewBinding { ItemInAppPurchaseBinding.bind(itemView) } + init { - inAppPurchaseConsumeAction.setOnClickListener { itemData?.let(onItemClick) } + viewBinding.inAppPurchaseConsumeAction.setOnClickListener { itemData?.let(onItemClick) } } override fun onBind(data: Purchase) { - inAppPurchaseSku.text = data.skus.first() - inAppPurchaseTime.text = context.getString(R.string.debug_purchase_date, DateTimeHelper.getPrintableDate(Date(data.purchaseTime), DateTimeHelper.DISPLAY_DATETIME_PATTERN, TimeZone.getDefault())) - inAppPurchaseStatus.text = context.getString(R.string.debug_purchase_status, data.purchaseState.toString()) + viewBinding.inAppPurchaseSku.text = data.skus.first() + viewBinding.inAppPurchaseTime.text = context.getString(R.string.debug_purchase_date, DateTimeHelper.getPrintableDate(Date(data.purchaseTime), DateTimeHelper.DISPLAY_DATETIME_PATTERN, TimeZone.getDefault())) + viewBinding.inAppPurchaseStatus.text = context.getString(R.string.debug_purchase_status, data.purchaseState.toString()) - inAppPurchaseCourse.isVisible = data.developerPayload.isNotEmpty() - inAppPurchaseUser.isVisible = data.developerPayload.isNotEmpty() + viewBinding.inAppPurchaseCourse.isVisible = data.developerPayload.isNotEmpty() + viewBinding.inAppPurchaseUser.isVisible = data.developerPayload.isNotEmpty() } } -} \ No newline at end of file +} diff --git a/app/src/debug/java/org/stepik/android/view/debug/ui/adapter/delegate/SplitTestDataAdapterDelegate.kt b/app/src/debug/java/org/stepik/android/view/debug/ui/adapter/delegate/SplitTestDataAdapterDelegate.kt index 6f6bd20719..454b573f13 100644 --- a/app/src/debug/java/org/stepik/android/view/debug/ui/adapter/delegate/SplitTestDataAdapterDelegate.kt +++ b/app/src/debug/java/org/stepik/android/view/debug/ui/adapter/delegate/SplitTestDataAdapterDelegate.kt @@ -2,9 +2,9 @@ package org.stepik.android.view.debug.ui.adapter.delegate import android.view.View import android.view.ViewGroup -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_split_test_data.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemSplitTestDataBinding import org.stepik.android.domain.debug.model.SplitTestData import ru.nobird.android.ui.adapterdelegates.AdapterDelegate import ru.nobird.android.ui.adapterdelegates.DelegateViewHolder @@ -18,13 +18,15 @@ class SplitTestDataAdapterDelegate( override fun onCreateViewHolder(parent: ViewGroup): DelegateViewHolder = ViewHolder(createView(parent, R.layout.item_split_test_data)) - private inner class ViewHolder(override val containerView: View) : DelegateViewHolder(containerView), LayoutContainer { + private inner class ViewHolder(containerView: View) : DelegateViewHolder(containerView) { + private val viewBinding: ItemSplitTestDataBinding by viewBinding { ItemSplitTestDataBinding.bind(itemView) } + init { containerView.setOnClickListener { itemData?.let { onItemClick(it.splitTestName, it.splitTestValue, it.splitTestGroups) } } } override fun onBind(data: SplitTestData) { - splitTestGroupTitle.text = data.splitTestName - splitTestGroupValue.text = data.splitTestValue + viewBinding.splitTestGroupTitle.text = data.splitTestName + viewBinding.splitTestGroupValue.text = data.splitTestValue } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepic/droid/adaptive/ui/activities/AdaptiveStatsActivity.kt b/app/src/main/java/org/stepic/droid/adaptive/ui/activities/AdaptiveStatsActivity.kt index b288349424..716abc4c51 100644 --- a/app/src/main/java/org/stepic/droid/adaptive/ui/activities/AdaptiveStatsActivity.kt +++ b/app/src/main/java/org/stepic/droid/adaptive/ui/activities/AdaptiveStatsActivity.kt @@ -4,13 +4,14 @@ import android.os.Bundle import android.view.MenuItem import androidx.appcompat.app.AppCompatDelegate import androidx.viewpager2.widget.ViewPager2 +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.tabs.TabLayoutMediator -import kotlinx.android.synthetic.main.activity_adaptive_stats.* import org.stepic.droid.R import org.stepic.droid.adaptive.model.AdaptiveStatsTabs import org.stepic.droid.adaptive.ui.adapters.AdaptiveStatsViewPagerAdapter import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.base.FragmentActivityBase +import org.stepic.droid.databinding.ActivityAdaptiveStatsBinding import org.stepic.droid.ui.util.initCenteredToolbar import org.stepic.droid.util.AppConstants @@ -21,6 +22,7 @@ class AdaptiveStatsActivity : FragmentActivityBase() { } } + private val adaptiveStatsBinding: ActivityAdaptiveStatsBinding by viewBinding(ActivityAdaptiveStatsBinding::bind) private var courseId: Long = 0 private var hasSavedInstanceState: Boolean = false private lateinit var adapter: AdaptiveStatsViewPagerAdapter @@ -46,24 +48,24 @@ class AdaptiveStatsActivity : FragmentActivityBase() { adapter = AdaptiveStatsViewPagerAdapter(this, courseId) - pager.adapter = adapter - pager.offscreenPageLimit = adapter.itemCount + adaptiveStatsBinding.pager.adapter = adapter + adaptiveStatsBinding.pager.offscreenPageLimit = adapter.itemCount - TabLayoutMediator(tabLayout, pager) { tab, position -> + TabLayoutMediator(adaptiveStatsBinding.tabLayout, adaptiveStatsBinding.pager) { tab, position -> tab.setText(AdaptiveStatsTabs.values()[position].fragmentTitleRes) }.attach() } override fun onResume() { super.onResume() - pager.registerOnPageChangeCallback(onPageChangeListener) - if (!hasSavedInstanceState && pager.currentItem == 0) { + adaptiveStatsBinding.pager.registerOnPageChangeCallback(onPageChangeListener) + if (!hasSavedInstanceState && adaptiveStatsBinding.pager.currentItem == 0) { onPageChangeListener.onPageSelected(0) } } override fun onPause() { - pager.unregisterOnPageChangeCallback(onPageChangeListener) + adaptiveStatsBinding.pager.unregisterOnPageChangeCallback(onPageChangeListener) super.onPause() } @@ -74,4 +76,4 @@ class AdaptiveStatsActivity : FragmentActivityBase() { } return super.onOptionsItemSelected(item) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepic/droid/adaptive/ui/adapters/AdaptiveRatingAdapter.kt b/app/src/main/java/org/stepic/droid/adaptive/ui/adapters/AdaptiveRatingAdapter.kt index c7efd9ee79..68c106969e 100644 --- a/app/src/main/java/org/stepic/droid/adaptive/ui/adapters/AdaptiveRatingAdapter.kt +++ b/app/src/main/java/org/stepic/droid/adaptive/ui/adapters/AdaptiveRatingAdapter.kt @@ -10,8 +10,8 @@ import android.widget.TextView import androidx.core.content.ContextCompat import androidx.core.widget.ImageViewCompat import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.adaptive_rating_item.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.AdaptiveRatingItemBinding import org.stepic.droid.preferences.SharedPreferenceHelper import org.stepik.android.model.adaptive.RatingItem @@ -110,11 +110,12 @@ class AdaptiveRatingAdapter( } class RatingViewHolder(val root: View) : RecyclerView.ViewHolder(root) { - val icon: ImageView = root.icon - val rank: TextView = root.rank - val exp: TextView = root.exp - val name: TextView = root.name + private val binding = AdaptiveRatingItemBinding.bind(root) + val icon: ImageView = binding.icon + val rank: TextView = binding.rank + val exp: TextView = binding.exp + val name: TextView = binding.name } class SeparatorViewHolder(view: View) : RecyclerView.ViewHolder(view) -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepic/droid/adaptive/ui/adapters/AdaptiveWeeksAdapter.kt b/app/src/main/java/org/stepic/droid/adaptive/ui/adapters/AdaptiveWeeksAdapter.kt index 8dbf629d67..d9529cd251 100644 --- a/app/src/main/java/org/stepic/droid/adaptive/ui/adapters/AdaptiveWeeksAdapter.kt +++ b/app/src/main/java/org/stepic/droid/adaptive/ui/adapters/AdaptiveWeeksAdapter.kt @@ -10,10 +10,10 @@ import androidx.recyclerview.widget.RecyclerView import com.github.mikephil.charting.charts.LineChart import com.github.mikephil.charting.data.LineData import com.github.mikephil.charting.data.LineDataSet -import kotlinx.android.synthetic.main.adaptive_header_stats.view.* -import kotlinx.android.synthetic.main.adaptive_item_week.view.* import org.stepic.droid.R import org.stepic.droid.adaptive.model.AdaptiveWeekProgress +import org.stepic.droid.databinding.AdaptiveHeaderStatsBinding +import org.stepic.droid.databinding.AdaptiveItemWeekBinding import org.stepic.droid.util.defaultLocale import org.stepic.droid.util.resolveColorAttribute import java.util.ArrayList @@ -111,16 +111,18 @@ class AdaptiveWeeksAdapter : RecyclerView.Adapter() override fun injectComponent() { @@ -45,17 +46,17 @@ class AdaptiveRatingFragment: FragmentBase(), AdaptiveRatingView { val context = requireContext() super.onViewCreated(view, savedInstanceState) - recycler.layoutManager = LinearLayoutManager(context) + adaptiveRatingBinding.recycler.layoutManager = LinearLayoutManager(context) val divider = DividerItemDecoration(context, DividerItemDecoration.VERTICAL) divider.setDrawable(ContextCompat.getDrawable(context, R.drawable.bg_divider_vertical)!!) - recycler.addItemDecoration(divider) + adaptiveRatingBinding.recycler.addItemDecoration(divider) val spinnerAdapter = ArrayAdapter(context, R.layout.adaptive_item_rating_period, context.resources.getStringArray(R.array.adaptive_rating_periods)) spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) - spinner.adapter = spinnerAdapter + adaptiveRatingBinding.spinner.adapter = spinnerAdapter - spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + adaptiveRatingBinding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onNothingSelected(p0: AdapterView<*>?) {} override fun onItemSelected(p0: AdapterView<*>?, p1: View?, pos: Int, p3: Long) { @@ -63,39 +64,39 @@ class AdaptiveRatingFragment: FragmentBase(), AdaptiveRatingView { } } - tryAgain.setOnClickListener { adaptiveRatingPresenter.retry() } + adaptiveRatingBinding.errorNoConnectionWithButton.tryAgain.setOnClickListener { adaptiveRatingPresenter.retry() } } override fun onLoading() { - error.visibility = View.GONE - progress.visibility = View.VISIBLE - container.visibility = View.GONE + adaptiveRatingBinding.errorNoConnectionWithButton.error.visibility = View.GONE + adaptiveRatingBinding.progress.visibility = View.VISIBLE + adaptiveRatingBinding.container.visibility = View.GONE } private fun onError() { - error.visibility = View.VISIBLE - progress.visibility = View.GONE - container.visibility = View.GONE + adaptiveRatingBinding.errorNoConnectionWithButton.error.visibility = View.VISIBLE + adaptiveRatingBinding.progress.visibility = View.GONE + adaptiveRatingBinding.container.visibility = View.GONE } override fun onConnectivityError() { - errorMessage.setText(R.string.no_connection) + adaptiveRatingBinding.errorNoConnectionWithButton.errorMessage.setText(R.string.no_connection) onError() } override fun onRequestError() { - errorMessage.setText(R.string.request_error) + adaptiveRatingBinding.errorNoConnectionWithButton.errorMessage.setText(R.string.request_error) onError() } override fun onComplete() { - error.visibility = View.GONE - progress.visibility = View.GONE - container.visibility = View.VISIBLE + adaptiveRatingBinding.errorNoConnectionWithButton.error.visibility = View.GONE + adaptiveRatingBinding.progress.visibility = View.GONE + adaptiveRatingBinding.container.visibility = View.VISIBLE } override fun onRatingAdapter(adapter: AdaptiveRatingAdapter) { - recycler.adapter = adapter + adaptiveRatingBinding.recycler.adapter = adapter } override fun onStart() { @@ -117,4 +118,4 @@ class AdaptiveRatingFragment: FragmentBase(), AdaptiveRatingView { adaptiveRatingPresenter.destroy() super.onDestroy() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepic/droid/adaptive/ui/fragments/RecommendationsFragment.kt b/app/src/main/java/org/stepic/droid/adaptive/ui/fragments/RecommendationsFragment.kt index 22e3a6337b..57aadc566c 100644 --- a/app/src/main/java/org/stepic/droid/adaptive/ui/fragments/RecommendationsFragment.kt +++ b/app/src/main/java/org/stepic/droid/adaptive/ui/fragments/RecommendationsFragment.kt @@ -7,8 +7,7 @@ import android.view.ViewGroup import android.widget.PopupWindow import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat -import kotlinx.android.synthetic.main.error_no_connection_with_button.* -import kotlinx.android.synthetic.main.fragment_recommendations.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.adaptive.ui.adapters.QuizCardsAdapter import org.stepic.droid.adaptive.ui.animations.RecommendationsFragmentAnimations @@ -17,6 +16,7 @@ import org.stepic.droid.base.App import org.stepic.droid.base.FragmentBase import org.stepic.droid.core.presenters.RecommendationsPresenter import org.stepic.droid.core.presenters.contracts.RecommendationsView +import org.stepic.droid.databinding.FragmentRecommendationsBinding import org.stepic.droid.ui.util.PopupHelper import org.stepic.droid.util.AppConstants import org.stepic.droid.util.resolveColorAttribute @@ -46,6 +46,8 @@ class RecommendationsFragment : FragmentBase(), RecommendationsView { private lateinit var animations: RecommendationsFragmentAnimations + private val recommendationsBinding: FragmentRecommendationsBinding by viewBinding(FragmentRecommendationsBinding::bind) + private var course: Course? = null private val loadingPlaceholders by lazy { resources.getStringArray(R.array.recommendation_loading_placeholders) } @@ -71,21 +73,21 @@ class RecommendationsFragment : FragmentBase(), RecommendationsView { val context = requireContext() - animations = RecommendationsFragmentAnimations(streakSuccessContainer.context) + animations = RecommendationsFragmentAnimations(recommendationsBinding.streakSuccessContainer.context) - error.setBackgroundColor(0) + recommendationsBinding.errorNoConnectionWithButton.error.setBackgroundColor(0) - tryAgain.setOnClickListener { + recommendationsBinding.errorNoConnectionWithButton.tryAgain.setOnClickListener { recommendationsPresenter.retry() } (activity as? AppCompatActivity)?.let { - it.setSupportActionBar(toolbar) + it.setSupportActionBar(recommendationsBinding.toolbar) it.supportActionBar?.setDisplayHomeAsUpEnabled(true) it.supportActionBar?.setDisplayShowTitleEnabled(false) } - toolbar.setOnClickListener { + recommendationsBinding.toolbar.setOnClickListener { screenManager.showAdaptiveStats(context, course?.id ?: 0) expPopupWindow?.let { popup -> @@ -95,54 +97,54 @@ class RecommendationsFragment : FragmentBase(), RecommendationsView { } } - streakSuccessContainer.nestedTextView = streakSuccess - streakSuccessContainer.setGradientDrawableParams(streakSuccessContainer.context.resolveColorAttribute(R.attr.colorPrimary), 0f) + recommendationsBinding.streakSuccessContainer.nestedTextView = recommendationsBinding.streakSuccess + recommendationsBinding.streakSuccessContainer.setGradientDrawableParams(recommendationsBinding.streakSuccessContainer.context.resolveColorAttribute(R.attr.colorPrimary), 0f) } override fun onAdapter(cardsAdapter: QuizCardsAdapter) { - cardsContainer.setAdapter(cardsAdapter) + recommendationsBinding.cardsContainer.setAdapter(cardsAdapter) } override fun onLoading() { - progress.visibility = View.VISIBLE - error.visibility = View.GONE - loadingPlaceholder.text = loadingPlaceholders.random() + recommendationsBinding.progress.visibility = View.VISIBLE + recommendationsBinding.errorNoConnectionWithButton.error.visibility = View.GONE + recommendationsBinding.loadingPlaceholder.text = loadingPlaceholders.random() } override fun onCardLoaded() { - progress.visibility = View.GONE - cardsContainer.visibility = View.VISIBLE + recommendationsBinding.progress.visibility = View.GONE + recommendationsBinding.cardsContainer.visibility = View.VISIBLE } private fun onError() { - cardsContainer.visibility = View.GONE - error.visibility = View.VISIBLE - progress.visibility = View.GONE + recommendationsBinding.cardsContainer.visibility = View.GONE + recommendationsBinding.errorNoConnectionWithButton.error.visibility = View.VISIBLE + recommendationsBinding.progress.visibility = View.GONE } override fun onConnectivityError() { - errorMessage.setText(R.string.no_connection) + recommendationsBinding.errorNoConnectionWithButton.errorMessage.setText(R.string.no_connection) onError() } override fun onRequestError() { - errorMessage.setText(R.string.request_error) + recommendationsBinding.errorNoConnectionWithButton.errorMessage.setText(R.string.request_error) onError() } private fun onCourseState() { - cardsContainer.visibility = View.GONE - progress.visibility = View.GONE - courseState.visibility = View.VISIBLE + recommendationsBinding.cardsContainer.visibility = View.GONE + recommendationsBinding.progress.visibility = View.GONE + recommendationsBinding.courseState.visibility = View.VISIBLE } override fun onCourseCompleted() { - courseStateText.setText(R.string.adaptive_course_completed) + recommendationsBinding.courseStateText.setText(R.string.adaptive_course_completed) onCourseState() } override fun onCourseNotSupported() { - courseStateText.setText(R.string.adaptive_course_not_supported) + recommendationsBinding.courseStateText.setText(R.string.adaptive_course_not_supported) onCourseState() } @@ -153,37 +155,37 @@ class RecommendationsFragment : FragmentBase(), RecommendationsView { level: Long ) { - expProgress.progress = (exp - currentLevelExp).toInt() - expProgress.max = (nextLevelExp - currentLevelExp).toInt() + recommendationsBinding.expProgress.progress = (exp - currentLevelExp).toInt() + recommendationsBinding.expProgress.max = (nextLevelExp - currentLevelExp).toInt() - expCounter.text = formatExp(exp) - expLevel.text = getString(R.string.adaptive_exp_title, level) - expLevelNext.text = getString(R.string.adaptive_exp_subtitle, formatExp(nextLevelExp - exp)) + recommendationsBinding.expCounter.text = formatExp(exp) + recommendationsBinding.expLevel.text = getString(R.string.adaptive_exp_title, level) + recommendationsBinding.expLevelNext.text = getString(R.string.adaptive_exp_subtitle, formatExp(nextLevelExp - exp)) } override fun onStreak(streak: Long) { - expInc.text = getString(R.string.adaptive_exp_inc, streak) - streakSuccess.text = resources.getQuantityString(R.plurals.adaptive_streak_success, streak.toInt(), streak) + recommendationsBinding.expInc.text = getString(R.string.adaptive_exp_inc, streak) + recommendationsBinding.streakSuccess.text = resources.getQuantityString(R.plurals.adaptive_streak_success, streak.toInt(), streak) if (streak > 1) { animations.playStreakSuccessAnimationSequence( - root = rootView, - streakSuccessContainer = streakSuccessContainer, - expProgress = expProgress, - expInc = expInc, - expBubble = expBubble + root = recommendationsBinding.rootView, + streakSuccessContainer = recommendationsBinding.streakSuccessContainer, + expProgress = recommendationsBinding.expProgress, + expInc = recommendationsBinding.expInc, + expBubble = recommendationsBinding.expBubble ) } else { - animations.playStreakBubbleAnimation(expInc) + animations.playStreakBubbleAnimation(recommendationsBinding.expInc) } } override fun showExpTooltip() { - expPopupWindow = PopupHelper.showPopupAnchoredToView(requireContext(), expBubble, getString(R.string.adaptive_exp_tooltip_text), withArrow = true) + expPopupWindow = PopupHelper.showPopupAnchoredToView(requireContext(), recommendationsBinding.expBubble, getString(R.string.adaptive_exp_tooltip_text), withArrow = true) } override fun onStreakLost() { - animations.playStreakFailedAnimation(streakFailed, expProgress) + animations.playStreakFailedAnimation(recommendationsBinding.streakFailed, recommendationsBinding.expProgress) } override fun showNewLevelDialog(level: Long) { @@ -211,4 +213,4 @@ class RecommendationsFragment : FragmentBase(), RecommendationsView { recommendationsPresenter.destroy() super.onDestroy() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepic/droid/features/stories/ui/adapter/StoriesAdapter.kt b/app/src/main/java/org/stepic/droid/features/stories/ui/adapter/StoriesAdapter.kt index 2da7a29263..c5e724e7f1 100644 --- a/app/src/main/java/org/stepic/droid/features/stories/ui/adapter/StoriesAdapter.kt +++ b/app/src/main/java/org/stepic/droid/features/stories/ui/adapter/StoriesAdapter.kt @@ -8,8 +8,8 @@ import androidx.core.view.isInvisible import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide import com.google.android.material.imageview.ShapeableImageView -import kotlinx.android.synthetic.main.view_story_item.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.ViewStoryItemBinding import ru.nobird.android.stories.model.Story import kotlin.properties.Delegates @@ -66,9 +66,10 @@ class StoriesAdapter( } inner class StoryViewHolder(root: View) : RecyclerView.ViewHolder(root) { - val cover: ShapeableImageView = root.storyCover - private val title = root.storyTitle - private val activeStoryMarker = root.activeStoryMarker + private val binding = ViewStoryItemBinding.bind(root) + val cover: ShapeableImageView = binding.storyCover + private val title = binding.storyTitle + private val activeStoryMarker = binding.activeStoryMarker init { root.setOnClickListener { @@ -93,4 +94,4 @@ class StoriesAdapter( itemView.isInvisible = position == selected } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepic/droid/features/stories/ui/delegate/FeedbackStoryPartDelegate.kt b/app/src/main/java/org/stepic/droid/features/stories/ui/delegate/FeedbackStoryPartDelegate.kt index 807c1d1f78..d329e39869 100644 --- a/app/src/main/java/org/stepic/droid/features/stories/ui/delegate/FeedbackStoryPartDelegate.kt +++ b/app/src/main/java/org/stepic/droid/features/stories/ui/delegate/FeedbackStoryPartDelegate.kt @@ -16,11 +16,10 @@ import androidx.core.view.isVisible import androidx.core.widget.TextViewCompat import androidx.swiperefreshlayout.widget.CircularProgressDrawable import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.view_story_feedback.view.* -import kotlinx.android.synthetic.main.view_story_text_input.view.* import org.stepic.droid.R import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.analytic.Analytic +import org.stepic.droid.databinding.ViewStoryFeedbackBinding import org.stepic.droid.features.stories.model.FeedbackStoryPart import org.stepic.droid.ui.util.setOnKeyboardOpenListener import org.stepik.android.model.StoryTemplate @@ -50,42 +49,42 @@ class FeedbackStoryPartDelegate( override fun isForViewType(part: StoryPart): Boolean = part is FeedbackStoryPart - override fun onBindView(storyView: StoryView, container: ViewGroup, position: Int, part: StoryPart): View = - container.inflate(R.layout.view_story_feedback, false).apply { - part as FeedbackStoryPart - (context as? AppCompatActivity)?.currentFocus?.clearFocus() - - Glide.with(context) - .load(part.cover) - .placeholder(progressDrawable) - .into(this.storyCover) - - val story = storyView.adapter?.story - if (story != null) { - analytic.reportAmplitudeEvent( - AmplitudeAnalytic.Stories.STORY_PART_OPENED, mapOf( - AmplitudeAnalytic.Stories.Values.STORY_ID to story.id, - AmplitudeAnalytic.Stories.Values.POSITION to position - )) - } - - setUpText(this, part.text) - setUpButton(story, this, part.button, position) - setUpInput(this, storyView, part.feedback) + override fun onBindView(storyView: StoryView, container: ViewGroup, position: Int, part: StoryPart): View { + val binding = ViewStoryFeedbackBinding.inflate(LayoutInflater.from(context), container, false) + part as FeedbackStoryPart + (context as? AppCompatActivity)?.currentFocus?.clearFocus() + + Glide.with(context) + .load(part.cover) + .placeholder(progressDrawable) + .into(binding.storyCover) + + val story = storyView.adapter?.story + if (story != null) { + analytic.reportAmplitudeEvent( + AmplitudeAnalytic.Stories.STORY_PART_OPENED, mapOf( + AmplitudeAnalytic.Stories.Values.STORY_ID to story.id, + AmplitudeAnalytic.Stories.Values.POSITION to position + )) } - private fun setUpText(view: View, text: StoryTemplate.Text?) { + setUpText(binding, part.text) + setUpButton(story, binding, part.button, position) + setUpInput(binding, storyView, part.feedback) + return binding.root + } + + private fun setUpText(binding: ViewStoryFeedbackBinding, text: StoryTemplate.Text?) { if (text != null) { - val storyTitle = view.storyTitle @ColorInt val textColor = getColorInt(text.textColor) - storyTitle.setTextColor(textColor) - storyTitle.text = text.title + binding.storyTitle.setTextColor(textColor) + binding.storyTitle.text = text.title } } - private fun setUpButton(story: Story?, view: View, button: StoryTemplate.Button?, position: Int) { - val storyButton = view.storyButton - val storyFeedbackEditText = view.storyFeedbackEditText + private fun setUpButton(story: Story?, binding: ViewStoryFeedbackBinding, button: StoryTemplate.Button?, position: Int) { + val storyButton = binding.storyButton + val storyFeedbackEditText = binding.storyInputContainer.storyFeedbackEditText if (button != null) { ViewCompat.setBackgroundTintList(storyButton, ColorStateList.valueOf(getColorInt(button.backgroundColor))) storyButton.setTextColor(getColorInt(button.textColor)) @@ -114,15 +113,15 @@ class FeedbackStoryPartDelegate( } } - private fun setUpInput(view: View, storyView: StoryView, feedback: StoryTemplate.Feedback?) { + private fun setUpInput(binding: ViewStoryFeedbackBinding, storyView: StoryView, feedback: StoryTemplate.Feedback?) { if (feedback == null) return - val title = view.storyTitle - val storyFeedbackContainer = view.storyInputContainer - val storyFeedbackText = view.storyFeedbackText - val storyFeedbackIcon = view.storyFeedbackIcon - val storyFeedbackEditText = view.storyFeedbackEditText + val title = binding.storyTitle + val storyInputBinding = binding.storyInputContainer + val storyFeedbackText = storyInputBinding.storyFeedbackText + val storyFeedbackIcon = storyInputBinding.storyFeedbackIcon + val storyFeedbackEditText = storyInputBinding.storyFeedbackEditText - storyFeedbackContainer.background = getColoredDrawable(R.drawable.bg_shape_rounded, feedback.backgroundColor) + storyInputBinding.root.background = getColoredDrawable(R.drawable.bg_shape_rounded, feedback.backgroundColor) storyFeedbackText.text = feedback.text storyFeedbackText.setTextColor(getColorInt(feedback.textColor)) @@ -140,9 +139,9 @@ class FeedbackStoryPartDelegate( storyFeedbackEditText.setHintTextColor(getColorInt(feedback.placeholderTextColor)) storyFeedbackEditText.setOnFocusChangeListener { _, hasFocus -> - view.isFocusableInTouchMode = hasFocus - view.isFocusable = hasFocus - view.isClickable = hasFocus + binding.root.isFocusableInTouchMode = hasFocus + binding.root.isFocusable = hasFocus + binding.root.isClickable = hasFocus storyFeedbackEditText.post { dismissableLayout.isFocusable = !hasFocus dismissableLayout.isFocusableInTouchMode = !hasFocus @@ -181,4 +180,4 @@ class FeedbackStoryPartDelegate( */ private fun getColorInt(color: String): Int = "#$color".toColorInt() -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepic/droid/features/stories/ui/delegate/PlainTextWithButtonStoryPartDelegate.kt b/app/src/main/java/org/stepic/droid/features/stories/ui/delegate/PlainTextWithButtonStoryPartDelegate.kt index b9fb91869a..9c42e63f06 100644 --- a/app/src/main/java/org/stepic/droid/features/stories/ui/delegate/PlainTextWithButtonStoryPartDelegate.kt +++ b/app/src/main/java/org/stepic/droid/features/stories/ui/delegate/PlainTextWithButtonStoryPartDelegate.kt @@ -3,6 +3,7 @@ package org.stepic.droid.features.stories.ui.delegate import android.content.Context import android.content.res.ColorStateList import android.net.Uri +import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.annotation.ColorInt @@ -11,10 +12,10 @@ import androidx.core.view.ViewCompat import androidx.core.view.isVisible import androidx.swiperefreshlayout.widget.CircularProgressDrawable import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.view_story_plain_text_with_button.view.* import org.stepic.droid.R import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.analytic.Analytic +import org.stepic.droid.databinding.ViewStoryPlainTextWithButtonBinding import org.stepic.droid.features.stories.model.PlainTextWithButtonStoryPart import org.stepik.android.domain.story.model.StoryReaction import org.stepik.android.model.StoryTemplate @@ -23,7 +24,6 @@ import ru.nobird.android.stories.model.Story import ru.nobird.android.stories.model.StoryPart import ru.nobird.android.stories.ui.custom.StoryView import ru.nobird.android.stories.ui.delegate.StoryPartViewDelegate -import ru.nobird.android.view.base.ui.extension.inflate class PlainTextWithButtonStoryPartDelegate( private val analytic: Analytic, @@ -47,47 +47,45 @@ class PlainTextWithButtonStoryPartDelegate( override fun isForViewType(part: StoryPart): Boolean = part is PlainTextWithButtonStoryPart - override fun onBindView(storyView: StoryView, container: ViewGroup, position: Int, part: StoryPart): View = - container.inflate(R.layout.view_story_plain_text_with_button, false).apply { - part as PlainTextWithButtonStoryPart - (context as? AppCompatActivity)?.currentFocus?.clearFocus() - - Glide.with(context) - .load(part.cover) - .placeholder(progressDrawable) - .into(this.storyCover) - - val story = storyView.adapter?.story - if (story != null) { - analytic.reportAmplitudeEvent(AmplitudeAnalytic.Stories.STORY_PART_OPENED, mapOf( - AmplitudeAnalytic.Stories.Values.STORY_ID to story.id, - AmplitudeAnalytic.Stories.Values.POSITION to position - )) - } - - setUpText(this, part.text) - setUpButton(story, this, part.button, position) - setUpReactions(story, this, position) + override fun onBindView(storyView: StoryView, container: ViewGroup, position: Int, part: StoryPart): View { + val binding = ViewStoryPlainTextWithButtonBinding.inflate(LayoutInflater.from(context), container, false) + part as PlainTextWithButtonStoryPart + (context as? AppCompatActivity)?.currentFocus?.clearFocus() + + Glide.with(context) + .load(part.cover) + .placeholder(progressDrawable) + .into(binding.storyCover) + + val story = storyView.adapter?.story + if (story != null) { + analytic.reportAmplitudeEvent(AmplitudeAnalytic.Stories.STORY_PART_OPENED, mapOf( + AmplitudeAnalytic.Stories.Values.STORY_ID to story.id, + AmplitudeAnalytic.Stories.Values.POSITION to position + )) } - private fun setUpText(view: View, text: StoryTemplate.Text?) { - if (text != null) { - val storyTitle = view.storyTitle - val storyText = view.storyText + setUpText(binding, part.text) + setUpButton(story, binding, part.button, position) + setUpReactions(story, binding, position) + return binding.root + } + private fun setUpText(binding: ViewStoryPlainTextWithButtonBinding, text: StoryTemplate.Text?) { + if (text != null) { @ColorInt val textColor = COLOR_MASK or text.textColor.toInt(16) - storyTitle.setTextColor(textColor) - storyText.setTextColor(textColor) + binding.storyTitle.setTextColor(textColor) + binding.storyText.setTextColor(textColor) - storyTitle.text = text.title - storyText.text = text.text - storyText.isVisible = text.text?.isNotBlank() ?: false + binding.storyTitle.text = text.title + binding.storyText.text = text.text + binding.storyText.isVisible = text.text?.isNotBlank() ?: false } } - private fun setUpButton(story: Story?, view: View, button: StoryTemplate.Button?, position: Int) { - val storyButton = view.storyButton + private fun setUpButton(story: Story?, binding: ViewStoryPlainTextWithButtonBinding, button: StoryTemplate.Button?, position: Int) { + val storyButton = binding.storyButton if (button != null) { ViewCompat.setBackgroundTintList(storyButton, ColorStateList.valueOf(COLOR_MASK or button.backgroundColor.toInt(16))) storyButton.setTextColor(COLOR_MASK or button.textColor.toInt(16)) @@ -111,18 +109,18 @@ class PlainTextWithButtonStoryPartDelegate( } } - fun setUpReactions(story: Story?, view: View, position: Int) { + fun setUpReactions(story: Story?, binding: ViewStoryPlainTextWithButtonBinding, position: Int) { val storyId = story?.id ?: 0 val vote = storyReactions[storyId] - with(view.storyReactionLike) { + with(binding.storyReactionLike) { setOnClickListener { val id = story?.id ?: return@setOnClickListener storyReactionListener.invoke(id, position, StoryReaction.LIKE) } isActivated = vote == StoryReaction.LIKE } - with(view.storyReactionDislike) { + with(binding.storyReactionDislike) { setOnClickListener { val id = story?.id ?: return@setOnClickListener storyReactionListener.invoke(id, position, StoryReaction.DISLIKE) @@ -130,4 +128,4 @@ class PlainTextWithButtonStoryPartDelegate( isActivated = vote == StoryReaction.DISLIKE } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepic/droid/features/stories/ui/delegate/StoriesActivityDelegate.kt b/app/src/main/java/org/stepic/droid/features/stories/ui/delegate/StoriesActivityDelegate.kt index 5b8c9063a4..1a53d6a527 100644 --- a/app/src/main/java/org/stepic/droid/features/stories/ui/delegate/StoriesActivityDelegate.kt +++ b/app/src/main/java/org/stepic/droid/features/stories/ui/delegate/StoriesActivityDelegate.kt @@ -4,10 +4,11 @@ import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.core.view.children import androidx.viewpager.widget.ViewPager -import kotlinx.android.synthetic.main.activity_stories.* import org.stepic.droid.R import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.analytic.Analytic +import org.stepic.droid.databinding.ActivityStoriesBinding +import org.stepic.droid.databinding.ViewStoryPlainTextWithButtonBinding import org.stepik.android.domain.story.model.StoryReaction import ru.nobird.app.core.model.safeCast import ru.nobird.android.stories.model.Story @@ -22,15 +23,17 @@ class StoriesActivityDelegate( private val analytic: Analytic, storyReactionListener: (storyId: Long, storyPosition: Int, storyReaction: StoryReaction) -> Unit ) : StoriesActivityDelegateBase(activity) { + private val activityBinding = ActivityStoriesBinding.bind(activity.findViewById(R.id.content)) + private val storyReactions = mutableMapOf() private val storyPartDelegate = PlainTextWithButtonStoryPartDelegate(analytic, activity, storyReactions, storyReactionListener) public override val dismissableLayout: DismissableLayout = - activity.content + activityBinding.content public override val storiesViewPager: ViewPager = - activity.storiesPager + activityBinding.storiesPager override val arguments: Bundle = activity.intent.extras ?: Bundle.EMPTY @@ -76,8 +79,9 @@ class StoriesActivityDelegate( ?.findViewById(R.id.storyViewPager) storyPartPager?.children?.forEach { view -> - storyPartDelegate.setUpReactions(story, view, position) + val binding = ViewStoryPlainTextWithButtonBinding.bind(view) + storyPartDelegate.setUpReactions(story, binding, position) } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepic/droid/model/CertificateListItem.kt b/app/src/main/java/org/stepic/droid/model/CertificateListItem.kt index 799e065bf6..fd7ac179ff 100644 --- a/app/src/main/java/org/stepic/droid/model/CertificateListItem.kt +++ b/app/src/main/java/org/stepic/droid/model/CertificateListItem.kt @@ -1,7 +1,7 @@ package org.stepic.droid.model import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import org.stepik.android.model.Certificate sealed class CertificateListItem : Parcelable { diff --git a/app/src/main/java/org/stepic/droid/persistence/model/StepPersistentWrapper.kt b/app/src/main/java/org/stepic/droid/persistence/model/StepPersistentWrapper.kt index ededaf8bfb..979569097c 100644 --- a/app/src/main/java/org/stepic/droid/persistence/model/StepPersistentWrapper.kt +++ b/app/src/main/java/org/stepic/droid/persistence/model/StepPersistentWrapper.kt @@ -1,8 +1,8 @@ package org.stepic.droid.persistence.model import android.os.Parcelable -import kotlinx.android.parcel.IgnoredOnParcel -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.IgnoredOnParcel +import kotlinx.parcelize.Parcelize import org.stepic.droid.util.AppConstants import org.stepik.android.model.Progressable import org.stepik.android.model.Step diff --git a/app/src/main/java/org/stepic/droid/ui/activities/AnimatedOnboardingActivity.kt b/app/src/main/java/org/stepic/droid/ui/activities/AnimatedOnboardingActivity.kt index 462166dc8d..a25c85ac48 100644 --- a/app/src/main/java/org/stepic/droid/ui/activities/AnimatedOnboardingActivity.kt +++ b/app/src/main/java/org/stepic/droid/ui/activities/AnimatedOnboardingActivity.kt @@ -3,7 +3,7 @@ package org.stepic.droid.ui.activities import android.content.pm.ActivityInfo import android.os.Bundle import androidx.viewpager.widget.ViewPager -import kotlinx.android.synthetic.main.activity_onboarding.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.analytic.Analytic @@ -11,6 +11,7 @@ import org.stepic.droid.analytic.experiments.DeferredAuthSplitTest import org.stepic.droid.analytic.experiments.OnboardingSplitTestVersion2 import org.stepic.droid.base.App import org.stepic.droid.base.FragmentActivityBase +import org.stepic.droid.databinding.ActivityOnboardingBinding import org.stepic.droid.ui.activities.contracts.OnNextClickedListener import org.stepic.droid.ui.adapters.OnboardingAdapter import org.stepic.droid.ui.custom.OnboardingPageTransformer @@ -18,6 +19,7 @@ import org.stepic.droid.ui.fragments.OnboardingFragment import javax.inject.Inject class AnimatedOnboardingActivity : FragmentActivityBase(), OnNextClickedListener { + private val onboardingBinding: ActivityOnboardingBinding by viewBinding(ActivityOnboardingBinding::bind) @Inject lateinit var deferredAuthSplitsTest: DeferredAuthSplitTest @@ -33,10 +35,10 @@ class AnimatedOnboardingActivity : FragmentActivityBase(), OnNextClickedListener private fun initViewPager() { val onboardingAdapter = OnboardingAdapter(supportFragmentManager) - onboardingViewPager.adapter = onboardingAdapter - onboardingViewPager.offscreenPageLimit = onboardingAdapter.count + onboardingBinding.onboardingViewPager.adapter = onboardingAdapter + onboardingBinding.onboardingViewPager.offscreenPageLimit = onboardingAdapter.count - onboardingCircleIndicator.setViewPager(onboardingViewPager) + onboardingBinding.onboardingCircleIndicator.setViewPager(onboardingBinding.onboardingViewPager) val pageChangeListener: ViewPager.OnPageChangeListener = object : ViewPager.OnPageChangeListener { override fun onPageScrollStateChanged(state: Int) {} @@ -47,11 +49,11 @@ class AnimatedOnboardingActivity : FragmentActivityBase(), OnNextClickedListener reportToAmplitude(AmplitudeAnalytic.Onboarding.SCREEN_OPENED) } } - onboardingViewPager.addOnPageChangeListener(pageChangeListener) - onboardingViewPager.setPageTransformer(false, OnboardingPageTransformer()) + onboardingBinding.onboardingViewPager.addOnPageChangeListener(pageChangeListener) + onboardingBinding.onboardingViewPager.setPageTransformer(false, OnboardingPageTransformer()) //we should post animation on next frame - onboardingViewPager.post { pageChangeListener.onPageSelected(onboardingViewPager.currentItem) } + onboardingBinding.onboardingViewPager.post { pageChangeListener.onPageSelected(onboardingBinding.onboardingViewPager.currentItem) } } private fun invokeAnimationOnFragment(position: Int) { @@ -61,8 +63,8 @@ class AnimatedOnboardingActivity : FragmentActivityBase(), OnNextClickedListener } private fun initClose() { - closeOnboarding.bringToFront() - closeOnboarding.setOnClickListener { + onboardingBinding.closeOnboarding.bringToFront() + onboardingBinding.closeOnboarding.setOnClickListener { onboardingClosed() } } @@ -73,10 +75,10 @@ class AnimatedOnboardingActivity : FragmentActivityBase(), OnNextClickedListener } override fun onNextClicked() { - val current = onboardingViewPager.currentItem + val current = onboardingBinding.onboardingViewPager.currentItem val next = current + 1 - if (next < onboardingViewPager.adapter!!.count) { - onboardingViewPager.setCurrentItem(next, true) + if (next < onboardingBinding.onboardingViewPager.adapter!!.count) { + onboardingBinding.onboardingViewPager.setCurrentItem(next, true) } else { onboardingComplete() } @@ -84,7 +86,7 @@ class AnimatedOnboardingActivity : FragmentActivityBase(), OnNextClickedListener private fun reportToAmplitude(eventName: String) { - val analyticPosition = onboardingViewPager.currentItem + 1 + val analyticPosition = onboardingBinding.onboardingViewPager.currentItem + 1 analytic.reportAmplitudeEvent(eventName, mapOf(AmplitudeAnalytic.Onboarding.PARAM_SCREEN to analyticPosition)) } @@ -112,11 +114,11 @@ class AnimatedOnboardingActivity : FragmentActivityBase(), OnNextClickedListener } } - private fun isFirstItem() = onboardingViewPager.currentItem == 0 + private fun isFirstItem() = onboardingBinding.onboardingViewPager.currentItem == 0 private fun showPreviousSlide() { - val previous = onboardingViewPager.currentItem - 1 - onboardingViewPager.setCurrentItem(previous, true) + val previous = onboardingBinding.onboardingViewPager.currentItem - 1 + onboardingBinding.onboardingViewPager.setCurrentItem(previous, true) } override fun applyTransitionPrev() { diff --git a/app/src/main/java/org/stepic/droid/ui/activities/MainFeedActivity.kt b/app/src/main/java/org/stepic/droid/ui/activities/MainFeedActivity.kt index e9fff74f19..d1467b03b7 100644 --- a/app/src/main/java/org/stepic/droid/ui/activities/MainFeedActivity.kt +++ b/app/src/main/java/org/stepic/droid/ui/activities/MainFeedActivity.kt @@ -14,9 +14,10 @@ import androidx.annotation.IdRes import androidx.annotation.RequiresApi import androidx.core.app.ActivityCompat import androidx.fragment.app.Fragment +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.firebase.remoteconfig.FirebaseRemoteConfig -import kotlinx.android.synthetic.main.activity_main_feed.* +import org.stepic.droid.databinding.ActivityMainFeedBinding import org.stepic.droid.BuildConfig import org.stepic.droid.R import org.stepic.droid.analytic.Analytic @@ -70,6 +71,8 @@ class MainFeedActivity : BackToExitActivityWithSmartLockBase(), StreakNotificationDialogFragment.Callback, TimeIntervalPickerDialogFragment.Companion.Callback { + private val mainFeedBinding: ActivityMainFeedBinding by viewBinding(ActivityMainFeedBinding::bind) + companion object { const val CURRENT_INDEX_KEY = "currentIndexKey" @@ -236,7 +239,7 @@ class MainFeedActivity : BackToExitActivityWithSmartLockBase(), when (getFragmentIndexFromIntent(launchIntent)) { HOME_INDEX -> { - navigationView.selectedItemId = R.id.home + mainFeedBinding.navigationView.selectedItemId = R.id.home val storyId = launchIntent?.getStoryId() if (storyId != null) { StoryDeepLinkDialogFragment @@ -245,7 +248,7 @@ class MainFeedActivity : BackToExitActivityWithSmartLockBase(), } } CATALOG_INDEX -> { - navigationView.selectedItemId = R.id.catalog + mainFeedBinding.navigationView.selectedItemId = R.id.catalog launchIntent?.data?.pathSegments?.let { when { it.contains(CATALOG_DEEPLINK) -> { @@ -268,9 +271,9 @@ class MainFeedActivity : BackToExitActivityWithSmartLockBase(), } } } - PROFILE_INDEX -> navigationView.selectedItemId = R.id.profile - NOTIFICATIONS_INDEX -> navigationView.selectedItemId = R.id.notifications - DEBUG_INDEX -> navigationView.selectedItemId = R.id.debug + PROFILE_INDEX -> mainFeedBinding.navigationView.selectedItemId = R.id.profile + NOTIFICATIONS_INDEX -> mainFeedBinding.navigationView.selectedItemId = R.id.notifications + DEBUG_INDEX -> mainFeedBinding.navigationView.selectedItemId = R.id.debug else -> { //do nothing } @@ -299,9 +302,9 @@ class MainFeedActivity : BackToExitActivityWithSmartLockBase(), } private fun initNavigation() { - navigationView.setOnNavigationItemSelectedListener(::onNavigationItemSelected) - navigationView.setOnNavigationItemReselectedListener(::onNavigationItemReselected) - navigationView.menu.findItem(R.id.debug).isVisible = BuildConfig.BUILD_TYPE == DEBUG_BUILD_TYPE || BuildConfig.BUILD_TYPE == STAGE_DEBUGGABLE_BUILD_TYPE + mainFeedBinding.navigationView.setOnNavigationItemSelectedListener(::onNavigationItemSelected) + mainFeedBinding.navigationView.setOnNavigationItemReselectedListener(::onNavigationItemReselected) + mainFeedBinding.navigationView.menu.findItem(R.id.debug).isVisible = BuildConfig.BUILD_TYPE == DEBUG_BUILD_TYPE || BuildConfig.BUILD_TYPE == STAGE_DEBUGGABLE_BUILD_TYPE } private fun showCurrentFragment(@IdRes id: Int) { @@ -319,11 +322,11 @@ class MainFeedActivity : BackToExitActivityWithSmartLockBase(), } override fun onBackPressed() { - if (navigationView.selectedItemId == R.id.home) { + if (mainFeedBinding.navigationView.selectedItemId == R.id.home) { finish() return } else { - navigationView.selectedItemId = R.id.home + mainFeedBinding.navigationView.selectedItemId = R.id.home } } @@ -413,8 +416,8 @@ class MainFeedActivity : BackToExitActivityWithSmartLockBase(), //RootScreen methods override fun showCatalog() { - if (navigationView.selectedItemId != R.id.catalog) { - navigationView.selectedItemId = R.id.catalog + if (mainFeedBinding.navigationView.selectedItemId != R.id.catalog) { + mainFeedBinding.navigationView.selectedItemId = R.id.catalog } } @@ -445,11 +448,11 @@ class MainFeedActivity : BackToExitActivityWithSmartLockBase(), } override fun onBadgeShouldBeHidden() { - navigationView.removeBadge(R.id.notifications) + mainFeedBinding.navigationView.removeBadge(R.id.notifications) } override fun onBadgeCountChanged(count: Int) { - val badge = navigationView.getOrCreateBadge(R.id.notifications) + val badge = mainFeedBinding.navigationView.getOrCreateBadge(R.id.notifications) badge.number = count badge.maxCharacterCount = 3 badge.isVisible = true @@ -479,7 +482,7 @@ class MainFeedActivity : BackToExitActivityWithSmartLockBase(), if (deniedPermissionIndex != -1) { if (!ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[deniedPermissionIndex])) { - frame.snackbar(messageRes = R.string.notification_permission_error) + mainFeedBinding.frame.snackbar(messageRes = R.string.notification_permission_error) } } else { TimeIntervalPickerDialogFragment diff --git a/app/src/main/java/org/stepic/droid/ui/activities/PhotoViewActivity.kt b/app/src/main/java/org/stepic/droid/ui/activities/PhotoViewActivity.kt index d3c00edbf9..d9a7487c8e 100644 --- a/app/src/main/java/org/stepic/droid/ui/activities/PhotoViewActivity.kt +++ b/app/src/main/java/org/stepic/droid/ui/activities/PhotoViewActivity.kt @@ -6,12 +6,13 @@ import android.graphics.drawable.PictureDrawable import android.os.Bundle import android.view.MenuItem import androidx.core.view.isVisible +import by.kirich1409.viewbindingdelegate.viewBinding import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.transition.Transition import com.github.chrisbanes.photoview.PhotoViewAttacher -import kotlinx.android.synthetic.main.fragment_photo_view.* import org.stepic.droid.R import org.stepic.droid.base.FragmentActivityBase +import org.stepic.droid.databinding.FragmentPhotoViewBinding import org.stepik.android.view.glide.model.GlideRequestFactory import kotlin.math.abs import kotlin.math.min @@ -21,14 +22,15 @@ class PhotoViewActivity : FragmentActivityBase() { const val EXTRA_PATH = "extra_path" } + private val photoViewBinding: FragmentPhotoViewBinding by viewBinding(FragmentPhotoViewBinding::bind) private lateinit var photoViewAttacher: PhotoViewAttacher private var screenHeight: Int = 0 private var dismissPathLength: Int = 0 private val target = object : CustomTarget() { override fun onResourceReady(resource: PictureDrawable, transition: Transition?) { - internetProblemRootView.isVisible = false - zoomableImageView.setImageDrawable(resource) + photoViewBinding.internetProblemRootView.isVisible = false + photoViewBinding.zoomableImageView.setImageDrawable(resource) photoViewAttacher.update() // Timber.d("resource ready") } @@ -38,7 +40,7 @@ class PhotoViewActivity : FragmentActivityBase() { } override fun onLoadFailed(errorDrawable: Drawable?) { - internetProblemRootView.isVisible = true + photoViewBinding.internetProblemRootView.isVisible = true } } @@ -46,34 +48,34 @@ class PhotoViewActivity : FragmentActivityBase() { super.onCreate(savedInstanceState) setContentView(R.layout.fragment_photo_view) setUpToolbar() - photoViewAttacher = PhotoViewAttacher(zoomableImageView) + photoViewAttacher = PhotoViewAttacher(photoViewBinding.zoomableImageView) screenHeight = Resources.getSystem().displayMetrics.heightPixels dismissPathLength = resources.getDimensionPixelSize(R.dimen.dismiss_path_length) - verticalDragLayout.setOnDragListener { dy -> + photoViewBinding.verticalDragLayout.setOnDragListener { dy -> if (photoViewAttacher.scale > 1f) return@setOnDragListener val alpha = 1 - min(abs(dy / (3 * dismissPathLength)), 1f) - backgroundColorView.alpha = alpha - toolbar.alpha = alpha - zoomableImageView.translationY = -dy + photoViewBinding.backgroundColorView.alpha = alpha + photoViewBinding.toolbar.alpha = alpha + photoViewBinding.zoomableImageView.translationY = -dy } - verticalDragLayout.setOnReleaseDragListener { dy -> + photoViewBinding.verticalDragLayout.setOnReleaseDragListener { dy -> if (photoViewAttacher.scale > 1f) return@setOnReleaseDragListener if (abs(dy) > dismissPathLength) { - zoomableImageView.isVisible = false + photoViewBinding.zoomableImageView.isVisible = false finish() } else { - backgroundColorView.alpha = 1f - toolbar.alpha = 1f - zoomableImageView.translationY = 0f + photoViewBinding.backgroundColorView.alpha = 1f + photoViewBinding.toolbar.alpha = 1f + photoViewBinding.zoomableImageView.translationY = 0f } } val url = requireNotNull(intent.getStringExtra(EXTRA_PATH)) - retryButton.setOnClickListener { - internetProblemRootView.isVisible = false + photoViewBinding.retryButton.setOnClickListener { + photoViewBinding.internetProblemRootView.isVisible = false loadImage(url) } loadImage(url) @@ -103,7 +105,7 @@ class PhotoViewActivity : FragmentActivityBase() { } private fun setUpToolbar() { - setSupportActionBar(toolbar) + setSupportActionBar(photoViewBinding.toolbar) val supportActionBar = supportActionBar if (supportActionBar != null) { diff --git a/app/src/main/java/org/stepic/droid/ui/adapters/SearchQueriesAdapter.kt b/app/src/main/java/org/stepic/droid/ui/adapters/SearchQueriesAdapter.kt index 207cc4ffe0..3128f3be5c 100644 --- a/app/src/main/java/org/stepic/droid/ui/adapters/SearchQueriesAdapter.kt +++ b/app/src/main/java/org/stepic/droid/ui/adapters/SearchQueriesAdapter.kt @@ -10,10 +10,10 @@ import android.widget.ImageView import android.widget.TextView import androidx.appcompat.widget.SearchView import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.search_query_item.view.* import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App +import org.stepic.droid.databinding.SearchQueryItemBinding import org.stepic.droid.model.SearchQuery import org.stepic.droid.model.SearchQuerySource import org.stepic.droid.ui.custom.AutoCompleteSearchView @@ -58,8 +58,8 @@ class SearchQueriesAdapter(context: Context) : RecyclerView.Adapter - val binding = ItemBannerBinding.inflate(layoutInflater, homeMainContainer, false) + val binding = ItemBannerBinding.inflate(layoutInflater, homeBinding.homeMainContainer, false) binding.root.setOnClickListener { // TODO // Probably better to move into ViewTreeObserver @@ -186,10 +187,10 @@ class HomeFragment : FragmentBase(), HomeStreakView, FastContinueNewHomeFragment binding.bind(banner, bannerResourcesMapper) - val insertionIndex = min(banner.position + offset, homeMainContainer.childCount) - val previousFragment = homeMainContainer.getChildAt(insertionIndex - 1).findFragment() + val insertionIndex = min(banner.position + offset, homeBinding.homeMainContainer.childCount) + val previousFragment = homeBinding.homeMainContainer.getChildAt(insertionIndex - 1).findFragment() - homeMainContainer.addView(binding.root, insertionIndex) + homeBinding.homeMainContainer.addView(binding.root, insertionIndex) binding.root.updateLayoutParams { val margin = if (previousFragment is LearningActionsFragment) { @@ -208,6 +209,6 @@ class HomeFragment : FragmentBase(), HomeStreakView, FastContinueNewHomeFragment } else { 0 } - homeNestedScrollView.setPadding(0, 0, 0, padding) + homeBinding.homeNestedScrollView.setPadding(0, 0, 0, padding) } } diff --git a/app/src/main/java/org/stepic/droid/ui/fragments/NotificationSettingsFragment.kt b/app/src/main/java/org/stepic/droid/ui/fragments/NotificationSettingsFragment.kt index c1b4752c54..ca847f7e3f 100644 --- a/app/src/main/java/org/stepic/droid/ui/fragments/NotificationSettingsFragment.kt +++ b/app/src/main/java/org/stepic/droid/ui/fragments/NotificationSettingsFragment.kt @@ -4,12 +4,15 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import kotlinx.android.synthetic.main.fragment_notification_settings.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.base.FragmentBase +import org.stepic.droid.databinding.FragmentNotificationSettingsBinding import org.stepic.droid.notifications.model.NotificationType class NotificationSettingsFragment : FragmentBase() { + private val notificationSettingsBinding: FragmentNotificationSettingsBinding by viewBinding(FragmentNotificationSettingsBinding::bind) + companion object { fun newInstance(): NotificationSettingsFragment = NotificationSettingsFragment() } @@ -28,41 +31,41 @@ class NotificationSettingsFragment : FragmentBase() { override fun onDestroyView() { super.onDestroyView() - fragmentSettingsNotificationLearnSwitch.setOnCheckedChangeListener(null) - fragmentSettingsNotificationCommentSwitch.setOnCheckedChangeListener(null) - fragmentSettingsNotificationTeachingSwitch.setOnCheckedChangeListener(null) - fragmentSettingsNotificationOtherSwitch.setOnCheckedChangeListener(null) - fragmentSettingsNotificationReviewSwitch.setOnCheckedChangeListener(null) - fragmentSettingsNotificationVibrationSwitch.setOnCheckedChangeListener(null) - fragmentSettingsNotificationSoundSwitch.setOnCheckedChangeListener(null) + notificationSettingsBinding.fragmentSettingsNotificationLearnSwitch.setOnCheckedChangeListener(null) + notificationSettingsBinding.fragmentSettingsNotificationCommentSwitch.setOnCheckedChangeListener(null) + notificationSettingsBinding.fragmentSettingsNotificationTeachingSwitch.setOnCheckedChangeListener(null) + notificationSettingsBinding.fragmentSettingsNotificationOtherSwitch.setOnCheckedChangeListener(null) + notificationSettingsBinding.fragmentSettingsNotificationReviewSwitch.setOnCheckedChangeListener(null) + notificationSettingsBinding.fragmentSettingsNotificationVibrationSwitch.setOnCheckedChangeListener(null) + notificationSettingsBinding.fragmentSettingsNotificationSoundSwitch.setOnCheckedChangeListener(null) } private fun setUpNotificationVibration() { - fragmentSettingsNotificationVibrationSwitch.isChecked = userPreferences.isVibrateNotificationEnabled - fragmentSettingsNotificationVibrationSwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.isVibrateNotificationEnabled = isChecked } + notificationSettingsBinding.fragmentSettingsNotificationVibrationSwitch.isChecked = userPreferences.isVibrateNotificationEnabled + notificationSettingsBinding.fragmentSettingsNotificationVibrationSwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.isVibrateNotificationEnabled = isChecked } } private fun setUpSound() { - fragmentSettingsNotificationSoundSwitch.isChecked = userPreferences.isSoundNotificationEnabled - fragmentSettingsNotificationSoundSwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.setNotificationSoundEnabled(isChecked) } + notificationSettingsBinding.fragmentSettingsNotificationSoundSwitch.isChecked = userPreferences.isSoundNotificationEnabled + notificationSettingsBinding.fragmentSettingsNotificationSoundSwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.setNotificationSoundEnabled(isChecked) } } private fun setUpNotifications() { - fragmentSettingsNotificationLearnSwitch.isChecked = userPreferences.isNotificationEnabled(NotificationType.learn) - fragmentSettingsNotificationLearnSwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.setNotificationEnabled(NotificationType.learn, isChecked) } + notificationSettingsBinding.fragmentSettingsNotificationLearnSwitch.isChecked = userPreferences.isNotificationEnabled(NotificationType.learn) + notificationSettingsBinding.fragmentSettingsNotificationLearnSwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.setNotificationEnabled(NotificationType.learn, isChecked) } - fragmentSettingsNotificationCommentSwitch.isChecked = userPreferences.isNotificationEnabled(NotificationType.comments) - fragmentSettingsNotificationCommentSwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.setNotificationEnabled(NotificationType.comments, isChecked) } + notificationSettingsBinding.fragmentSettingsNotificationCommentSwitch.isChecked = userPreferences.isNotificationEnabled(NotificationType.comments) + notificationSettingsBinding.fragmentSettingsNotificationCommentSwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.setNotificationEnabled(NotificationType.comments, isChecked) } - fragmentSettingsNotificationReviewSwitch.isChecked = userPreferences.isNotificationEnabled(NotificationType.review) - fragmentSettingsNotificationReviewSwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.setNotificationEnabled(NotificationType.review, isChecked) } + notificationSettingsBinding.fragmentSettingsNotificationReviewSwitch.isChecked = userPreferences.isNotificationEnabled(NotificationType.review) + notificationSettingsBinding.fragmentSettingsNotificationReviewSwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.setNotificationEnabled(NotificationType.review, isChecked) } - fragmentSettingsNotificationTeachingSwitch.isChecked = userPreferences.isNotificationEnabled(NotificationType.teach) - fragmentSettingsNotificationTeachingSwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.setNotificationEnabled(NotificationType.teach, isChecked) } + notificationSettingsBinding.fragmentSettingsNotificationTeachingSwitch.isChecked = userPreferences.isNotificationEnabled(NotificationType.teach) + notificationSettingsBinding.fragmentSettingsNotificationTeachingSwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.setNotificationEnabled(NotificationType.teach, isChecked) } - fragmentSettingsNotificationOtherSwitch.isChecked = userPreferences.isNotificationEnabled(NotificationType.other) - fragmentSettingsNotificationOtherSwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.setNotificationEnabled(NotificationType.other, isChecked) } + notificationSettingsBinding.fragmentSettingsNotificationOtherSwitch.isChecked = userPreferences.isNotificationEnabled(NotificationType.other) + notificationSettingsBinding.fragmentSettingsNotificationOtherSwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.setNotificationEnabled(NotificationType.other, isChecked) } } diff --git a/app/src/main/java/org/stepic/droid/ui/fragments/OnboardingFragment.kt b/app/src/main/java/org/stepic/droid/ui/fragments/OnboardingFragment.kt index 0ae3cd8367..3665c17e02 100644 --- a/app/src/main/java/org/stepic/droid/ui/fragments/OnboardingFragment.kt +++ b/app/src/main/java/org/stepic/droid/ui/fragments/OnboardingFragment.kt @@ -4,14 +4,17 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import kotlinx.android.synthetic.main.fragment_onboarding_page.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.base.FragmentBase +import org.stepic.droid.databinding.FragmentOnboardingPageBinding import org.stepic.droid.model.OnboardingType import org.stepic.droid.ui.activities.contracts.OnNextClickedListener import ru.nobird.android.view.base.ui.extension.argument class OnboardingFragment : FragmentBase() { + private val onboardingPageBinding: FragmentOnboardingPageBinding by viewBinding(FragmentOnboardingPageBinding::bind) + companion object { fun newInstance(onboardingType: OnboardingType): OnboardingFragment = OnboardingFragment() @@ -31,29 +34,29 @@ class OnboardingFragment : FragmentBase() { } private fun initScreen(type: OnboardingType) { - onboardingPageTitle.setText(type.title) - onboardingPageSubtitle.setText(type.subtitle) - onboardingPageAction.setText(type.getActionText()) + onboardingPageBinding.onboardingPageTitle.setText(type.title) + onboardingPageBinding.onboardingPageSubtitle.setText(type.subtitle) + onboardingPageBinding.onboardingPageAction.setText(type.getActionText()) - onboardingPageAction.setOnClickListener { + onboardingPageBinding.onboardingPageAction.setOnClickListener { (context as OnNextClickedListener).onNextClicked() } initAnimation(type) } private fun initAnimation(type: OnboardingType) { - onboardingAnimationView.visibility = View.VISIBLE - onboardingAnimationView.pauseAnimation() - onboardingAnimationView.setAnimation(type.assetPathToAnimation) + onboardingPageBinding.onboardingAnimationView.visibility = View.VISIBLE + onboardingPageBinding.onboardingAnimationView.pauseAnimation() + onboardingPageBinding.onboardingAnimationView.setAnimation(type.assetPathToAnimation) } fun startAnimation() { - onboardingAnimationView.setAnimation(onboardingType.assetPathToAnimation) - onboardingAnimationView.playAnimation() + onboardingPageBinding.onboardingAnimationView.setAnimation(onboardingType.assetPathToAnimation) + onboardingPageBinding.onboardingAnimationView.playAnimation() } override fun onPause() { super.onPause() - onboardingAnimationView.pauseAnimation() + onboardingPageBinding.onboardingAnimationView.pauseAnimation() } } diff --git a/app/src/main/java/org/stepic/droid/ui/fragments/StoreManagementFragment.kt b/app/src/main/java/org/stepic/droid/ui/fragments/StoreManagementFragment.kt index d52ebf19cf..cfa4ab4ed7 100644 --- a/app/src/main/java/org/stepic/droid/ui/fragments/StoreManagementFragment.kt +++ b/app/src/main/java/org/stepic/droid/ui/fragments/StoreManagementFragment.kt @@ -7,12 +7,13 @@ import android.view.View import androidx.core.view.isVisible import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment -import kotlinx.android.synthetic.main.fragment_space_management.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.presenters.StoreManagementPresenter import org.stepic.droid.core.presenters.contracts.StoreManagementView +import org.stepic.droid.databinding.FragmentSpaceManagementBinding import org.stepic.droid.persistence.model.StorageLocation import org.stepic.droid.ui.dialogs.ChooseStorageDialog import org.stepic.droid.ui.dialogs.ClearVideosDialog @@ -32,6 +33,8 @@ class StoreManagementFragment : Fragment(R.layout.fragment_space_management), St StoreManagementFragment() } + private val storeManagementBinding: FragmentSpaceManagementBinding by viewBinding(FragmentSpaceManagementBinding::bind) + private var mClearCacheDialogFragment: DialogFragment? = null private var loadingProgressDialogFragment: DialogFragment? = null @@ -70,42 +73,42 @@ class StoreManagementFragment : Fragment(R.layout.fragment_space_management), St } override fun onDestroyView() { - clearCacheButton.setOnClickListener(null) - chooseStorageButton.setOnClickListener(null) + storeManagementBinding.clearCacheButton.setOnClickListener(null) + storeManagementBinding.chooseStorageButton.setOnClickListener(null) super.onDestroyView() } private fun hideAllStorageInfo() { - notMountExplanation.visibility = View.GONE - mountExplanation.visibility = View.GONE - chooseStorageButton.visibility = View.GONE + storeManagementBinding.notMountExplanation.visibility = View.GONE + storeManagementBinding.mountExplanation.visibility = View.GONE + storeManagementBinding.chooseStorageButton.visibility = View.GONE } override fun setStorageOptions(options: List, selectedOption: StorageLocation?) { when { options.size > 1 -> { - notMountExplanation.visibility = View.GONE - mountExplanation.visibility = View.VISIBLE - chooseStorageButton.visibility = View.VISIBLE + storeManagementBinding.notMountExplanation.visibility = View.GONE + storeManagementBinding.mountExplanation.visibility = View.VISIBLE + storeManagementBinding.chooseStorageButton.visibility = View.VISIBLE val chooseStorageDialog = ChooseStorageDialog.newInstance() chooseStorageDialog.setTargetFragment(this, 0) - chooseStorageButton.setOnClickListener { + storeManagementBinding.chooseStorageButton.setOnClickListener { if (!chooseStorageDialog.isAdded) { chooseStorageDialog.show(requireFragmentManager(), null) } } - userStorageInfo.isVisible = selectedOption != null + storeManagementBinding.userStorageInfo.isVisible = selectedOption != null if (selectedOption != null) { - userStorageInfo.text = + storeManagementBinding.userStorageInfo.text = storageLocationDescriptionMapper.mapToDescription(options.indexOf(selectedOption), selectedOption) } } options.size == 1 -> { - notMountExplanation.visibility = View.VISIBLE - mountExplanation.visibility = View.GONE - chooseStorageButton.visibility = View.GONE + storeManagementBinding.notMountExplanation.visibility = View.VISIBLE + storeManagementBinding.mountExplanation.visibility = View.GONE + storeManagementBinding.chooseStorageButton.visibility = View.GONE } else -> @@ -117,23 +120,23 @@ class StoreManagementFragment : Fragment(R.layout.fragment_space_management), St mClearCacheDialogFragment = ClearVideosDialog.newInstance() mClearCacheDialogFragment?.setTargetFragment(this, ClearVideosDialog.REQUEST_CODE) - clearCacheButton.setOnClickListener { + storeManagementBinding.clearCacheButton.setOnClickListener { analytic.reportEvent(Analytic.Interaction.CLICK_CLEAR_CACHE) if (mClearCacheDialogFragment?.isAdded != true) { mClearCacheDialogFragment?.show(requireFragmentManager(), ClearVideosDialog.TAG) } } - clearCacheButton.isEnabled = false + storeManagementBinding.clearCacheButton.isEnabled = false } override fun setUpClearCacheButton(cacheSize: Long) { if (cacheSize > 0) { - clearCacheButton.isEnabled = true - clearCacheLabel.text = TextUtil.formatBytes(cacheSize) + storeManagementBinding.clearCacheButton.isEnabled = true + storeManagementBinding.clearCacheLabel.text = TextUtil.formatBytes(cacheSize) } else { - clearCacheButton.isEnabled = false - clearCacheLabel.setText(R.string.empty) + storeManagementBinding.clearCacheButton.isEnabled = false + storeManagementBinding.clearCacheLabel.setText(R.string.empty) } } @@ -164,4 +167,4 @@ class StoreManagementFragment : Fragment(R.layout.fragment_space_management), St super.onActivityResult(requestCode, resultCode, data) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepic/droid/ui/quiz/ChoiceQuizDelegate.kt b/app/src/main/java/org/stepic/droid/ui/quiz/ChoiceQuizDelegate.kt index 678bc60d54..6fd14d5fb9 100644 --- a/app/src/main/java/org/stepic/droid/ui/quiz/ChoiceQuizDelegate.kt +++ b/app/src/main/java/org/stepic/droid/ui/quiz/ChoiceQuizDelegate.kt @@ -3,9 +3,9 @@ package org.stepic.droid.ui.quiz import android.view.View import android.view.ViewGroup import android.widget.Button -import kotlinx.android.synthetic.main.view_choice_attempt.view.* import org.stepic.droid.R import org.stepic.droid.ui.adapters.StepikRadioGroupAdapter +import org.stepic.droid.databinding.ViewChoiceAttemptBinding import org.stepik.android.model.Submission import org.stepik.android.model.attempts.Attempt import ru.nobird.android.view.base.ui.extension.inflate @@ -26,7 +26,7 @@ class ChoiceQuizDelegate: QuizDelegate() { parent.inflate(R.layout.view_choice_attempt, false) override fun onViewCreated(view: View) { - choiceAdapter = StepikRadioGroupAdapter(view.choice_container) + choiceAdapter = StepikRadioGroupAdapter(ViewChoiceAttemptBinding.bind(view).choiceContainer) } override fun setSubmission(submission: Submission?) = choiceAdapter.setSubmission(submission) diff --git a/app/src/main/java/org/stepic/droid/ui/util/PopupHelper.kt b/app/src/main/java/org/stepic/droid/ui/util/PopupHelper.kt index b6df8cbfe4..8762357b2d 100644 --- a/app/src/main/java/org/stepic/droid/ui/util/PopupHelper.kt +++ b/app/src/main/java/org/stepic/droid/ui/util/PopupHelper.kt @@ -10,8 +10,8 @@ import android.widget.PopupWindow import androidx.annotation.DrawableRes import androidx.core.view.isVisible import androidx.core.widget.PopupWindowCompat -import kotlinx.android.synthetic.main.popup_window.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.PopupWindowBinding object PopupHelper { enum class PopupTheme( @@ -44,10 +44,11 @@ object PopupHelper { anchorView ?: return null val inflater = context.getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater - val popupView = inflater.inflate(R.layout.popup_window, null) + val binding = PopupWindowBinding.inflate(inflater) + val popupView = binding.root - val popupTextView = popupView.popupText - val popupArrowView = popupView.arrowView + val popupTextView = binding.popupText + val popupArrowView = binding.arrowView popupTextView.text = popupText popupTextView.setBackgroundResource(theme.backgroundRes) @@ -57,7 +58,7 @@ object PopupHelper { if (withArrow) { popupView.viewTreeObserver.addOnGlobalLayoutListener { - popupArrowView.x = calcArrowHorizontalOffset(anchorView, popupView, popupView.arrowView) + popupArrowView.x = calcArrowHorizontalOffset(anchorView, popupView, popupArrowView) } } diff --git a/app/src/main/java/org/stepic/droid/ui/util/ToolbarHelper.kt b/app/src/main/java/org/stepic/droid/ui/util/ToolbarHelper.kt index fbc4f721d6..c98cae2491 100644 --- a/app/src/main/java/org/stepic/droid/ui/util/ToolbarHelper.kt +++ b/app/src/main/java/org/stepic/droid/ui/util/ToolbarHelper.kt @@ -1,5 +1,7 @@ package org.stepic.droid.ui.util +import android.view.View +import android.widget.TextView import androidx.annotation.AttrRes import androidx.annotation.DrawableRes import androidx.annotation.StringRes @@ -7,11 +9,9 @@ import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.widget.Toolbar import androidx.fragment.app.Fragment -import kotlinx.android.synthetic.main.view_centered_toolbar.* import org.stepic.droid.R import org.stepik.android.view.base.ui.extension.setTintList - //Fragment's functions: @JvmOverloads @@ -39,7 +39,7 @@ private fun Fragment.initCenteredToolbarBase( @DrawableRes homeIndicatorRes: Int = -1 ) { val appCompatActivity = activity as AppCompatActivity - appCompatActivity.initCenteredToolbarBase(showHomeButton, homeIndicatorRes) + appCompatActivity.initCenteredToolbarBase(centeredToolbar, showHomeButton, homeIndicatorRes) } fun Fragment.setTitleToCenteredToolbar(title: String) { @@ -53,15 +53,16 @@ fun AppCompatActivity.initCenteredToolbar( showHomeButton: Boolean = false, @DrawableRes homeIndicator: Int = -1 ) { - initCenteredToolbarBase(showHomeButton, homeIndicator) + initCenteredToolbarBase(centeredToolbar, showHomeButton, homeIndicator) centeredToolbarTitle.setText(titleRes) } private fun AppCompatActivity.initCenteredToolbarBase( + toolbar: Toolbar, showHomeButton: Boolean, @DrawableRes homeIndicatorRes: Int = -1 ) { - this.setSupportActionBar(centeredToolbar) + this.setSupportActionBar(toolbar) val actionBar = this.supportActionBar ?: throw IllegalStateException("support action bar should be set") @@ -88,3 +89,47 @@ fun Toolbar.setTintedNavigationIcon(@DrawableRes iconRes: Int, @AttrRes tintRes: .getDrawable(context, iconRes) ?.setTintList(context, tintRes) } + + +private val Fragment.centeredToolbar: Toolbar + get() = requireView().findCenteredToolbar() + +private val Fragment.centeredToolbarTitle: TextView + get() = requireView().findCenteredToolbarTitle() + +private val AppCompatActivity.centeredToolbar: Toolbar + get() = findCenteredToolbar() + +private val AppCompatActivity.centeredToolbarTitle: TextView + get() = findCenteredToolbarTitle() + +private fun View.findCenteredToolbar(): Toolbar = + findViewById(R.id.centeredToolbar) + ?: findCenteredToolbarTitle().findParentToolbar() + ?: throw IllegalStateException("View with R.id.centeredToolbarTitle must be inside a Toolbar") + +private fun View.findCenteredToolbarTitle(): TextView = + findViewById(R.id.centeredToolbarTitle) + ?: throw IllegalStateException("View with R.id.centeredToolbarTitle was not found") + +private fun AppCompatActivity.findCenteredToolbar(): Toolbar = + findViewById(R.id.centeredToolbar) + ?: findCenteredToolbarTitle().findParentToolbar() + ?: throw IllegalStateException("View with R.id.centeredToolbarTitle must be inside a Toolbar") + +private fun AppCompatActivity.findCenteredToolbarTitle(): TextView = + findViewById(R.id.centeredToolbarTitle) + ?: throw IllegalStateException("View with R.id.centeredToolbarTitle was not found") + +// An overrides the included toolbar root id at runtime, +// so centeredToolbarTitle can be the only stable id. Walk up from it to recover the Toolbar. +private fun View.findParentToolbar(): Toolbar? { + var currentParent = parent + while (currentParent is View) { + if (currentParent is Toolbar) { + return currentParent + } + currentParent = (currentParent as View).parent + } + return null +} \ No newline at end of file diff --git a/app/src/main/java/org/stepik/android/data/course_list/model/CourseListQueryData.kt b/app/src/main/java/org/stepik/android/data/course_list/model/CourseListQueryData.kt index e3478b0820..422e6802d2 100644 --- a/app/src/main/java/org/stepik/android/data/course_list/model/CourseListQueryData.kt +++ b/app/src/main/java/org/stepik/android/data/course_list/model/CourseListQueryData.kt @@ -1,7 +1,7 @@ package org.stepik.android.data.course_list.model import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Parcelize data class CourseListQueryData( diff --git a/app/src/main/java/org/stepik/android/data/purchase_notification/model/PurchaseNotificationScheduled.kt b/app/src/main/java/org/stepik/android/data/purchase_notification/model/PurchaseNotificationScheduled.kt index 43ca4c1f02..c40235f5b3 100644 --- a/app/src/main/java/org/stepik/android/data/purchase_notification/model/PurchaseNotificationScheduled.kt +++ b/app/src/main/java/org/stepik/android/data/purchase_notification/model/PurchaseNotificationScheduled.kt @@ -1,7 +1,7 @@ package org.stepik.android.data.purchase_notification.model import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Parcelize data class PurchaseNotificationScheduled( diff --git a/app/src/main/java/org/stepik/android/domain/achievement/model/AchievementItem.kt b/app/src/main/java/org/stepik/android/domain/achievement/model/AchievementItem.kt index adf28f6b79..9adb3708b0 100644 --- a/app/src/main/java/org/stepik/android/domain/achievement/model/AchievementItem.kt +++ b/app/src/main/java/org/stepik/android/domain/achievement/model/AchievementItem.kt @@ -1,8 +1,8 @@ package org.stepik.android.domain.achievement.model import android.os.Parcelable -import kotlinx.android.parcel.IgnoredOnParcel -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.IgnoredOnParcel +import kotlinx.parcelize.Parcelize import org.stepik.android.model.achievements.Achievement import org.stepik.android.model.achievements.AchievementProgress import ru.nobird.app.core.model.Identifiable diff --git a/app/src/main/java/org/stepik/android/domain/calendar/model/CalendarItem.kt b/app/src/main/java/org/stepik/android/domain/calendar/model/CalendarItem.kt index 0bf63a8a96..1ced200a24 100644 --- a/app/src/main/java/org/stepik/android/domain/calendar/model/CalendarItem.kt +++ b/app/src/main/java/org/stepik/android/domain/calendar/model/CalendarItem.kt @@ -1,7 +1,7 @@ package org.stepik.android.domain.calendar.model import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Parcelize data class CalendarItem( diff --git a/app/src/main/java/org/stepik/android/domain/course/model/CourseHeaderData.kt b/app/src/main/java/org/stepik/android/domain/course/model/CourseHeaderData.kt index 6ee8db7473..43bcd40476 100644 --- a/app/src/main/java/org/stepik/android/domain/course/model/CourseHeaderData.kt +++ b/app/src/main/java/org/stepik/android/domain/course/model/CourseHeaderData.kt @@ -1,7 +1,7 @@ package org.stepik.android.domain.course.model import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import org.stepik.android.domain.course_payments.model.CoursePurchaseInfo import org.stepik.android.domain.course_payments.model.DefaultPromoCode import org.stepik.android.domain.course_payments.model.DeeplinkPromoCode diff --git a/app/src/main/java/org/stepik/android/domain/course/model/CourseStats.kt b/app/src/main/java/org/stepik/android/domain/course/model/CourseStats.kt index a235cc910e..130b040653 100644 --- a/app/src/main/java/org/stepik/android/domain/course/model/CourseStats.kt +++ b/app/src/main/java/org/stepik/android/domain/course/model/CourseStats.kt @@ -1,7 +1,7 @@ package org.stepik.android.domain.course.model import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import org.stepik.android.model.Progress @Parcelize diff --git a/app/src/main/java/org/stepik/android/domain/course/model/EnrollmentState.kt b/app/src/main/java/org/stepik/android/domain/course/model/EnrollmentState.kt index 5e74731363..6432d35115 100644 --- a/app/src/main/java/org/stepik/android/domain/course/model/EnrollmentState.kt +++ b/app/src/main/java/org/stepik/android/domain/course/model/EnrollmentState.kt @@ -1,7 +1,7 @@ package org.stepik.android.domain.course.model import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import org.stepik.android.domain.mobile_tiers.model.LightSku import org.stepik.android.domain.user_courses.model.UserCourse diff --git a/app/src/main/java/org/stepik/android/domain/course_info/model/CourseInfoData.kt b/app/src/main/java/org/stepik/android/domain/course_info/model/CourseInfoData.kt index 6d788150a9..d94546d505 100644 --- a/app/src/main/java/org/stepik/android/domain/course_info/model/CourseInfoData.kt +++ b/app/src/main/java/org/stepik/android/domain/course_info/model/CourseInfoData.kt @@ -1,7 +1,7 @@ package org.stepik.android.domain.course_info.model import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import org.stepik.android.model.user.User import org.stepik.android.view.video_player.model.VideoPlayerMediaData diff --git a/app/src/main/java/org/stepik/android/domain/course_list/model/CourseListQuery.kt b/app/src/main/java/org/stepik/android/domain/course_list/model/CourseListQuery.kt index 30d401016f..bf76c4ee01 100644 --- a/app/src/main/java/org/stepik/android/domain/course_list/model/CourseListQuery.kt +++ b/app/src/main/java/org/stepik/android/domain/course_list/model/CourseListQuery.kt @@ -2,7 +2,7 @@ package org.stepik.android.domain.course_list.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import org.stepik.android.domain.filter.model.CourseListFilterQuery import ru.nobird.app.core.model.mapOfNotNull import java.io.Serializable diff --git a/app/src/main/java/org/stepik/android/domain/course_list/model/UserCourseQuery.kt b/app/src/main/java/org/stepik/android/domain/course_list/model/UserCourseQuery.kt index e98e489401..4811d747d4 100644 --- a/app/src/main/java/org/stepik/android/domain/course_list/model/UserCourseQuery.kt +++ b/app/src/main/java/org/stepik/android/domain/course_list/model/UserCourseQuery.kt @@ -1,7 +1,7 @@ package org.stepik.android.domain.course_list.model import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Parcelize data class UserCourseQuery( diff --git a/app/src/main/java/org/stepik/android/domain/course_payments/model/CoursePurchaseInfo.kt b/app/src/main/java/org/stepik/android/domain/course_payments/model/CoursePurchaseInfo.kt index 1239330985..5d119b365d 100644 --- a/app/src/main/java/org/stepik/android/domain/course_payments/model/CoursePurchaseInfo.kt +++ b/app/src/main/java/org/stepik/android/domain/course_payments/model/CoursePurchaseInfo.kt @@ -1,7 +1,7 @@ package org.stepik.android.domain.course_payments.model import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import org.stepik.android.domain.course_purchase.model.CoursePurchaseObfuscatedParams sealed class CoursePurchaseInfo : Parcelable { diff --git a/app/src/main/java/org/stepik/android/domain/course_payments/model/DeeplinkPromoCode.kt b/app/src/main/java/org/stepik/android/domain/course_payments/model/DeeplinkPromoCode.kt index 87f4af5607..5e0c96c24f 100644 --- a/app/src/main/java/org/stepik/android/domain/course_payments/model/DeeplinkPromoCode.kt +++ b/app/src/main/java/org/stepik/android/domain/course_payments/model/DeeplinkPromoCode.kt @@ -1,7 +1,7 @@ package org.stepik.android.domain.course_payments.model import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Parcelize data class DeeplinkPromoCode( diff --git a/app/src/main/java/org/stepik/android/domain/course_payments/model/DefaultPromoCode.kt b/app/src/main/java/org/stepik/android/domain/course_payments/model/DefaultPromoCode.kt index a8f8f3fdd4..930f313586 100644 --- a/app/src/main/java/org/stepik/android/domain/course_payments/model/DefaultPromoCode.kt +++ b/app/src/main/java/org/stepik/android/domain/course_payments/model/DefaultPromoCode.kt @@ -1,7 +1,7 @@ package org.stepik.android.domain.course_payments.model import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import java.util.Date @Parcelize diff --git a/app/src/main/java/org/stepik/android/domain/course_payments/model/PromoCodeSku.kt b/app/src/main/java/org/stepik/android/domain/course_payments/model/PromoCodeSku.kt index 5b7d135112..8ea37a4571 100644 --- a/app/src/main/java/org/stepik/android/domain/course_payments/model/PromoCodeSku.kt +++ b/app/src/main/java/org/stepik/android/domain/course_payments/model/PromoCodeSku.kt @@ -1,7 +1,7 @@ package org.stepik.android.domain.course_payments.model import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import org.stepik.android.domain.mobile_tiers.model.LightSku @Parcelize diff --git a/app/src/main/java/org/stepik/android/domain/course_purchase/model/CoursePurchaseObfuscatedParams.kt b/app/src/main/java/org/stepik/android/domain/course_purchase/model/CoursePurchaseObfuscatedParams.kt index cf4d0a10bf..bd0a406586 100644 --- a/app/src/main/java/org/stepik/android/domain/course_purchase/model/CoursePurchaseObfuscatedParams.kt +++ b/app/src/main/java/org/stepik/android/domain/course_purchase/model/CoursePurchaseObfuscatedParams.kt @@ -1,7 +1,7 @@ package org.stepik.android.domain.course_purchase.model import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Parcelize data class CoursePurchaseObfuscatedParams( diff --git a/app/src/main/java/org/stepik/android/domain/course_revenue/model/CourseBeneficiary.kt b/app/src/main/java/org/stepik/android/domain/course_revenue/model/CourseBeneficiary.kt index e31920b067..5907c7db47 100644 --- a/app/src/main/java/org/stepik/android/domain/course_revenue/model/CourseBeneficiary.kt +++ b/app/src/main/java/org/stepik/android/domain/course_revenue/model/CourseBeneficiary.kt @@ -2,7 +2,7 @@ package org.stepik.android.domain.course_revenue.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Parcelize data class CourseBeneficiary( diff --git a/app/src/main/java/org/stepik/android/domain/course_revenue/model/CourseBenefit.kt b/app/src/main/java/org/stepik/android/domain/course_revenue/model/CourseBenefit.kt index 6bac630d29..e33d72303a 100644 --- a/app/src/main/java/org/stepik/android/domain/course_revenue/model/CourseBenefit.kt +++ b/app/src/main/java/org/stepik/android/domain/course_revenue/model/CourseBenefit.kt @@ -2,8 +2,8 @@ package org.stepik.android.domain.course_revenue.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.IgnoredOnParcel -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.IgnoredOnParcel +import kotlinx.parcelize.Parcelize import java.util.Date @Parcelize diff --git a/app/src/main/java/org/stepik/android/domain/filter/model/CourseListFilterQuery.kt b/app/src/main/java/org/stepik/android/domain/filter/model/CourseListFilterQuery.kt index d793468fc2..bff3d2a01c 100644 --- a/app/src/main/java/org/stepik/android/domain/filter/model/CourseListFilterQuery.kt +++ b/app/src/main/java/org/stepik/android/domain/filter/model/CourseListFilterQuery.kt @@ -2,7 +2,7 @@ package org.stepik.android.domain.filter.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import org.stepik.android.domain.filter_search.Difficulty import org.stepik.android.domain.filter_search.Language import org.stepik.android.domain.filter_search.Type diff --git a/app/src/main/java/org/stepik/android/domain/filter/model/SubmissionsFilterQuery.kt b/app/src/main/java/org/stepik/android/domain/filter/model/SubmissionsFilterQuery.kt index 8a6a98db69..06add8b7d3 100644 --- a/app/src/main/java/org/stepik/android/domain/filter/model/SubmissionsFilterQuery.kt +++ b/app/src/main/java/org/stepik/android/domain/filter/model/SubmissionsFilterQuery.kt @@ -2,7 +2,7 @@ package org.stepik.android.domain.filter.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import ru.nobird.app.core.model.mapOfNotNull @Parcelize diff --git a/app/src/main/java/org/stepik/android/domain/lesson/model/LessonData.kt b/app/src/main/java/org/stepik/android/domain/lesson/model/LessonData.kt index d45353c737..2eba209914 100644 --- a/app/src/main/java/org/stepik/android/domain/lesson/model/LessonData.kt +++ b/app/src/main/java/org/stepik/android/domain/lesson/model/LessonData.kt @@ -1,8 +1,8 @@ package org.stepik.android.domain.lesson.model import android.os.Parcelable -import kotlinx.android.parcel.IgnoredOnParcel -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.IgnoredOnParcel +import kotlinx.parcelize.Parcelize import org.stepik.android.model.Course import org.stepik.android.model.Lesson import org.stepik.android.model.Section diff --git a/app/src/main/java/org/stepik/android/domain/mobile_tiers/model/LightSku.kt b/app/src/main/java/org/stepik/android/domain/mobile_tiers/model/LightSku.kt index efd21b1e58..ed9bd7a419 100644 --- a/app/src/main/java/org/stepik/android/domain/mobile_tiers/model/LightSku.kt +++ b/app/src/main/java/org/stepik/android/domain/mobile_tiers/model/LightSku.kt @@ -3,7 +3,7 @@ package org.stepik.android.domain.mobile_tiers.model import android.os.Parcelable import androidx.room.Entity import androidx.room.PrimaryKey -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Parcelize @Entity diff --git a/app/src/main/java/org/stepik/android/domain/review_instruction/model/ReviewInstruction.kt b/app/src/main/java/org/stepik/android/domain/review_instruction/model/ReviewInstruction.kt index 4df651fb0f..cc05bc43d4 100644 --- a/app/src/main/java/org/stepik/android/domain/review_instruction/model/ReviewInstruction.kt +++ b/app/src/main/java/org/stepik/android/domain/review_instruction/model/ReviewInstruction.kt @@ -4,7 +4,7 @@ import android.os.Parcelable import androidx.room.Entity import androidx.room.PrimaryKey import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import org.stepik.android.model.ReviewStrategyType import ru.nobird.app.core.model.Identifiable diff --git a/app/src/main/java/org/stepik/android/domain/review_instruction/model/ReviewInstructionData.kt b/app/src/main/java/org/stepik/android/domain/review_instruction/model/ReviewInstructionData.kt index 787f792734..8b0f0613bc 100644 --- a/app/src/main/java/org/stepik/android/domain/review_instruction/model/ReviewInstructionData.kt +++ b/app/src/main/java/org/stepik/android/domain/review_instruction/model/ReviewInstructionData.kt @@ -1,7 +1,7 @@ package org.stepik.android.domain.review_instruction.model import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import org.stepik.android.domain.rubric.model.Rubric import ru.nobird.app.core.model.Identifiable diff --git a/app/src/main/java/org/stepik/android/domain/rubric/model/Rubric.kt b/app/src/main/java/org/stepik/android/domain/rubric/model/Rubric.kt index 1dacc5b4f9..a54f9bba35 100644 --- a/app/src/main/java/org/stepik/android/domain/rubric/model/Rubric.kt +++ b/app/src/main/java/org/stepik/android/domain/rubric/model/Rubric.kt @@ -4,7 +4,7 @@ import android.os.Parcelable import androidx.room.Entity import androidx.room.PrimaryKey import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Entity @Parcelize diff --git a/app/src/main/java/org/stepik/android/domain/step_quiz/model/StepQuizLessonData.kt b/app/src/main/java/org/stepik/android/domain/step_quiz/model/StepQuizLessonData.kt index c2a04e8139..039baec72f 100644 --- a/app/src/main/java/org/stepik/android/domain/step_quiz/model/StepQuizLessonData.kt +++ b/app/src/main/java/org/stepik/android/domain/step_quiz/model/StepQuizLessonData.kt @@ -1,7 +1,7 @@ package org.stepik.android.domain.step_quiz.model import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import org.stepik.android.domain.lesson.model.LessonData import org.stepik.android.model.DiscountingPolicyType diff --git a/app/src/main/java/org/stepik/android/domain/user_courses/model/UserCourse.kt b/app/src/main/java/org/stepik/android/domain/user_courses/model/UserCourse.kt index 076bfc6d48..2ff5c60421 100644 --- a/app/src/main/java/org/stepik/android/domain/user_courses/model/UserCourse.kt +++ b/app/src/main/java/org/stepik/android/domain/user_courses/model/UserCourse.kt @@ -2,7 +2,7 @@ package org.stepik.android.domain.user_courses.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import java.util.Date @Parcelize diff --git a/app/src/main/java/org/stepik/android/presentation/course_purchase/model/CoursePurchaseData.kt b/app/src/main/java/org/stepik/android/presentation/course_purchase/model/CoursePurchaseData.kt index 0deb80faa0..2917096b96 100644 --- a/app/src/main/java/org/stepik/android/presentation/course_purchase/model/CoursePurchaseData.kt +++ b/app/src/main/java/org/stepik/android/presentation/course_purchase/model/CoursePurchaseData.kt @@ -1,7 +1,7 @@ package org.stepik.android.presentation.course_purchase.model import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import org.stepik.android.domain.course.model.CourseStats import org.stepik.android.domain.course_payments.model.PromoCodeSku import org.stepik.android.domain.mobile_tiers.model.LightSku diff --git a/app/src/main/java/org/stepik/android/remote/course_payments/model/PromoCodeResponse.kt b/app/src/main/java/org/stepik/android/remote/course_payments/model/PromoCodeResponse.kt index 7bb271ea4c..5455966bd3 100644 --- a/app/src/main/java/org/stepik/android/remote/course_payments/model/PromoCodeResponse.kt +++ b/app/src/main/java/org/stepik/android/remote/course_payments/model/PromoCodeResponse.kt @@ -2,7 +2,7 @@ package org.stepik.android.remote.course_payments.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Parcelize data class PromoCodeResponse( diff --git a/app/src/main/java/org/stepik/android/view/achievement/ui/adapter/delegate/AchievementAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/achievement/ui/adapter/delegate/AchievementAdapterDelegate.kt index 90642f7d07..3820c00e72 100644 --- a/app/src/main/java/org/stepik/android/view/achievement/ui/adapter/delegate/AchievementAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/achievement/ui/adapter/delegate/AchievementAdapterDelegate.kt @@ -2,9 +2,9 @@ package org.stepik.android.view.achievement.ui.adapter.delegate import android.view.View import android.view.ViewGroup -import android.widget.TextView -import kotlinx.android.synthetic.main.view_achievement_item.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ViewAchievementItemBinding import org.stepik.android.view.achievement.ui.resolver.AchievementResourceResolver import org.stepik.android.domain.achievement.model.AchievementItem import org.stepik.android.view.achievement.ui.delegate.AchievementTileDelegate @@ -22,10 +22,9 @@ class AchievementAdapterDelegate( ViewHolder(createView(parent, R.layout.view_achievement_item)) private inner class ViewHolder(root: View) : DelegateViewHolder(root) { - private val achievementTitle: TextView = root.achievementTitle - private val achievementDescription: TextView = root.achievementDescription + private val viewBinding: ViewAchievementItemBinding by viewBinding { ViewAchievementItemBinding.bind(itemView) } - private val achievementTileDelegate = AchievementTileDelegate(root.achievementTile, achievementResourceResolver) + private val achievementTileDelegate = AchievementTileDelegate(viewBinding.achievementTile.root, achievementResourceResolver) init { root.setOnClickListener { itemData?.let(onItemClicked) } @@ -34,8 +33,8 @@ class AchievementAdapterDelegate( override fun onBind(data: AchievementItem) { achievementTileDelegate.setAchievement(data) - achievementTitle.text = achievementResourceResolver.resolveTitleForKind(data.kind) - achievementDescription.text = achievementResourceResolver.resolveDescription(data) + viewBinding.achievementTitle.text = achievementResourceResolver.resolveTitleForKind(data.kind) + viewBinding.achievementDescription.text = achievementResourceResolver.resolveDescription(data) } } } \ No newline at end of file diff --git a/app/src/main/java/org/stepik/android/view/achievement/ui/delegate/AchievementTileDelegate.kt b/app/src/main/java/org/stepik/android/view/achievement/ui/delegate/AchievementTileDelegate.kt index e4e227c04e..86b4f88435 100644 --- a/app/src/main/java/org/stepik/android/view/achievement/ui/delegate/AchievementTileDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/achievement/ui/delegate/AchievementTileDelegate.kt @@ -3,8 +3,8 @@ package org.stepik.android.view.achievement.ui.delegate import android.view.View import androidx.appcompat.content.res.AppCompatResources import androidx.core.view.isGone -import kotlinx.android.synthetic.main.view_achievement_tile.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.ViewAchievementTileBinding import org.stepik.android.view.achievement.ui.resolver.AchievementResourceResolver import org.stepik.android.domain.achievement.model.AchievementItem import org.stepik.android.view.glide.ui.extension.wrapWithGlide @@ -15,9 +15,11 @@ class AchievementTileDelegate( root: View, private val achievementResourceResolver: AchievementResourceResolver ) { - private val achievementLevels: VectorRatingBar = root.achievementLevels - private val achievementLevelProgress: AchievementCircleProgressView = root.achievementLevelProgress - private val achievementIcon = root.achievementIcon.wrapWithGlide() + private val binding = ViewAchievementTileBinding.bind(root) + + private val achievementLevels: VectorRatingBar = binding.achievementLevels + private val achievementLevelProgress: AchievementCircleProgressView = binding.achievementLevelProgress + private val achievementIcon = binding.achievementIcon.wrapWithGlide() private val achievementIconSize = root.resources.getDimensionPixelSize(R.dimen.achievement_tile_width) private val achievementIconPlaceholder = diff --git a/app/src/main/java/org/stepik/android/view/achievement/ui/dialog/AchievementDetailsDialog.kt b/app/src/main/java/org/stepik/android/view/achievement/ui/dialog/AchievementDetailsDialog.kt index 0cfc1f0ac0..ecddb9d028 100644 --- a/app/src/main/java/org/stepik/android/view/achievement/ui/dialog/AchievementDetailsDialog.kt +++ b/app/src/main/java/org/stepik/android/view/achievement/ui/dialog/AchievementDetailsDialog.kt @@ -8,11 +8,11 @@ import androidx.appcompat.content.res.AppCompatResources import androidx.core.view.isVisible import androidx.fragment.app.DialogFragment import com.google.android.material.dialog.MaterialAlertDialogBuilder -import kotlinx.android.synthetic.main.dialog_achievement_details.view.* import org.stepic.droid.R import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App +import org.stepic.droid.databinding.DialogAchievementDetailsBinding import org.stepik.android.view.glide.ui.extension.wrapWithGlide import org.stepik.android.domain.achievement.model.AchievementItem import org.stepik.android.view.achievement.ui.resolver.AchievementResourceResolver @@ -51,36 +51,34 @@ class AchievementDetailsDialog : DialogFragment() { } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val view = LayoutInflater.from(context).inflate(R.layout.dialog_achievement_details, null, false) - - view.apply { - achievementTitle.text = achievementResourceResolver.resolveTitleForKind(achievementItem.kind) - achievementDescription.text = achievementResourceResolver.resolveDescription(achievementItem) - achievementIcon.apply { - wrapWithGlide() - .setImagePath( - achievementResourceResolver.resolveAchievementIcon(achievementItem, resources.getDimensionPixelSize(R.dimen.achievement_details_icon_size)), - placeholder = AppCompatResources.getDrawable(context, R.drawable.ic_achievement_empty) - ) - } - - achievementLevelProgress.progress = achievementItem.currentScore.toFloat() / achievementItem.targetScore - achievementLevels.progress = achievementItem.currentLevel - achievementLevels.total = achievementItem.maxLevel - - achievementLevel.text = getString(R.string.achievement_level, achievementItem.currentLevel, achievementItem.maxLevel) - - val scoreDiff = achievementItem.targetScore - achievementItem.currentScore - achievementRest.text = if (achievementItem.isLocked) { - getString(R.string.achievement_remaining_exp_locked) - } else { - getString(R.string.achievement_remaining_exp, scoreDiff) - } - achievementRest.isVisible = scoreDiff > 0 + val binding = DialogAchievementDetailsBinding.inflate(LayoutInflater.from(context)) + + binding.achievementTitle.text = achievementResourceResolver.resolveTitleForKind(achievementItem.kind) + binding.achievementDescription.text = achievementResourceResolver.resolveDescription(achievementItem) + binding.achievementIcon.apply { + wrapWithGlide() + .setImagePath( + achievementResourceResolver.resolveAchievementIcon(achievementItem, resources.getDimensionPixelSize(R.dimen.achievement_details_icon_size)), + placeholder = AppCompatResources.getDrawable(context, R.drawable.ic_achievement_empty) + ) } + binding.achievementLevelProgress.progress = achievementItem.currentScore.toFloat() / achievementItem.targetScore + binding.achievementLevels.progress = achievementItem.currentLevel + binding.achievementLevels.total = achievementItem.maxLevel + + binding.achievementLevel.text = getString(R.string.achievement_level, achievementItem.currentLevel, achievementItem.maxLevel) + + val scoreDiff = achievementItem.targetScore - achievementItem.currentScore + binding.achievementRest.text = if (achievementItem.isLocked) { + getString(R.string.achievement_remaining_exp_locked) + } else { + getString(R.string.achievement_remaining_exp, scoreDiff) + } + binding.achievementRest.isVisible = scoreDiff > 0 + val builder = MaterialAlertDialogBuilder(requireContext()) - .setView(view) + .setView(binding.root) if (canShare && !achievementItem.isLocked) { builder diff --git a/app/src/main/java/org/stepik/android/view/achievement/ui/fragment/AchievementsListFragment.kt b/app/src/main/java/org/stepik/android/view/achievement/ui/fragment/AchievementsListFragment.kt index b0c85fbc76..bb9f536c6e 100644 --- a/app/src/main/java/org/stepik/android/view/achievement/ui/fragment/AchievementsListFragment.kt +++ b/app/src/main/java/org/stepik/android/view/achievement/ui/fragment/AchievementsListFragment.kt @@ -10,12 +10,12 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.error_no_connection_with_button.* -import kotlinx.android.synthetic.main.fragment_achievements_list.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App +import org.stepic.droid.databinding.FragmentAchievementsListBinding import org.stepic.droid.ui.util.initCenteredToolbar import org.stepik.android.domain.achievement.model.AchievementItem import org.stepik.android.presentation.achievement.AchievementsPresenter @@ -30,6 +30,8 @@ import ru.nobird.android.view.base.ui.extension.showIfNotExists import javax.inject.Inject class AchievementsListFragment : Fragment(), AchievementsView { + private val achievementsListBinding: FragmentAchievementsListBinding by viewBinding(FragmentAchievementsListBinding::bind) + companion object { fun newInstance(userId: Long, isMyProfile: Boolean): Fragment = AchievementsListFragment().apply { @@ -78,26 +80,26 @@ class AchievementsListFragment : Fragment(), AchievementsView { val context = requireContext() viewStateDelegate = ViewStateDelegate() - viewStateDelegate.addState(progress) - viewStateDelegate.addState(progress) - viewStateDelegate.addState(error) - viewStateDelegate.addState(recycler) + viewStateDelegate.addState(achievementsListBinding.progress) + viewStateDelegate.addState(achievementsListBinding.progress) + viewStateDelegate.addState(achievementsListBinding.errorLayout.root) + viewStateDelegate.addState(achievementsListBinding.recycler) initPlaceholders() initCenteredToolbar(R.string.achievements_title, showHomeButton = true) - recycler.layoutManager = LinearLayoutManager(context) - recycler.adapter = achievementsAdapter + achievementsListBinding.recycler.layoutManager = LinearLayoutManager(context) + achievementsListBinding.recycler.adapter = achievementsAdapter val divider = DividerItemDecoration(context, DividerItemDecoration.VERTICAL) divider.setDrawable(ContextCompat.getDrawable(context, R.drawable.bg_divider_vertical)!!) - recycler.addItemDecoration(divider) + achievementsListBinding.recycler.addItemDecoration(divider) achievementsPresenter.attachView(this) fetchAchievements() - tryAgain.setOnClickListener { fetchAchievements(true) } + achievementsListBinding.errorLayout.tryAgain.setOnClickListener { fetchAchievements(true) } } private fun initPlaceholders() { @@ -105,8 +107,8 @@ class AchievementsListFragment : Fragment(), AchievementsView { val screenHeight = resources.displayMetrics.heightPixels for (i in 0..(screenHeight / itemHeight).toInt()) { - progress.addView(layoutInflater.inflate(R.layout.view_achievement_item_placeholder, progress, false)) - progress.addView(layoutInflater.inflate(R.layout.view_divider_vertical, progress, false)) + achievementsListBinding.progress.addView(layoutInflater.inflate(R.layout.view_achievement_item_placeholder, achievementsListBinding.progress, false)) + achievementsListBinding.progress.addView(layoutInflater.inflate(R.layout.view_divider_vertical, achievementsListBinding.progress, false)) } } diff --git a/app/src/main/java/org/stepik/android/view/app_rating/ui/dialog/RateAppDialog.kt b/app/src/main/java/org/stepik/android/view/app_rating/ui/dialog/RateAppDialog.kt index ba8641f839..f7a2e7255f 100644 --- a/app/src/main/java/org/stepik/android/view/app_rating/ui/dialog/RateAppDialog.kt +++ b/app/src/main/java/org/stepik/android/view/app_rating/ui/dialog/RateAppDialog.kt @@ -8,8 +8,9 @@ import android.widget.TextView import androidx.annotation.AttrRes import androidx.annotation.StringRes import androidx.fragment.app.DialogFragment -import kotlinx.android.synthetic.main.dialog_rate_app.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.DialogRateAppBinding import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.util.RatingUtil @@ -37,6 +38,8 @@ class RateAppDialog : DialogFragment() { fun onClickSupport(starNumber: Int) } } + private val rateAppBinding: DialogRateAppBinding by viewBinding(DialogRateAppBinding::bind) + @Inject lateinit var analytic: Analytic @@ -54,14 +57,14 @@ class RateAppDialog : DialogFragment() { val callback = targetFragment as? Callback ?: activity as Callback - rateDialogLater.setOnClickListener { + rateAppBinding.rateDialogLater.setOnClickListener { dialog?.dismiss() - callback.onClickLater(rateDialogRatingBar.rating.toInt()) + callback.onClickLater(rateAppBinding.rateDialogRatingBar.rating.toInt()) } - rateDialogPositive.setOnClickListener { + rateAppBinding.rateDialogPositive.setOnClickListener { dialog?.dismiss() - val rating = rateDialogRatingBar.rating.toInt() + val rating = rateAppBinding.rateDialogRatingBar.rating.toInt() if (RatingUtil.isExcellent(rating)) { callback.onClickGooglePlay(rating) } else { @@ -69,7 +72,7 @@ class RateAppDialog : DialogFragment() { } } - rateDialogRatingBar.setOnRatingBarChangeListener { _, rating, fromUser -> + rateAppBinding.rateDialogRatingBar.setOnRatingBarChangeListener { _, rating, fromUser -> if (!fromUser) { return@setOnRatingBarChangeListener } @@ -87,28 +90,28 @@ class RateAppDialog : DialogFragment() { private fun applyRating(rating: Int) { if (rating == 0) { - rateDialogTitle.setText(R.string.rate_dialog_title) - rateDialogButtonsContainer.visibility = View.GONE - rateDialogHint.visibility = View.GONE + rateAppBinding.rateDialogTitle.setText(R.string.rate_dialog_title) + rateAppBinding.rateDialogButtonsContainer.visibility = View.GONE + rateAppBinding.rateDialogHint.visibility = View.GONE } else { - rateDialogHint.visibility = View.VISIBLE - rateDialogTitle.setText(R.string.rate_dialog_thanks) + rateAppBinding.rateDialogHint.visibility = View.VISIBLE + rateAppBinding.rateDialogTitle.setText(R.string.rate_dialog_thanks) if (rating in 1..4) { - rateDialogHint.setText(R.string.rate_dialog_hint_negative) - rateDialogPositive.setTextAndColor(R.string.rate_dialog_support, R.attr.colorError) + rateAppBinding.rateDialogHint.setText(R.string.rate_dialog_hint_negative) + rateAppBinding.rateDialogPositive.setTextAndColor(R.string.rate_dialog_support, R.attr.colorError) } else if (RatingUtil.isExcellent(rating)) { - rateDialogHint.setText(R.string.rate_dialog_hint_positive) - rateDialogPositive.setTextAndColor(R.string.rate_dialog_google_play, R.attr.colorSecondary) + rateAppBinding.rateDialogHint.setText(R.string.rate_dialog_hint_positive) + rateAppBinding.rateDialogPositive.setTextAndColor(R.string.rate_dialog_google_play, R.attr.colorSecondary) } - rateDialogButtonsContainer.visibility = View.VISIBLE + rateAppBinding.rateDialogButtonsContainer.visibility = View.VISIBLE } } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - outState.putInt(ratingKey, rateDialogRatingBar.rating.toInt()) + outState.putInt(ratingKey, rateAppBinding.rateDialogRatingBar.rating.toInt()) } private fun TextView.setTextAndColor(@StringRes stringRes: Int, @AttrRes textColorRes: Int) { 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 c5bc30dfb1..c75af86365 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 @@ -15,11 +15,12 @@ import androidx.core.content.res.ResourcesCompat import androidx.core.view.isVisible import androidx.fragment.app.DialogFragment import androidx.lifecycle.ViewModelProvider -import kotlinx.android.synthetic.main.activity_auth_credential.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.analytic.LoginInteractionType import org.stepic.droid.base.App +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 @@ -37,6 +38,8 @@ import ru.nobird.android.view.base.ui.extension.hideKeyboard import javax.inject.Inject class CredentialAuthActivity : SmartLockActivityBase(), CredentialAuthView { + private val binding: ActivityAuthCredentialBinding by viewBinding(ActivityAuthCredentialBinding::bind) + companion object { private const val EXTRA_EMAIL = "extra_email" private const val EXTRA_PASSWORD = "extra_password" @@ -79,20 +82,20 @@ class CredentialAuthActivity : SmartLockActivityBase(), CredentialAuthView { initTitle() - forgotPasswordView.setOnClickListener { + binding.forgotPasswordView.setOnClickListener { screenManager.openRemindPassword(this@CredentialAuthActivity) } - loginField.setOnEditorActionListener { _, actionId, _ -> + binding.loginField.setOnEditorActionListener { _, actionId, _ -> var handled = false if (actionId == EditorInfo.IME_ACTION_NEXT) { - passwordField.requestFocus() + binding.passwordField.requestFocus() handled = true } handled } - passwordField.setOnEditorActionListener { _, actionId, _ -> + binding.passwordField.setOnEditorActionListener { _, actionId, _ -> var handled = false if (actionId == EditorInfo.IME_ACTION_SEND) { analytic.reportEvent(Analytic.Interaction.CLICK_SIGN_IN_NEXT_ON_SIGN_IN_SCREEN) @@ -108,8 +111,8 @@ class CredentialAuthActivity : SmartLockActivityBase(), CredentialAuthView { analytic.reportEvent(Analytic.Login.TAP_ON_FIELDS) } } - loginField.setOnFocusChangeListener(onFocusField) - passwordField.setOnFocusChangeListener(onFocusField) + binding.loginField.setOnFocusChangeListener(onFocusField) + binding.passwordField.setOnFocusChangeListener(onFocusField) val reportAnalyticWhenTextBecomeNotBlank = object : TextWatcher { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { @@ -124,10 +127,10 @@ class CredentialAuthActivity : SmartLockActivityBase(), CredentialAuthView { override fun afterTextChanged(s: Editable?) {} } - loginField.addTextChangedListener(reportAnalyticWhenTextBecomeNotBlank) - passwordField.addTextChangedListener(reportAnalyticWhenTextBecomeNotBlank) + binding.loginField.addTextChangedListener(reportAnalyticWhenTextBecomeNotBlank) + binding.passwordField.addTextChangedListener(reportAnalyticWhenTextBecomeNotBlank) - launchSignUpButton.setOnClickListener { + binding.launchSignUpButton.setOnClickListener { analytic.reportEvent(Analytic.Interaction.CLICK_SIGN_UP) screenManager .showRegistration( @@ -136,22 +139,22 @@ class CredentialAuthActivity : SmartLockActivityBase(), CredentialAuthView { ) } - signInWithSocial.setOnClickListener { finish() } - loginButton.setOnClickListener { + binding.signInWithSocial.setOnClickListener { finish() } + binding.loginButton.setOnClickListener { analytic.reportEvent(Analytic.Interaction.CLICK_SIGN_IN_ON_SIGN_IN_SCREEN) analytic.reportEvent(Analytic.Login.REQUEST_LOGIN_WITH_INTERACTION_TYPE, LoginInteractionType.button.toBundle()) submit() } - loginRootView.requestFocus() + binding.loginRootView.requestFocus() initGoogleApiClient() - setOnKeyboardOpenListener(root_view, { - stepikLogo.isVisible = false - signInText.isVisible = false + setOnKeyboardOpenListener(binding.rootView, { + binding.stepikLogo.isVisible = false + binding.signInText.isVisible = false }, { - stepikLogo.isVisible = true - signInText.isVisible = true + binding.stepikLogo.isVisible = true + binding.signInText.isVisible = true }) if (savedInstanceState == null) { @@ -185,7 +188,7 @@ class CredentialAuthActivity : SmartLockActivityBase(), CredentialAuthView { spannableSignIn.setSpan(TypefaceSpanCompat(typeface), 0, signInString.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - signInText.text = spannableSignIn + binding.signInText.text = spannableSignIn } override fun onNewIntent(intent: Intent) { @@ -201,15 +204,15 @@ class CredentialAuthActivity : SmartLockActivityBase(), CredentialAuthView { val email = intent.getStringExtra(EXTRA_EMAIL) val password = intent.getStringExtra(EXTRA_PASSWORD) - loginField.setText(email) - passwordField.setText(password) + binding.loginField.setText(email) + binding.passwordField.setText(password) when { email == null -> - loginField.requestFocus() + binding.loginField.requestFocus() - passwordField == null -> - passwordField.requestFocus() + password == null -> + binding.passwordField.requestFocus() } if (autoAuth != AutoAuth.NONE) { @@ -220,8 +223,8 @@ class CredentialAuthActivity : SmartLockActivityBase(), CredentialAuthView { private fun submit(autoAuth: AutoAuth = AutoAuth.NONE) { currentFocus?.hideKeyboard() - val login = loginField.text.toString() - val password = passwordField.text.toString() + val login = binding.loginField.text.toString() + val password = binding.passwordField.text.toString() credentialAuthPresenter.submit(Credentials(login, password), isRegistration = autoAuth == AutoAuth.REGISTRATION) } @@ -237,19 +240,19 @@ class CredentialAuthActivity : SmartLockActivityBase(), CredentialAuthView { when (state) { is CredentialAuthView.State.Idle -> { - loginButton.isEnabled = true - loginForm.isEnabled = true - loginErrorMessage.isVisible = false + binding.loginButton.isEnabled = true + binding.loginForm.isEnabled = true + binding.loginErrorMessage.isVisible = false } is CredentialAuthView.State.Error -> { - loginErrorMessage.text = getMessageFor(state.failType) - loginErrorMessage.isVisible = true + binding.loginErrorMessage.text = getMessageFor(state.failType) + binding.loginErrorMessage.isVisible = true if (state.failType == LoginFailType.EMAIL_ALREADY_USED || state.failType == LoginFailType.EMAIL_PASSWORD_INVALID) { - loginForm.isEnabled = false - loginButton.isEnabled = false + binding.loginForm.isEnabled = false + binding.loginButton.isEnabled = false } } 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 5fde1c5a8a..0db1ed2c7a 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 @@ -18,11 +18,12 @@ import androidx.core.view.isVisible import androidx.core.widget.CompoundButtonCompat import androidx.fragment.app.DialogFragment import androidx.lifecycle.ViewModelProvider -import kotlinx.android.synthetic.main.activity_registration.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.analytic.LoginInteractionType 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 @@ -47,6 +48,8 @@ import javax.inject.Inject import kotlin.getValue class RegistrationActivity : SmartLockActivityBase(), RegistrationView { + private val binding: ActivityRegistrationBinding by viewBinding(ActivityRegistrationBinding::bind) + companion object { private const val ERROR_DELIMITER = " " @@ -104,24 +107,24 @@ class RegistrationActivity : SmartLockActivityBase(), RegistrationView { initTitle() - requiredConsentText.movementMethod = LinkMovementMethod.getInstance() - requiredConsentText.text = textResolver.fromHtml(consentRequiredMessage) - stripUnderlinesFromLinks(requiredConsentText) + binding.requiredConsentText.movementMethod = LinkMovementMethod.getInstance() + binding.requiredConsentText.text = textResolver.fromHtml(consentRequiredMessage) + stripUnderlinesFromLinks(binding.requiredConsentText) - requiredConsentCheckBox.setOnCheckedChangeListener { _, _ -> + binding.requiredConsentCheckBox.setOnCheckedChangeListener { _, _ -> clearRequiredConsentError() } clearRequiredConsentError() if (consentState.isMarketingVisible) { - marketingConsentRow.isVisible = true - marketingHelperText.isVisible = true - marketingConsentCheckBox.isChecked = consentState.isMarketingChecked + binding.marketingConsentRow.isVisible = true + binding.marketingHelperText.isVisible = true + binding.marketingConsentCheckBox.isChecked = consentState.isMarketingChecked } - signUpButton.setOnClickListener { submit(LoginInteractionType.button) } + binding.signUpButton.setOnClickListener { submit(LoginInteractionType.button) } - passwordField.setOnEditorActionListener { _, actionId, _ -> + binding.passwordField.setOnEditorActionListener { _, actionId, _ -> var handled = false if (actionId == EditorInfo.IME_ACTION_SEND) { analytic.reportEvent(Analytic.Registration.CLICK_SEND_IME) @@ -147,49 +150,49 @@ class RegistrationActivity : SmartLockActivityBase(), RegistrationView { } } - firstNameField.addTextChangedListener(reportAnalyticWhenTextBecomeNotBlank) - emailField.addTextChangedListener(reportAnalyticWhenTextBecomeNotBlank) - passwordField.addTextChangedListener(reportAnalyticWhenTextBecomeNotBlank) + binding.firstNameField.addTextChangedListener(reportAnalyticWhenTextBecomeNotBlank) + binding.emailField.addTextChangedListener(reportAnalyticWhenTextBecomeNotBlank) + binding.passwordField.addTextChangedListener(reportAnalyticWhenTextBecomeNotBlank) val onFocusChangeListener = View.OnFocusChangeListener { _, hasFocus -> if (hasFocus) { analytic.reportEvent(Analytic.Registration.TAP_ON_FIELDS) } } - firstNameField.onFocusChangeListener = onFocusChangeListener - emailField.onFocusChangeListener = onFocusChangeListener - passwordField.onFocusChangeListener = onFocusChangeListener + binding.firstNameField.onFocusChangeListener = onFocusChangeListener + binding.emailField.onFocusChangeListener = onFocusChangeListener + binding.passwordField.onFocusChangeListener = onFocusChangeListener - firstNameField.setOnEditorActionListener { _, actionId, _ -> + binding.firstNameField.setOnEditorActionListener { _, actionId, _ -> if (actionId == EditorInfo.IME_ACTION_NEXT) { - emailField.requestFocus() + binding.emailField.requestFocus() true } else { false } } - emailField.setOnEditorActionListener { _, actionId, _ -> + binding.emailField.setOnEditorActionListener { _, actionId, _ -> if (actionId == EditorInfo.IME_ACTION_NEXT) { - passwordField.requestFocus() + binding.passwordField.requestFocus() true } else { false } } - registerRootView.requestFocus() + binding.registerRootView.requestFocus() initGoogleApiClient() setSignUpButtonState() - setOnKeyboardOpenListener(root_view, { - stepikLogo.isVisible = false - signUpText.isVisible = false + setOnKeyboardOpenListener(binding.rootView, { + binding.stepikLogo.isVisible = false + binding.signUpText.isVisible = false }, { - stepikLogo.isVisible = true - signUpText.isVisible = true + binding.stepikLogo.isVisible = true + binding.signUpText.isVisible = true }) } @@ -211,10 +214,10 @@ class RegistrationActivity : SmartLockActivityBase(), RegistrationView { } private fun setSignUpButtonState() { - signUpButton.isEnabled = - emailField.text.isNullOrBlank() == false && - firstNameField.text.isNullOrBlank() == false && - passwordField.text.isNullOrBlank() == false + binding.signUpButton.isEnabled = + binding.emailField.text.isNullOrBlank() == false && + binding.firstNameField.text.isNullOrBlank() == false && + binding.passwordField.text.isNullOrBlank() == false } private fun initTitle() { @@ -226,17 +229,17 @@ class RegistrationActivity : SmartLockActivityBase(), RegistrationView { spannableSignIn.setSpan(TypefaceSpanCompat(typeface), 0, signUpString.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - signUpText.text = spannableSignIn + binding.signUpText.text = spannableSignIn } private fun submit(interactionType: LoginInteractionType) { analytic.reportEvent(Analytic.Registration.CLICK_WITH_INTERACTION_TYPE, interactionType.toBundle()) currentFocus?.hideKeyboard() - val firstName = firstNameField.text.toString().trim() + val firstName = binding.firstNameField.text.toString().trim() val lastName = " " // registrationSecondName.text.toString().trim() - val email = emailField.text.toString().trim() - val password = passwordField.text.toString() + val email = binding.emailField.text.toString().trim() + val password = binding.passwordField.text.toString() analytic.reportEvent(Analytic.Interaction.CLICK_REGISTER_BUTTON) @@ -272,23 +275,23 @@ class RegistrationActivity : SmartLockActivityBase(), RegistrationView { private fun getCurrentConsentState(): RegistrationConsentState = RegistrationConsentState( - isRequiredConsentGranted = requiredConsentCheckBox.isChecked, - isMarketingVisible = marketingConsentRow.isVisible, - isMarketingChecked = marketingConsentCheckBox.isChecked + isRequiredConsentGranted = binding.requiredConsentCheckBox.isChecked, + isMarketingVisible = binding.marketingConsentRow.isVisible, + isMarketingChecked = binding.marketingConsentCheckBox.isChecked ) private fun showRequiredConsentError(showErrorText: Boolean = false) { - consentErrorText.isVisible = showErrorText + binding.consentErrorText.isVisible = showErrorText CompoundButtonCompat.setButtonTintList( - requiredConsentCheckBox, + binding.requiredConsentCheckBox, requiredConsentErrorTint ) } private fun clearRequiredConsentError() { - consentErrorText.isVisible = false + binding.consentErrorText.isVisible = false CompoundButtonCompat.setButtonTintList( - requiredConsentCheckBox, + binding.requiredConsentCheckBox, requiredConsentDefaultTint ) } @@ -314,9 +317,9 @@ class RegistrationActivity : SmartLockActivityBase(), RegistrationView { when (state) { is RegistrationView.State.Idle -> { - signUpButton.isEnabled = true - registerForm.isEnabled = true - registerErrorMessage.isVisible = false + binding.signUpButton.isEnabled = true + binding.registerForm.isEnabled = true + binding.registerErrorMessage.isVisible = false } is RegistrationView.State.Error -> { @@ -342,7 +345,7 @@ class RegistrationActivity : SmartLockActivityBase(), RegistrationView { } override fun showNetworkError() { - registerRootView.snackbar(messageRes = R.string.connectionProblems) + binding.registerRootView.snackbar(messageRes = R.string.connectionProblems) } override fun applyTransitionPrev() {} // we need default system animation @@ -350,11 +353,11 @@ class RegistrationActivity : SmartLockActivityBase(), RegistrationView { private fun showError(errorText: String?) { errorText?.let { analytic.reportEventWithName(Analytic.Registration.ERROR, errorText) - if (registerErrorMessage.visibility == View.GONE) { - signUpButton.isEnabled = false - registerForm.isEnabled = false - registerErrorMessage.text = it - registerErrorMessage.isVisible = true + if (binding.registerErrorMessage.visibility == View.GONE) { + binding.signUpButton.isEnabled = false + binding.registerForm.isEnabled = false + binding.registerErrorMessage.text = it + binding.registerErrorMessage.isVisible = true } } } 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 63ad56c5b1..6033e8bb07 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 @@ -12,6 +12,7 @@ import androidx.core.view.isVisible import androidx.fragment.app.DialogFragment import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.GridLayoutManager +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.gms.auth.api.Auth import com.google.android.gms.common.api.GoogleApiClient import com.vk.api.sdk.VK @@ -20,12 +21,12 @@ 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 kotlinx.android.synthetic.main.activity_auth_social.* import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.analytic.experiments.DeferredAuthSplitTest import org.stepic.droid.analytic.experiments.OnboardingSplitTestVersion2 import org.stepic.droid.base.App +import org.stepic.droid.databinding.ActivityAuthSocialBinding import org.stepic.droid.model.Credentials import org.stepic.droid.preferences.SharedPreferenceHelper import org.stepik.android.domain.auth.model.PendingSocialMarketingConsent @@ -49,6 +50,8 @@ import ru.nobird.android.view.base.ui.extension.showIfNotExists import javax.inject.Inject class SocialAuthActivity : SmartLockActivityBase(), SocialAuthView, SocialAuthConsentBottomSheetDialogFragment.Callback { + private val binding: ActivityAuthSocialBinding by viewBinding(ActivityAuthSocialBinding::bind) + companion object { private const val REQUEST_CODE_GOOGLE_SIGN_IN = 7007 @@ -113,19 +116,19 @@ class SocialAuthActivity : SmartLockActivityBase(), SocialAuthView, SocialAuthCo overridePendingTransition(R.anim.no_transition, R.anim.slide_out_to_bottom) - dismissButton.setOnClickListener { + binding.dismissButton.setOnClickListener { onBackPressed() } - dismissButton.isVisible = true + binding.dismissButton.isVisible = true // dismissButton.isVisible = deferredAuthSplitTest.currentGroup.isDeferredAuth || onboardingSplitTest.currentGroup == OnboardingSplitTest.Group.Personalized - launchSignUpButton.setOnClickListener { + binding.launchSignUpButton.setOnClickListener { analytic.reportEvent(Analytic.Interaction.CLICK_SIGN_UP) screenManager.showRegistration(this@SocialAuthActivity, course) } - signInWithEmail.setOnClickListener { + binding.signInWithEmail.setOnClickListener { analytic.reportEvent(Analytic.Interaction.CLICK_SIGN_IN) screenManager.showLogin(this@SocialAuthActivity, null, null, AutoAuth.NONE, course) } @@ -149,7 +152,7 @@ class SocialAuthActivity : SmartLockActivityBase(), SocialAuthView, SocialAuthCo spannableSignIn.setSpan(typefaceSpan, 0, signInString.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - signInText.text = spannableSignIn + binding.signInText.text = spannableSignIn // callbackManager = CallbackManager.Factory.create() // LoginManager.getInstance().registerCallback(callbackManager, object : FacebookCallback { @@ -206,30 +209,30 @@ class SocialAuthActivity : SmartLockActivityBase(), SocialAuthView, SocialAuthCo } private fun initSocialRecycler(state: SocialAuthAdapter.State = SocialAuthAdapter.State.NORMAL) { - socialListRecyclerView.layoutManager = GridLayoutManager(this, 3) + binding.socialListRecyclerView.layoutManager = GridLayoutManager(this, 3) - socialListRecyclerView.itemAnimator = FadeInDownAnimator() + binding.socialListRecyclerView.itemAnimator = FadeInDownAnimator() .apply { removeDuration = 0 } val adapter = SocialAuthAdapter(this::onSocialItemClicked, state) - showMore.setOnClickListener { - showMore.isVisible = false - showLess.isVisible = true + binding.showMore.setOnClickListener { + binding.showMore.isVisible = false + binding.showLess.isVisible = true adapter.showMore() } - showLess.setOnClickListener { - showLess.isVisible = false - showMore.isVisible = true + binding.showLess.setOnClickListener { + binding.showLess.isVisible = false + binding.showMore.isVisible = true adapter.showLess() } - showLess.isVisible = state == SocialAuthAdapter.State.EXPANDED - showMore.isVisible = state == SocialAuthAdapter.State.NORMAL + binding.showLess.isVisible = state == SocialAuthAdapter.State.EXPANDED + binding.showMore.isVisible = state == SocialAuthAdapter.State.NORMAL - socialListRecyclerView.adapter = adapter + binding.socialListRecyclerView.adapter = adapter } private fun onSocialItemClicked(type: SocialNetwork) { @@ -249,7 +252,7 @@ class SocialAuthActivity : SmartLockActivityBase(), SocialAuthView, SocialAuthCo SocialNetwork.GOOGLE -> { if (googleApiClient == null) { analytic.reportEvent(Analytic.Interaction.GOOGLE_SOCIAL_IS_NOT_ENABLED) - root_view.snackbar(messageRes = R.string.google_services_late) + binding.rootView.snackbar(messageRes = R.string.google_services_late) } else { val signInIntent = Auth.GoogleSignInApi.getSignInIntent(googleApiClient) startActivityForResult(signInIntent, REQUEST_CODE_GOOGLE_SIGN_IN) @@ -368,7 +371,7 @@ class SocialAuthActivity : SmartLockActivityBase(), SocialAuthView, SocialAuthCo } override fun showAuthError(failType: LoginFailType) { - root_view.snackbar(message = getMessageFor(failType)) + binding.rootView.snackbar(message = getMessageFor(failType)) // logout from socials VK.logout() @@ -380,7 +383,7 @@ class SocialAuthActivity : SmartLockActivityBase(), SocialAuthView, SocialAuthCo } override fun showNetworkError() { - root_view.snackbar(messageRes = R.string.connectionProblems) + binding.rootView.snackbar(messageRes = R.string.connectionProblems) } override fun onSocialLoginWithExistingEmail(email: String) { @@ -388,7 +391,7 @@ class SocialAuthActivity : SmartLockActivityBase(), SocialAuthView, SocialAuthCo } override fun onSaveInstanceState(outState: Bundle) { - val adapter = socialListRecyclerView.adapter + val adapter = binding.socialListRecyclerView.adapter if (adapter is SocialAuthAdapter) { outState.putSerializable(KEY_SOCIAL_ADAPTER_STATE, adapter.state) } diff --git a/app/src/main/java/org/stepik/android/view/auth/ui/dialog/SocialAuthConsentBottomSheetDialogFragment.kt b/app/src/main/java/org/stepik/android/view/auth/ui/dialog/SocialAuthConsentBottomSheetDialogFragment.kt index d126025ffc..1ab7b8760e 100644 --- a/app/src/main/java/org/stepik/android/view/auth/ui/dialog/SocialAuthConsentBottomSheetDialogFragment.kt +++ b/app/src/main/java/org/stepik/android/view/auth/ui/dialog/SocialAuthConsentBottomSheetDialogFragment.kt @@ -10,11 +10,12 @@ import androidx.core.text.HtmlCompat import androidx.core.view.isVisible import androidx.core.widget.CompoundButtonCompat import androidx.fragment.app.DialogFragment +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import kotlinx.android.synthetic.main.bottom_sheet_dialog_social_auth_consent.* import org.stepic.droid.R +import org.stepic.droid.databinding.BottomSheetDialogSocialAuthConsentBinding import org.stepic.droid.util.resolveColorAttribute import org.stepic.droid.util.stripUnderlinesFromLinks import org.stepik.android.domain.auth.mapper.SocialConsentMapper @@ -27,6 +28,8 @@ import ru.nobird.android.view.base.ui.extension.argument class SocialAuthConsentBottomSheetDialogFragment : BottomSheetDialogFragment() { + private val binding: BottomSheetDialogSocialAuthConsentBinding by viewBinding(BottomSheetDialogSocialAuthConsentBinding::bind) + companion object { const val TAG = "SocialAuthConsentBottomSheetDialogFragment" @@ -69,26 +72,26 @@ class SocialAuthConsentBottomSheetDialogFragment : super.onViewCreated(view, savedInstanceState) val consentRequiredHtml = getString(R.string.registration_consent_required) - requiredConsentText.movementMethod = LinkMovementMethod.getInstance() - requiredConsentText.text = HtmlCompat.fromHtml(consentRequiredHtml, HtmlCompat.FROM_HTML_MODE_LEGACY) - stripUnderlinesFromLinks(requiredConsentText) + binding.requiredConsentText.movementMethod = LinkMovementMethod.getInstance() + binding.requiredConsentText.text = HtmlCompat.fromHtml(consentRequiredHtml, HtmlCompat.FROM_HTML_MODE_LEGACY) + stripUnderlinesFromLinks(binding.requiredConsentText) if (isMarketingEnabled) { - marketingConsentRow.isVisible = true - marketingHelperText.isVisible = true - marketingConsentCheckBox.isChecked = true + binding.marketingConsentRow.isVisible = true + binding.marketingHelperText.isVisible = true + binding.marketingConsentCheckBox.isChecked = true } - requiredConsentCheckBox.setOnCheckedChangeListener { _, _ -> + binding.requiredConsentCheckBox.setOnCheckedChangeListener { _, _ -> clearRequiredConsentError() } clearRequiredConsentError() - continueButton.setOnClickListener { + binding.continueButton.setOnClickListener { val currentState = RegistrationConsentState( - isRequiredConsentGranted = requiredConsentCheckBox.isChecked, - isMarketingVisible = marketingConsentRow.isVisible, - isMarketingChecked = marketingConsentCheckBox.isChecked + isRequiredConsentGranted = binding.requiredConsentCheckBox.isChecked, + isMarketingVisible = binding.marketingConsentRow.isVisible, + isMarketingChecked = binding.marketingConsentCheckBox.isChecked ) val result = SocialConsentMapper.mapConsent(currentState, socialNetwork) @@ -105,17 +108,17 @@ class SocialAuthConsentBottomSheetDialogFragment : } private fun showRequiredConsentError() { - consentErrorText.isVisible = true + binding.consentErrorText.isVisible = true CompoundButtonCompat.setButtonTintList( - requiredConsentCheckBox, + binding.requiredConsentCheckBox, requiredConsentErrorTint ) } private fun clearRequiredConsentError() { - consentErrorText.isVisible = false + binding.consentErrorText.isVisible = false CompoundButtonCompat.setButtonTintList( - requiredConsentCheckBox, + binding.requiredConsentCheckBox, requiredConsentDefaultTint ) } diff --git a/app/src/main/java/org/stepik/android/view/banner/extension/ItemBannerBindingExtension.kt b/app/src/main/java/org/stepik/android/view/banner/extension/ItemBannerBindingExtension.kt index 37b6e325e7..98e9c1c916 100644 --- a/app/src/main/java/org/stepik/android/view/banner/extension/ItemBannerBindingExtension.kt +++ b/app/src/main/java/org/stepik/android/view/banner/extension/ItemBannerBindingExtension.kt @@ -4,7 +4,6 @@ import android.net.Uri import androidx.appcompat.content.res.AppCompatResources import androidx.core.view.ViewCompat import androidx.fragment.app.FragmentManager -import kotlinx.android.synthetic.main.item_banner.view.* import org.stepic.droid.databinding.ItemBannerBinding import org.stepik.android.domain.banner.model.Banner import org.stepik.android.view.banner.mapper.BannerResourcesMapper @@ -29,6 +28,6 @@ fun ItemBannerBinding.bind(banner: Banner, bannerResourcesMapper: BannerResource val descriptionTextColorRes = bannerResourcesMapper.mapBannerTypeToDescriptionTextColor(root.context, banner.type) bannerImage.setImageResource(imageRes) - ViewCompat.setBackgroundTintList(root.bannerRoot, AppCompatResources.getColorStateList(root.context, backgroundColorRes)) + ViewCompat.setBackgroundTintList(bannerRoot, AppCompatResources.getColorStateList(root.context, backgroundColorRes)) bannerDescription.setTextColor(descriptionTextColorRes) } \ No newline at end of file diff --git a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/AuthorAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/AuthorAdapterDelegate.kt index 3052543305..50b64acec7 100644 --- a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/AuthorAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/AuthorAdapterDelegate.kt @@ -1,12 +1,10 @@ package org.stepik.android.view.catalog.ui.adapter.delegate -import android.view.View import android.view.ViewGroup +import by.kirich1409.viewbindingdelegate.viewBinding import com.bumptech.glide.Glide -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_author.* -import kotlinx.android.synthetic.main.layout_author_properties.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.ItemAuthorBinding import org.stepic.droid.util.TextUtil import org.stepik.android.domain.catalog.model.CatalogAuthor import ru.nobird.android.ui.adapterdelegates.AdapterDelegate @@ -21,14 +19,14 @@ class AuthorAdapterDelegate( override fun onCreateViewHolder(parent: ViewGroup): DelegateViewHolder = ViewHolder(createView(parent, R.layout.item_author)) - private inner class ViewHolder( - override val containerView: View - ) : DelegateViewHolder(containerView), LayoutContainer { - private val authorCourseCount = authorListPropertiesContainer.coursesCountText - private val authorSubscriberCount = authorListPropertiesContainer.subscribersCountText + private inner class ViewHolder(root: android.view.View) : DelegateViewHolder(root) { + private val viewBinding: ItemAuthorBinding by viewBinding { ItemAuthorBinding.bind(root) } + + private val authorCourseCount = viewBinding.authorListPropertiesContainer.coursesCountText + private val authorSubscriberCount = viewBinding.authorListPropertiesContainer.subscribersCountText init { - containerView.setOnClickListener { itemData?.id?.let(onItemClick) } + root.setOnClickListener { itemData?.id?.let(onItemClick) } } override fun onBind(data: CatalogAuthor) { @@ -38,9 +36,9 @@ class AuthorAdapterDelegate( .load(data.avatar) .placeholder(R.drawable.general_placeholder) .fitCenter() - .into(authorListImage) + .into(viewBinding.authorListImage) - authorListTitle.text = data.fullName + viewBinding.authorListTitle.text = data.fullName authorCourseCount.text = context.resources.getQuantityString(R.plurals.course_count, data.createdCoursesCount, data.createdCoursesCount) authorSubscriberCount.text = context.resources.getString(R.string.author_subscribers, TextUtil.formatNumbers(data.followersCount.toLong())) } diff --git a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/AuthorListAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/AuthorListAdapterDelegate.kt index e4bc219362..bbfcb8ca1c 100644 --- a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/AuthorListAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/AuthorListAdapterDelegate.kt @@ -1,13 +1,11 @@ package org.stepik.android.view.catalog.ui.adapter.delegate -import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.LinearSnapHelper import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.header_catalog_block.* -import kotlinx.android.synthetic.main.item_author_list.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemAuthorListBinding import org.stepik.android.domain.catalog.model.CatalogAuthor import org.stepik.android.presentation.course_list_redux.model.CatalogBlockStateWrapper import org.stepik.android.view.base.ui.adapter.layoutmanager.TableLayoutManager @@ -31,12 +29,11 @@ class AuthorListAdapterDelegate( override fun onCreateViewHolder(parent: ViewGroup): DelegateViewHolder = AuthorListViewHolder(createView(parent, R.layout.item_author_list)) - private inner class AuthorListViewHolder( - override val containerView: View - ) : DelegateViewHolder(containerView), LayoutContainer { + private inner class AuthorListViewHolder(root: android.view.View) : DelegateViewHolder(root) { + private val viewBinding: ItemAuthorListBinding by viewBinding { ItemAuthorListBinding.bind(root) } private val catalogBlockTitleDelegate = - CatalogBlockHeaderDelegate(catalogBlockContainer, null) + CatalogBlockHeaderDelegate(viewBinding.catalogBlockHeader.root, null) private val adapter = DefaultDelegateAdapter() .also { @@ -45,7 +42,7 @@ class AuthorListAdapterDelegate( init { val rowCount = context.resources.getInteger(R.integer.author_lists_default_rows) - authorListRecycler.layoutManager = + viewBinding.authorListRecycler.layoutManager = TableLayoutManager( context, horizontalSpanCount = context.resources.getInteger(R.integer.author_lists_default_columns), @@ -53,12 +50,12 @@ class AuthorListAdapterDelegate( orientation = RecyclerView.HORIZONTAL, reverseLayout = false ) - authorListRecycler.setRecycledViewPool(sharedViewPool) - authorListRecycler.setHasFixedSize(true) - authorListRecycler.adapter = adapter + viewBinding.authorListRecycler.setRecycledViewPool(sharedViewPool) + viewBinding.authorListRecycler.setHasFixedSize(true) + viewBinding.authorListRecycler.adapter = adapter val snapHelper = LinearSnapHelper() - snapHelper.attachToRecyclerView(authorListRecycler) + snapHelper.attachToRecyclerView(viewBinding.authorListRecycler) } override fun onBind(data: CatalogItem) { @@ -74,4 +71,4 @@ class AuthorListAdapterDelegate( catalogBlockTitleDelegate.setCount(authorCountMapper.mapAuthorCountToString(context, count)) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/CourseListAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/CourseListAdapterDelegate.kt index df6d630aac..47dac181df 100644 --- a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/CourseListAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/CourseListAdapterDelegate.kt @@ -4,12 +4,12 @@ import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.LinearSnapHelper import androidx.recyclerview.widget.RecyclerView +import by.kirich1409.viewbindingdelegate.viewBinding import dagger.assisted.Assisted import dagger.assisted.AssistedInject -import kotlinx.android.synthetic.main.header_catalog_block.view.* -import kotlinx.android.synthetic.main.item_course_list_new.view.* import org.stepic.droid.R import org.stepic.droid.analytic.Analytic +import org.stepic.droid.databinding.ItemCourseListNewBinding import org.stepik.android.domain.catalog.model.CatalogBlock import org.stepik.android.domain.catalog.model.CatalogBlockContent import org.stepik.android.domain.course.analytic.CourseViewSource @@ -56,12 +56,13 @@ constructor( CourseCollectionViewHolder(createView(parent, R.layout.item_course_list_new)) private inner class CourseCollectionViewHolder(root: View) : DelegateViewHolder(root) { + private val viewBinding: ItemCourseListNewBinding by viewBinding { ItemCourseListNewBinding.bind(root) } private var catalogBlock: CatalogBlock? = null private var courseCount: Int? = null - private val courseListCoursesRecycler = root.courseListCoursesRecycler - private val courseListTitleContainer = root.catalogBlockContainer + private val courseListCoursesRecycler = viewBinding.courseListCoursesRecycler + private val courseListTitleContainer = viewBinding.catalogBlockHeader.root private val catalogBlockTitleDelegate = CatalogBlockHeaderDelegate(courseListTitleContainer) { val block = (catalogBlock?.content as? CatalogBlockContent.FullCourseList) ?: return@CatalogBlockHeaderDelegate diff --git a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/FiltersAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/FiltersAdapterDelegate.kt index 788c69a284..956fb8b75b 100644 --- a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/FiltersAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/FiltersAdapterDelegate.kt @@ -3,8 +3,8 @@ package org.stepik.android.view.catalog.ui.adapter.delegate import android.view.View import android.view.ViewGroup import com.google.android.material.button.MaterialButtonToggleGroup -import kotlinx.android.synthetic.main.view_course_languages.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.ViewCourseLanguagesBinding import org.stepic.droid.model.StepikFilter import org.stepik.android.presentation.filter.FiltersFeature import org.stepik.android.view.catalog.model.CatalogItem @@ -24,7 +24,7 @@ class FiltersAdapterDelegate( private inner class FiltersViewHolder(root: View) : DelegateViewHolder(root) { private val viewStateDelegate = ViewStateDelegate() - private val languages = itemView.languages + private val languages = ViewCourseLanguagesBinding.bind(itemView).languages init { val toggleListener = diff --git a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/OfflineAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/OfflineAdapterDelegate.kt index 7c5b82747f..94379d99e2 100644 --- a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/OfflineAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/OfflineAdapterDelegate.kt @@ -5,9 +5,9 @@ import android.view.ViewGroup import androidx.core.view.doOnLayout import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.error_no_connection_with_button_small.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ErrorNoConnectionWithButtonSmallBinding import org.stepik.android.view.catalog.model.CatalogItem import ru.nobird.app.core.model.safeCast import ru.nobird.android.ui.adapterdelegates.AdapterDelegate @@ -28,13 +28,14 @@ class OfflineAdapterDelegate( } private class OfflineViewHolder( - override val containerView: View, + root: View, private val onRetry: () -> Unit - ) : DelegateViewHolder(containerView), LayoutContainer { + ) : DelegateViewHolder(root) { + private val viewBinding: ErrorNoConnectionWithButtonSmallBinding by viewBinding { ErrorNoConnectionWithButtonSmallBinding.bind(root) } init { - tryAgain.setOnClickListener { onRetry() } - containerView.isVisible = true + viewBinding.tryAgain.setOnClickListener { onRetry() } + root.isVisible = true } override fun onBind(data: CatalogItem) { @@ -42,13 +43,13 @@ class OfflineAdapterDelegate( itemView.doOnLayout { val parent = it.parent.safeCast() ?: return@doOnLayout - val remainingHeight = parent.height - containerView.bottom - containerView.top + val remainingHeight = parent.height - itemView.bottom - itemView.top if (remainingHeight > 0) { itemView.updateLayoutParams { - height = containerView.height + remainingHeight + height = itemView.height + remainingHeight } } } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/RecommendedCourseListAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/RecommendedCourseListAdapterDelegate.kt index 8c98b933ed..82bbdaf878 100644 --- a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/RecommendedCourseListAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/RecommendedCourseListAdapterDelegate.kt @@ -4,13 +4,12 @@ import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.LinearSnapHelper import androidx.recyclerview.widget.RecyclerView +import by.kirich1409.viewbindingdelegate.viewBinding import dagger.assisted.Assisted import dagger.assisted.AssistedInject -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.header_catalog_block.view.* -import kotlinx.android.synthetic.main.item_course_list_new.* import org.stepic.droid.R import org.stepic.droid.analytic.Analytic +import org.stepic.droid.databinding.ItemCourseListNewBinding import org.stepik.android.domain.catalog.model.CatalogBlock import org.stepik.android.domain.catalog.model.CatalogBlockContent import org.stepik.android.domain.course.analytic.CourseViewSource @@ -54,11 +53,13 @@ constructor( CourseRecommendationsViewHolder(createView(parent, R.layout.item_course_list_new)) private inner class CourseRecommendationsViewHolder( - override val containerView: View - ) : DelegateViewHolder(containerView), LayoutContainer { + root: View + ) : DelegateViewHolder(root) { + private val viewBinding: ItemCourseListNewBinding by viewBinding { ItemCourseListNewBinding.bind(root) } + private var catalogBlock: CatalogBlock? = null - private val courseListTitleContainer = containerView.catalogBlockContainer + private val courseListTitleContainer = viewBinding.catalogBlockHeader.root private val catalogBlockTitleDelegate = CatalogBlockHeaderDelegate(courseListTitleContainer) private val skeletonCount = context.resources.getInteger(R.integer.course_list_rows) * context.resources.getInteger(R.integer.course_list_columns) @@ -69,9 +70,9 @@ constructor( private var tableLayoutManager: TableLayoutManager init { - viewStateDelegate.addState(courseListCoursesRecycler) - viewStateDelegate.addState(courseListTitleContainer, courseListCoursesRecycler) - viewStateDelegate.addState(courseListTitleContainer, courseListCoursesRecycler) + viewStateDelegate.addState(viewBinding.courseListCoursesRecycler) + viewStateDelegate.addState(courseListTitleContainer, viewBinding.courseListCoursesRecycler) + viewStateDelegate.addState(courseListTitleContainer, viewBinding.courseListCoursesRecycler) viewStateDelegate.addState() viewStateDelegate.addState() @@ -90,7 +91,7 @@ constructor( val columnsCount = context.resources.getInteger(R.integer.course_list_columns) tableLayoutManager = TableLayoutManager(context, columnsCount, rowCount, RecyclerView.HORIZONTAL, false) - with(courseListCoursesRecycler) { + with(viewBinding.courseListCoursesRecycler) { adapter = courseItemAdapter layoutManager = tableLayoutManager itemAnimator?.changeDuration = 0 diff --git a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SimpleCourseListDefaultAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SimpleCourseListDefaultAdapterDelegate.kt index 641e751be8..7b28768099 100644 --- a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SimpleCourseListDefaultAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SimpleCourseListDefaultAdapterDelegate.kt @@ -1,12 +1,11 @@ package org.stepik.android.view.catalog.ui.adapter.delegate -import android.view.View import android.view.ViewGroup import androidx.appcompat.content.res.AppCompatResources import androidx.core.view.ViewCompat -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_simple_course_list_default.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemSimpleCourseListDefaultBinding import org.stepik.android.domain.catalog.model.CatalogCourseList import org.stepik.android.view.catalog.mapper.CourseCountMapper import ru.nobird.android.ui.adapterdelegates.AdapterDelegate @@ -22,9 +21,9 @@ class SimpleCourseListDefaultAdapterDelegate( override fun onCreateViewHolder(parent: ViewGroup): DelegateViewHolder = ViewHolder(createView(parent, R.layout.item_simple_course_list_default)) - private inner class ViewHolder( - override val containerView: View - ) : DelegateViewHolder(containerView), LayoutContainer { + private inner class ViewHolder(root: android.view.View) : DelegateViewHolder(root) { + private val viewBinding: ItemSimpleCourseListDefaultBinding by viewBinding { ItemSimpleCourseListDefaultBinding.bind(root) } + private val colorSchemes = listOf( R.color.color_overlay_green, @@ -34,20 +33,20 @@ class SimpleCourseListDefaultAdapterDelegate( ).map { AppCompatResources.getColorStateList(context, it) } init { - containerView.setOnClickListener { onCourseListClicked(itemData ?: return@setOnClickListener) } + root.setOnClickListener { onCourseListClicked(itemData ?: return@setOnClickListener) } } override fun onBind(data: CatalogCourseList) { - simpleCourseListTitle.text = data.title - simpleCourseListCount.text = + viewBinding.simpleCourseListTitle.text = data.title + viewBinding.simpleCourseListCount.text = courseCountMapper.mapCourseCountToString(context, data.coursesCount) val colorList = colorSchemes[adapterPosition % colorSchemes.size] - simpleCourseListTitle.setTextColor(colorList) - simpleCourseListCount.setTextColor(colorList) + viewBinding.simpleCourseListTitle.setTextColor(colorList) + viewBinding.simpleCourseListCount.setTextColor(colorList) ViewCompat.setBackgroundTintList(itemView, colorList) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SimpleCourseListGridAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SimpleCourseListGridAdapterDelegate.kt index 4a10c6a792..ce237195e1 100644 --- a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SimpleCourseListGridAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SimpleCourseListGridAdapterDelegate.kt @@ -1,12 +1,11 @@ package org.stepik.android.view.catalog.ui.adapter.delegate -import android.view.View import android.view.ViewGroup import androidx.core.view.updateLayoutParams +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.flexbox.FlexboxLayoutManager -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_block_simple_course_list_grid.* import org.stepic.droid.R +import org.stepic.droid.databinding.ItemBlockSimpleCourseListGridBinding import org.stepik.android.domain.catalog.model.CatalogCourseList import ru.nobird.android.ui.adapterdelegates.AdapterDelegate import ru.nobird.android.ui.adapterdelegates.DelegateViewHolder @@ -20,17 +19,16 @@ class SimpleCourseListGridAdapterDelegate( override fun onCreateViewHolder(parent: ViewGroup): DelegateViewHolder = ViewHolder(createView(parent, R.layout.item_block_simple_course_list_grid)) - private inner class ViewHolder( - override val containerView: View - ) : DelegateViewHolder(containerView), LayoutContainer { + private inner class ViewHolder(root: android.view.View) : DelegateViewHolder(root) { + private val viewBinding: ItemBlockSimpleCourseListGridBinding by viewBinding { ItemBlockSimpleCourseListGridBinding.bind(root) } init { - containerView.setOnClickListener { onCourseListClicked(itemData ?: return@setOnClickListener) } + root.setOnClickListener { onCourseListClicked(itemData ?: return@setOnClickListener) } } override fun onBind(data: CatalogCourseList) { - simpleCourseListGridTitle.text = data.title + viewBinding.simpleCourseListGridTitle.text = data.title itemView.updateLayoutParams { flexGrow = 1f } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SimpleCourseListGridFirstAdapter.kt b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SimpleCourseListGridFirstAdapter.kt index bc7243f6b3..c460a67fea 100644 --- a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SimpleCourseListGridFirstAdapter.kt +++ b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SimpleCourseListGridFirstAdapter.kt @@ -1,10 +1,9 @@ package org.stepik.android.view.catalog.ui.adapter.delegate -import android.view.View import android.view.ViewGroup -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_block_simple_course_list_grid_first.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemBlockSimpleCourseListGridFirstBinding import org.stepik.android.domain.catalog.model.CatalogCourseList import org.stepik.android.view.catalog.mapper.CourseCountMapper import ru.nobird.android.ui.adapterdelegates.AdapterDelegate @@ -20,17 +19,17 @@ class SimpleCourseListGridFirstAdapter( override fun onCreateViewHolder(parent: ViewGroup): DelegateViewHolder = ViewHolder(createView(parent, R.layout.item_block_simple_course_list_grid_first)) - private inner class ViewHolder( - override val containerView: View - ) : DelegateViewHolder(containerView), LayoutContainer { + private inner class ViewHolder(root: android.view.View) : DelegateViewHolder(root) { + private val viewBinding: ItemBlockSimpleCourseListGridFirstBinding by viewBinding { ItemBlockSimpleCourseListGridFirstBinding.bind(root) } + init { - simpleCourseListGridOverlay.setOnClickListener { onCourseListClicked(itemData ?: return@setOnClickListener) } + viewBinding.simpleCourseListGridOverlay.setOnClickListener { onCourseListClicked(itemData ?: return@setOnClickListener) } } override fun onBind(data: CatalogCourseList) { - simpleCourseListGridTitle.text = data.title - simpleCourseListGridCount.text = + viewBinding.simpleCourseListGridTitle.text = data.title + viewBinding.simpleCourseListGridCount.text = courseCountMapper.mapCourseCountToString(context, data.coursesCount) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SimpleCourseListsDefaultAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SimpleCourseListsDefaultAdapterDelegate.kt index ab36b771f2..6cf19c412a 100644 --- a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SimpleCourseListsDefaultAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SimpleCourseListsDefaultAdapterDelegate.kt @@ -1,13 +1,11 @@ package org.stepik.android.view.catalog.ui.adapter.delegate -import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.header_catalog_block.* -import kotlinx.android.synthetic.main.item_block_simple_course_lists_default.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemBlockSimpleCourseListsDefaultBinding import org.stepic.droid.ui.util.CoursesSnapHelper import org.stepik.android.domain.catalog.model.CatalogCourseList import org.stepik.android.presentation.course_list_redux.model.CatalogBlockStateWrapper @@ -32,11 +30,11 @@ class SimpleCourseListsDefaultAdapterDelegate( override fun onCreateViewHolder(parent: ViewGroup): DelegateViewHolder = ViewHolder(createView(parent, R.layout.item_block_simple_course_lists_default)) - private inner class ViewHolder( - override val containerView: View - ) : DelegateViewHolder(containerView), LayoutContainer { + private inner class ViewHolder(root: android.view.View) : DelegateViewHolder(root) { + private val viewBinding: ItemBlockSimpleCourseListsDefaultBinding by viewBinding { ItemBlockSimpleCourseListsDefaultBinding.bind(root) } + private val catalogBlockTitleDelegate = - CatalogBlockHeaderDelegate(catalogBlockContainer, null) + CatalogBlockHeaderDelegate(viewBinding.catalogBlockHeader.root, null) private val adapter = DefaultDelegateAdapter() .also { @@ -45,7 +43,7 @@ class SimpleCourseListsDefaultAdapterDelegate( init { val rowCount = context.resources.getInteger(R.integer.simple_course_lists_default_rows) - courseListsRecycler.layoutManager = + viewBinding.courseListsRecycler.layoutManager = TableLayoutManager( context, horizontalSpanCount = context.resources.getInteger(R.integer.simple_course_lists_default_columns), @@ -53,12 +51,12 @@ class SimpleCourseListsDefaultAdapterDelegate( orientation = LinearLayoutManager.HORIZONTAL, reverseLayout = false ) - courseListsRecycler.setRecycledViewPool(sharedViewPool) - courseListsRecycler.setHasFixedSize(true) - courseListsRecycler.adapter = adapter + viewBinding.courseListsRecycler.setRecycledViewPool(sharedViewPool) + viewBinding.courseListsRecycler.setHasFixedSize(true) + viewBinding.courseListsRecycler.adapter = adapter val snapHelper = CoursesSnapHelper(rowCount) - snapHelper.attachToRecyclerView(courseListsRecycler) + snapHelper.attachToRecyclerView(viewBinding.courseListsRecycler) } override fun onBind(data: CatalogItem) { @@ -74,4 +72,4 @@ class SimpleCourseListsDefaultAdapterDelegate( catalogBlockTitleDelegate.setCount(context.resources.getQuantityString(R.plurals.catalog_course_lists, count, count)) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SimpleCourseListsGridAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SimpleCourseListsGridAdapterDelegate.kt index e09588b244..d0dcf2e23a 100644 --- a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SimpleCourseListsGridAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SimpleCourseListsGridAdapterDelegate.kt @@ -1,16 +1,14 @@ package org.stepik.android.view.catalog.ui.adapter.delegate -import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.flexbox.FlexboxLayoutManager import com.google.android.flexbox.FlexDirection import com.google.android.flexbox.FlexWrap import com.google.android.flexbox.JustifyContent -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.header_catalog_block.* -import kotlinx.android.synthetic.main.item_block_simple_course_lists_grid.courseListsRecycler import org.stepic.droid.R +import org.stepic.droid.databinding.ItemBlockSimpleCourseListsGridBinding import org.stepik.android.domain.catalog.model.CatalogCourseList import org.stepik.android.presentation.course_list_redux.model.CatalogBlockStateWrapper import org.stepik.android.view.catalog.mapper.CourseCountMapper @@ -33,11 +31,11 @@ class SimpleCourseListsGridAdapterDelegate( override fun onCreateViewHolder(parent: ViewGroup): DelegateViewHolder = ViewHolder(createView(parent, R.layout.item_block_simple_course_lists_grid)) - private inner class ViewHolder( - override val containerView: View - ) : DelegateViewHolder(containerView), LayoutContainer { + private inner class ViewHolder(root: android.view.View) : DelegateViewHolder(root) { + private val viewBinding: ItemBlockSimpleCourseListsGridBinding by viewBinding { ItemBlockSimpleCourseListsGridBinding.bind(root) } + private val catalogBlockTitleDelegate = - CatalogBlockHeaderDelegate(catalogBlockContainer, null) + CatalogBlockHeaderDelegate(viewBinding.catalogBlockHeader.root, null) private val adapter = DefaultDelegateAdapter() .also { @@ -46,13 +44,13 @@ class SimpleCourseListsGridAdapterDelegate( } init { - courseListsRecycler.layoutManager = + viewBinding.courseListsRecycler.layoutManager = FlexboxLayoutManager(context, FlexDirection.ROW, FlexWrap.WRAP) .apply { justifyContent = JustifyContent.FLEX_START } - courseListsRecycler.setRecycledViewPool(sharedViewPool) - courseListsRecycler.setHasFixedSize(true) - courseListsRecycler.adapter = adapter + viewBinding.courseListsRecycler.setRecycledViewPool(sharedViewPool) + viewBinding.courseListsRecycler.setHasFixedSize(true) + viewBinding.courseListsRecycler.adapter = adapter } override fun onBind(data: CatalogItem) { @@ -69,4 +67,4 @@ class SimpleCourseListsGridAdapterDelegate( catalogBlockTitleDelegate.setCount(context.resources.getQuantityString(R.plurals.catalog_course_lists, count, count)) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SpecializationAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SpecializationAdapterDelegate.kt index 2a1a6e1593..d4d22ac13d 100644 --- a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SpecializationAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SpecializationAdapterDelegate.kt @@ -1,12 +1,11 @@ package org.stepik.android.view.catalog.ui.adapter.delegate -import android.view.View import android.view.ViewGroup import androidx.core.text.buildSpannedString import androidx.core.text.strikeThrough -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_specialization.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemSpecializationBinding import org.stepik.android.domain.catalog.model.CatalogSpecialization import ru.nobird.android.ui.adapterdelegates.AdapterDelegate import ru.nobird.android.ui.adapterdelegates.DelegateViewHolder @@ -28,8 +27,9 @@ class SpecializationAdapterDelegate( ViewHolder(createView(parent, R.layout.item_specialization)) private inner class ViewHolder( - override val containerView: View - ) : DelegateViewHolder(containerView), LayoutContainer { + containerView: android.view.View + ) : DelegateViewHolder(containerView) { + private val viewBinding: ItemSpecializationBinding by viewBinding { ItemSpecializationBinding.bind(itemView) } init { containerView.setOnClickListener { @@ -40,19 +40,19 @@ class SpecializationAdapterDelegate( } override fun onBind(data: CatalogSpecialization) { - specializationTitle.text = data.title - specializationDuration.text = data.duration + viewBinding.specializationTitle.text = data.title + viewBinding.specializationDuration.text = data.duration val discount = data.discount?.toFloatOrNull() ?: 0f if (discount > 0f && data.discount != null) { - specializationPrice.text = formatDisplayPrice(data.discount.removeSuffix(PRICE_SUFFIX), data.currency) - specializationDiscountPrice.text = buildSpannedString { + viewBinding.specializationPrice.text = formatDisplayPrice(data.discount.removeSuffix(PRICE_SUFFIX), data.currency) + viewBinding.specializationDiscountPrice.text = buildSpannedString { strikeThrough { append(formatDisplayPrice(data.price.removeSuffix(PRICE_SUFFIX), data.currency)) } } } else { - specializationPrice.text = formatDisplayPrice(data.price.removeSuffix(PRICE_SUFFIX), data.currency) + viewBinding.specializationPrice.text = formatDisplayPrice(data.price.removeSuffix(PRICE_SUFFIX), data.currency) } } diff --git a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SpecializationListAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SpecializationListAdapterDelegate.kt index 8323d79591..2438b7d2f3 100644 --- a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SpecializationListAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/SpecializationListAdapterDelegate.kt @@ -1,13 +1,12 @@ package org.stepik.android.view.catalog.ui.adapter.delegate -import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearSnapHelper import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_specialization_list.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemSpecializationListBinding import org.stepik.android.domain.catalog.model.CatalogSpecialization import org.stepik.android.presentation.course_list_redux.model.CatalogBlockStateWrapper import org.stepik.android.view.base.ui.adapter.layoutmanager.TableLayoutManager @@ -29,8 +28,9 @@ class SpecializationListAdapterDelegate( SpecializationListViewHolder(createView(parent, R.layout.item_specialization_list)) private inner class SpecializationListViewHolder( - override val containerView: View - ) : DelegateViewHolder(containerView), LayoutContainer { + containerView: android.view.View + ) : DelegateViewHolder(containerView) { + private val viewBinding: ItemSpecializationListBinding by viewBinding { ItemSpecializationListBinding.bind(itemView) } private val adapter = DefaultDelegateAdapter() .also { @@ -39,7 +39,7 @@ class SpecializationListAdapterDelegate( init { val rowCount = context.resources.getInteger(R.integer.specializations_default_rows) - specializationListRecycler.layoutManager = + viewBinding.specializationListRecycler.layoutManager = TableLayoutManager( context, horizontalSpanCount = context.resources.getInteger(R.integer.specializations_default_columns), @@ -47,14 +47,14 @@ class SpecializationListAdapterDelegate( orientation = LinearLayoutManager.HORIZONTAL, reverseLayout = false ) - specializationListRecycler.setRecycledViewPool(sharedViewPool) - specializationListRecycler.setHasFixedSize(true) - specializationListRecycler.adapter = adapter + viewBinding.specializationListRecycler.setRecycledViewPool(sharedViewPool) + viewBinding.specializationListRecycler.setHasFixedSize(true) + viewBinding.specializationListRecycler.adapter = adapter val snapHelper = LinearSnapHelper() - snapHelper.attachToRecyclerView(specializationListRecycler) + snapHelper.attachToRecyclerView(viewBinding.specializationListRecycler) - specializationInfoAction.setOnClickListener { + viewBinding.specializationInfoAction.setOnClickListener { onOpenLinkInWeb(context.getString(R.string.specialization_url)) } } diff --git a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/StoriesAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/StoriesAdapterDelegate.kt index f3f992d3bf..84d4cc2e22 100644 --- a/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/StoriesAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/catalog/ui/adapter/delegate/StoriesAdapterDelegate.kt @@ -1,10 +1,10 @@ package org.stepik.android.view.catalog.ui.adapter.delegate -import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.view_stories_container.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ViewStoriesContainerBinding import org.stepic.droid.features.stories.ui.adapter.StoriesAdapter import org.stepik.android.presentation.stories.StoriesFeature import org.stepik.android.view.catalog.model.CatalogItem @@ -22,9 +22,10 @@ class StoriesAdapterDelegate( override fun onCreateViewHolder(parent: ViewGroup): DelegateViewHolder = StoriesViewHolder(createView(parent, R.layout.view_stories_container), onStoryClicked = onStoryClicked) - class StoriesViewHolder(root: View, onStoryClicked: (Story, Int) -> Unit) : DelegateViewHolder(root) { - private val storiesPlaceholder = root.storiesContainerLoadingPlaceholder - val storiesRecycler = root.storiesRecycler + class StoriesViewHolder(root: android.view.View, onStoryClicked: (Story, Int) -> Unit) : DelegateViewHolder(root) { + private val viewBinding: ViewStoriesContainerBinding by viewBinding { ViewStoriesContainerBinding.bind(itemView) } + private val storiesPlaceholder = viewBinding.storiesContainerLoadingPlaceholder + val storiesRecycler = viewBinding.storiesRecycler val storiesAdapter = StoriesAdapter(onStoryClicked = onStoryClicked) private val viewStateDelegate = ViewStateDelegate() diff --git a/app/src/main/java/org/stepik/android/view/catalog/ui/delegate/CatalogBlockHeaderDelegate.kt b/app/src/main/java/org/stepik/android/view/catalog/ui/delegate/CatalogBlockHeaderDelegate.kt index cd5d575693..01de401329 100644 --- a/app/src/main/java/org/stepik/android/view/catalog/ui/delegate/CatalogBlockHeaderDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/catalog/ui/delegate/CatalogBlockHeaderDelegate.kt @@ -2,29 +2,30 @@ package org.stepik.android.view.catalog.ui.delegate import android.view.View import androidx.core.view.isVisible -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.header_catalog_block.* +import org.stepic.droid.databinding.HeaderCatalogBlockBinding import org.stepik.android.domain.catalog.model.CatalogBlock class CatalogBlockHeaderDelegate( - override val containerView: View, + private val rootView: View, onClickListener: View.OnClickListener? = null -) : LayoutContainer { +) { + private val binding: HeaderCatalogBlockBinding = HeaderCatalogBlockBinding.bind(rootView) + init { - onClickListener?.let { containerView.setOnClickListener(it) } - containerViewAll.isVisible = onClickListener != null + onClickListener?.let { rootView.setOnClickListener(it) } + binding.containerViewAll.isVisible = onClickListener != null } fun setInformation(data: CatalogBlock) { - containerView.isVisible = data.isTitleVisible - containerTitle.text = data.title + rootView.isVisible = data.isTitleVisible + binding.containerTitle.text = data.title - containerDescription.text = data.description - containerDescription.isVisible = data.description.isNotEmpty() + binding.containerDescription.text = data.description + binding.containerDescription.isVisible = data.description.isNotEmpty() } fun setCount(countText: String) { - containerCarouselCount.isVisible = true - containerCarouselCount.text = countText + binding.containerCarouselCount.isVisible = true + binding.containerCarouselCount.text = countText } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/catalog/ui/fragment/CatalogFragment.kt b/app/src/main/java/org/stepik/android/view/catalog/ui/fragment/CatalogFragment.kt index a5dbb40d53..4a1a2244f3 100644 --- a/app/src/main/java/org/stepik/android/view/catalog/ui/fragment/CatalogFragment.kt +++ b/app/src/main/java/org/stepik/android/view/catalog/ui/fragment/CatalogFragment.kt @@ -15,11 +15,9 @@ import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager +import by.kirich1409.viewbindingdelegate.viewBinding +import com.google.android.material.appbar.MaterialToolbar import com.google.firebase.remoteconfig.FirebaseRemoteConfig -import kotlinx.android.synthetic.main.fragment_catalog.* -import kotlinx.android.synthetic.main.item_catalog_rubricator.view.rubricatorCard -import kotlinx.android.synthetic.main.view_catalog_search_toolbar.* -import kotlinx.android.synthetic.main.view_centered_toolbar.* import org.stepic.droid.R import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.analytic.Analytic @@ -28,6 +26,7 @@ import org.stepic.droid.configuration.RemoteConfig import org.stepic.droid.core.ScreenManager import org.stepic.droid.core.presenters.SearchSuggestionsPresenter import org.stepic.droid.core.presenters.contracts.SearchSuggestionsView +import org.stepic.droid.databinding.FragmentCatalogBinding import org.stepic.droid.databinding.ItemBannerBinding import org.stepic.droid.databinding.ItemCatalogRubricatorBinding import org.stepic.droid.features.stories.ui.activity.StoriesActivity @@ -148,6 +147,8 @@ class CatalogFragment : // This workaround is necessary, because onFocus get activated multiple times private var searchEventLogged: Boolean = false + private val catalogBinding: FragmentCatalogBinding by viewBinding(FragmentCatalogBinding::bind) + private val catalogViewModel: CatalogViewModel by reduxViewModel(this) { viewModelFactory } private var catalogItemAdapter: DefaultDelegateAdapter = DefaultDelegateAdapter() @@ -155,6 +156,18 @@ class CatalogFragment : private val progressDialogFragment: DialogFragment = LoadingProgressDialogFragment.newInstance() + private val searchViewToolbar: AutoCompleteSearchView + inline get() = catalogBinding.catalogSearchToolbar.searchViewToolbar + + private val backIcon: ImageView + inline get() = catalogBinding.catalogSearchToolbar.backIcon + + private val filterIcon: ImageView + inline get() = catalogBinding.catalogSearchToolbar.filterIcon + + private val centeredToolbar: MaterialToolbar + inline get() = catalogBinding.catalogSearchToolbar.centeredToolbar.centeredToolbar + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) injectComponent() @@ -191,7 +204,10 @@ class CatalogFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initCenteredToolbar(R.string.catalog_title, showHomeButton = false) + + // Views from included layouts are not in FragmentCatalogBinding — use findViewById searchIcon = searchViewToolbar.findViewById(androidx.appcompat.R.id.search_mag_icon) as ImageView + setupSearchBar() catalogItemAdapter += StoriesAdapterDelegate( @@ -257,7 +273,7 @@ class CatalogFragment : catalogItemAdapter += buildBannerBlockAdapterDelegate() catalogItemAdapter += buildRubricatorAdapterDelegate() - with(catalogRecyclerView) { + with(catalogBinding.catalogRecyclerView) { adapter = catalogItemAdapter layoutManager = LinearLayoutManager(context) itemAnimator = null @@ -274,8 +290,7 @@ class CatalogFragment : SharedTransitionsManager.registerTransitionDelegate(CATALOG_STORIES_KEY, object : SharedTransitionContainerDelegate { override fun getSharedView(position: Int): View? { - if (catalogRecyclerView == null) return null - val storiesViewHolder = catalogRecyclerView.findViewHolderForAdapterPosition( + val storiesViewHolder = catalogBinding.catalogRecyclerView.findViewHolderForAdapterPosition( CATALOG_STORIES_INDEX ) as? StoriesAdapterDelegate.StoriesViewHolder ?: return null @@ -288,8 +303,7 @@ class CatalogFragment : } override fun onPositionChanged(position: Int) { - if (catalogRecyclerView == null) return - val storiesViewHolder = catalogRecyclerView.findViewHolderForAdapterPosition( + val storiesViewHolder = catalogBinding.catalogRecyclerView.findViewHolderForAdapterPosition( CATALOG_STORIES_INDEX ) as? StoriesAdapterDelegate.StoriesViewHolder ?: return @@ -306,7 +320,7 @@ class CatalogFragment : SharedTransitionsManager.registerTransitionDelegate(CATALOG_DEEPLINK_STORY_KEY, object : SharedTransitionContainerDelegate { override fun getSharedView(position: Int): View? = - storyDeepLinkMockView + catalogBinding.storyDeepLinkMockView override fun onPositionChanged(position: Int) {} }) @@ -370,7 +384,7 @@ class CatalogFragment : listOf() } - catalogRecyclerView.post { + catalogBinding.catalogRecyclerView.post { val catalogItems = if (state.blocksState is CatalogFeature.BlocksState.Content) { resolveCatalogItems(bannerBlocks, collectionCatalogItems) @@ -409,7 +423,7 @@ class CatalogFragment : } private fun showStories(position: Int) { - val storiesViewHolder = catalogRecyclerView.findViewHolderForAdapterPosition( + val storiesViewHolder = catalogBinding.catalogRecyclerView.findViewHolderForAdapterPosition( CATALOG_STORIES_INDEX ) as? StoriesAdapterDelegate.StoriesViewHolder @@ -452,7 +466,7 @@ class CatalogFragment : } override fun onSyncFilterQueryWithParent(filterQuery: CourseListFilterQuery) { - val query = searchViewToolbar.query.toString() + val query = catalogBinding.catalogSearchToolbar.searchViewToolbar.query.toString() val intent = createSearchViewIntent(query, filterQuery) searchSuggestionsPresenter.onQueryTextSubmit(query) collapseSearchView() @@ -460,7 +474,7 @@ class CatalogFragment : } override fun setSuggestions(suggestions: List, source: SearchQuerySource) { - searchViewToolbar.setSuggestions(suggestions, source) + catalogBinding.catalogSearchToolbar.searchViewToolbar.setSuggestions(suggestions, source) } private fun logStoryEvent(story: Story) { @@ -480,13 +494,15 @@ class CatalogFragment : private fun setupSearchBar() { centeredToolbar.isVisible = false filterIcon.isVisible = true - searchViewToolbar.isVisible = true - searchViewToolbar.onActionViewExpanded() - searchViewToolbar.clearFocus() - searchViewToolbar.setIconifiedByDefault(false) - setupSearchView(searchViewToolbar) - searchViewToolbar.setFocusCallback(this) - searchViewToolbar.setSuggestionClickCallback(this) + catalogBinding.catalogSearchToolbar.searchViewToolbar.let { searchViewToolbar -> + searchViewToolbar.isVisible = true + searchViewToolbar.onActionViewExpanded() + searchViewToolbar.clearFocus() + searchViewToolbar.setIconifiedByDefault(false) + setupSearchView(searchViewToolbar) + searchViewToolbar.setFocusCallback(this) + searchViewToolbar.setSuggestionClickCallback(this) + } backIcon.setOnClickListener { collapseSearchView() } @@ -499,7 +515,7 @@ class CatalogFragment : private fun setupSearchView(searchView: AutoCompleteSearchView?) { searchView?.let { - it.initSuggestions(catalogContainer) + it.initSuggestions(catalogBinding.catalogContainer) it.setCloseIconDrawableRes(CloseIconHolder.getCloseIconDrawableRes()) it.setSearchable(requireActivity()) @@ -586,7 +602,7 @@ class CatalogFragment : ) { val rubricatorBinding = ItemCatalogRubricatorBinding.bind(this.itemView) - rubricatorBinding.root.rubricatorCard.setOnClickListener { + rubricatorBinding.rubricatorCard.setOnClickListener { (item as? CatalogItem.Rubricator)?.let { rubricatorBlock -> if (rubricatorBlock.state is FeaturesFeature.State.Success) { screenManager.showRubricator(requireContext(), rubricatorBlock.state.rubricatorUrl) @@ -594,4 +610,4 @@ class CatalogFragment : } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/certificate/ui/activity/CertificatesActivity.kt b/app/src/main/java/org/stepik/android/view/certificate/ui/activity/CertificatesActivity.kt index fba1e3ff14..bcfbe1022d 100644 --- a/app/src/main/java/org/stepik/android/view/certificate/ui/activity/CertificatesActivity.kt +++ b/app/src/main/java/org/stepik/android/view/certificate/ui/activity/CertificatesActivity.kt @@ -4,17 +4,16 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.view.MenuItem +import android.view.View import androidx.activity.viewModels import androidx.fragment.app.DialogFragment import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.activity_certificates.* -import kotlinx.android.synthetic.main.empty_certificates.* -import kotlinx.android.synthetic.main.error_no_connection_with_button.* -import kotlinx.android.synthetic.main.progress_bar_on_empty_screen.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.base.App import org.stepic.droid.base.FragmentActivityBase +import org.stepic.droid.databinding.ActivityCertificatesBinding import org.stepic.droid.model.CertificateListItem import org.stepic.droid.ui.dialogs.CertificateShareDialogFragment import org.stepic.droid.ui.dialogs.LoadingProgressDialogFragment @@ -65,6 +64,8 @@ class CertificatesActivity : private val viewStateDelegate = ViewStateDelegate() + private val binding: ActivityCertificatesBinding by viewBinding(ActivityCertificatesBinding::bind) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_certificates) @@ -82,7 +83,7 @@ class CertificatesActivity : isCurrentUser = isCurrentUser ) - with(certificateRecyclerView) { + with(binding.certificateRecyclerView) { adapter = certificatesAdapter layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) @@ -95,9 +96,13 @@ class CertificatesActivity : initViewStateDelegate() - certificateSwipeRefresh.setOnRefreshListener { certificatesPresenter.forceUpdate(userId) } - tryAgain.setOnClickListener { certificatesPresenter.forceUpdate(userId) } - goToCatalog.setOnClickListener { screenManager.showCatalog(this) } + binding.certificateSwipeRefresh.setOnRefreshListener { certificatesPresenter.forceUpdate(userId) } + binding.certificateErrorNoConnection.tryAgain.setOnClickListener { + certificatesPresenter.forceUpdate(userId) + } + binding.certificateEmptyView.goToCatalog.setOnClickListener { + screenManager.showCatalog(this) + } certificatesPresenter.fetchCertificates(userId) } @@ -130,17 +135,21 @@ class CertificatesActivity : } private fun initViewStateDelegate() { - viewStateDelegate.addState(reportEmptyCertificates) - viewStateDelegate.addState(loadProgressbarOnEmptyScreen) - viewStateDelegate.addState(error) - viewStateDelegate.addState(certificateSwipeRefresh, certificateRecyclerView) - viewStateDelegate.addState(certificateSwipeRefresh, certificateRecyclerView) - viewStateDelegate.addState(certificateSwipeRefresh, certificateRecyclerView, loadProgressbarOnEmptyScreen) + viewStateDelegate.addState(binding.certificateEmptyView.reportEmptyCertificates) + viewStateDelegate.addState(binding.certificateProgressBar.loadProgressbarOnEmptyScreen) + viewStateDelegate.addState(binding.certificateErrorNoConnection.error) + viewStateDelegate.addState(binding.certificateSwipeRefresh, binding.certificateRecyclerView) + viewStateDelegate.addState(binding.certificateSwipeRefresh, binding.certificateRecyclerView) + viewStateDelegate.addState( + binding.certificateSwipeRefresh, + binding.certificateRecyclerView, + binding.certificateProgressBar.loadProgressbarOnEmptyScreen + ) } override fun setState(state: CertificatesView.State) { - certificateSwipeRefresh.isRefreshing = false - certificateSwipeRefresh.isEnabled = (state is CertificatesView.State.CertificatesRemote || + binding.certificateSwipeRefresh.isRefreshing = false + binding.certificateSwipeRefresh.isEnabled = (state is CertificatesView.State.CertificatesRemote || state is CertificatesView.State.CertificatesCache || state is CertificatesView.State.NetworkError) viewStateDelegate.switchState(state) @@ -156,7 +165,7 @@ class CertificatesActivity : } override fun showNetworkError() { - root.snackbar(messageRes = R.string.connectionProblems) + binding.root.snackbar(messageRes = R.string.connectionProblems) } override fun setBlockingLoading(isLoading: Boolean) { @@ -168,7 +177,7 @@ class CertificatesActivity : } override fun showChangeNameSuccess() { - root.snackbar(messageRes = R.string.certificate_name_change_snackbar_success) + binding.root.snackbar(messageRes = R.string.certificate_name_change_snackbar_success) } override fun showChangeNameDialogError(certificate: Certificate, attemptedFullName: String) { @@ -199,4 +208,4 @@ class CertificatesActivity : .newInstance(certificate, attemptedFullName) .showIfNotExists(supportFragmentManager, CertificateNameChangeDialog.TAG) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/certificate/ui/adapter/delegate/CertificatesAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/certificate/ui/adapter/delegate/CertificatesAdapterDelegate.kt index a85691fd82..4078603988 100644 --- a/app/src/main/java/org/stepik/android/view/certificate/ui/adapter/delegate/CertificatesAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/certificate/ui/adapter/delegate/CertificatesAdapterDelegate.kt @@ -1,12 +1,12 @@ package org.stepik.android.view.certificate.ui.adapter.delegate -import android.view.View import android.view.ViewGroup import androidx.core.content.ContextCompat import androidx.core.view.isVisible +import by.kirich1409.viewbindingdelegate.viewBinding import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.certificate_item.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.CertificateItemBinding import org.stepic.droid.model.CertificateListItem import org.stepik.android.model.Certificate import ru.nobird.android.ui.adapterdelegates.AdapterDelegate @@ -25,30 +25,24 @@ class CertificatesAdapterDelegate( ViewHolder(createView(parent, R.layout.certificate_item)) private inner class ViewHolder( - root: View - ) : DelegateViewHolder(root) { - - private val certificateTitleView = root.certificate_title - private val certificateIcon = root.certificate_icon - private val certificateGradeView = root.certificate_grade - private val certificateDescription = root.certificate_description - private val certificateShareButton = root.certificate_share_button - private val certificateNameChangeButton = root.certificate_name_change_button + containerView: android.view.View + ) : DelegateViewHolder(containerView) { + private val viewBinding: CertificateItemBinding by viewBinding { CertificateItemBinding.bind(itemView) } private val certificatePlaceholder = ContextCompat.getDrawable(context, R.drawable.general_placeholder) init { - root.setOnClickListener { (itemData as? CertificateListItem.Data)?.let { onItemClick(it.certificate.url ?: "") } } - certificateShareButton.setOnClickListener { onShareButtonClick(itemData as CertificateListItem.Data) } - certificateNameChangeButton.setOnClickListener { (itemData as? CertificateListItem.Data)?.let(onChangeNameClick) } + containerView.setOnClickListener { (itemData as? CertificateListItem.Data)?.let { onItemClick(it.certificate.url ?: "") } } + viewBinding.certificateShareButton.setOnClickListener { onShareButtonClick(itemData as CertificateListItem.Data) } + viewBinding.certificateNameChangeButton.setOnClickListener { (itemData as? CertificateListItem.Data)?.let(onChangeNameClick) } } override fun onBind(data: CertificateListItem) { data as CertificateListItem.Data - certificateTitleView.text = data.title ?: "" + viewBinding.certificateTitle.text = data.title ?: "" - certificateDescription.text = + viewBinding.certificateDescription.text = when (data.certificate.type) { Certificate.Type.DISTINCTION -> context.resources.getString(R.string.certificate_distinction_with_substitution, data.title ?: "") @@ -58,16 +52,16 @@ class CertificatesAdapterDelegate( "" } - certificateGradeView.text = + viewBinding.certificateGrade.text = context.resources.getString(R.string.certificate_result_with_substitution, data.certificate.grade ?: "") - certificateGradeView.isVisible = data.certificate.isWithScore - certificateNameChangeButton.isVisible = data.certificate.editsCount < data.certificate.allowedEditsCount && isCurrentUser + viewBinding.certificateGrade.isVisible = data.certificate.isWithScore + viewBinding.certificateNameChangeButton.isVisible = data.certificate.editsCount < data.certificate.allowedEditsCount && isCurrentUser Glide.with(context) .load(data.coverFullPath ?: "") .placeholder(certificatePlaceholder) - .into(certificateIcon) + .into(viewBinding.certificateIcon) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/certificate/ui/dialog/CertificateNameChangeDialog.kt b/app/src/main/java/org/stepik/android/view/certificate/ui/dialog/CertificateNameChangeDialog.kt index 711f48171a..46f2ccac50 100644 --- a/app/src/main/java/org/stepik/android/view/certificate/ui/dialog/CertificateNameChangeDialog.kt +++ b/app/src/main/java/org/stepik/android/view/certificate/ui/dialog/CertificateNameChangeDialog.kt @@ -3,14 +3,13 @@ package org.stepik.android.view.certificate.ui.dialog import android.app.Dialog import android.content.Context import android.os.Bundle -import android.view.View import android.view.inputmethod.InputMethodManager import androidx.appcompat.app.AlertDialog import androidx.core.widget.doAfterTextChanged import androidx.fragment.app.DialogFragment import com.google.android.material.dialog.MaterialAlertDialogBuilder -import kotlinx.android.synthetic.main.dialog_certificate_name_change.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.DialogCertificateNameChangeBinding import org.stepik.android.model.Certificate import ru.nobird.android.view.base.ui.extension.argument @@ -29,9 +28,9 @@ class CertificateNameChangeDialog : DialogFragment() { private var attemptedFullName: String by argument() override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val view = View.inflate(requireContext(), R.layout.dialog_certificate_name_change, null) - val editTextWrapper = view.certificateChangeNameWrapper - val editText = view.certificateChangeNameEditText + val binding = DialogCertificateNameChangeBinding.inflate(layoutInflater) + val editTextWrapper = binding.certificateChangeNameWrapper + val editText = binding.certificateChangeNameEditText editText.setText(attemptedFullName) editText.doAfterTextChanged { editTextWrapper.error = null } @@ -46,7 +45,7 @@ class CertificateNameChangeDialog : DialogFragment() { } val remainingEdits = certificate.allowedEditsCount - certificate.editsCount - view.certificateChangeNameBody.text = resources.getString( + binding.certificateChangeNameBody.text = resources.getString( R.string.certificate_name_change_dialog_body_warning, resources.getQuantityString( R.plurals.times, @@ -57,7 +56,7 @@ class CertificateNameChangeDialog : DialogFragment() { return MaterialAlertDialogBuilder(requireContext()) .setTitle(R.string.certificate_name_change_dialog_title) - .setView(view) + .setView(binding.root) .setCancelable(false) .setNegativeButton(R.string.cancel) { _, _ -> } .setPositiveButton(R.string.ok, null) @@ -80,4 +79,4 @@ class CertificateNameChangeDialog : DialogFragment() { interface Callback { fun showUpdateCertificateConfirmationDialog(newFullName: String, certificate: Certificate) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/comment/ui/activity/CommentsActivity.kt b/app/src/main/java/org/stepik/android/view/comment/ui/activity/CommentsActivity.kt index f41c154e83..ab154a49dd 100644 --- a/app/src/main/java/org/stepik/android/view/comment/ui/activity/CommentsActivity.kt +++ b/app/src/main/java/org/stepik/android/view/comment/ui/activity/CommentsActivity.kt @@ -5,21 +5,19 @@ import android.content.Intent import android.os.Bundle import android.view.Menu import android.view.MenuItem +import android.widget.TextView import androidx.activity.viewModels import androidx.appcompat.content.res.AppCompatResources import androidx.core.content.ContextCompat import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.SimpleItemAnimator -import kotlinx.android.synthetic.main.activity_comments.* -import kotlinx.android.synthetic.main.empty_comments.* -import kotlinx.android.synthetic.main.empty_comments.view.* -import kotlinx.android.synthetic.main.error_no_connection.* -import kotlinx.android.synthetic.main.view_centered_toolbar.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.base.FragmentActivityBase +import org.stepic.droid.databinding.ActivityCommentsBinding import org.stepic.droid.ui.util.initCenteredToolbar import org.stepic.droid.ui.util.snackbar import org.stepic.droid.util.AppConstants @@ -84,6 +82,8 @@ class CommentsActivity : private val commentsPresenter: CommentsPresenter by viewModels { viewModelFactory } + private val commentsBinding: ActivityCommentsBinding by viewBinding(ActivityCommentsBinding::bind) + private lateinit var viewStateDelegate: ViewStateDelegate private lateinit var commentsViewStateDelegate: ViewStateDelegate private lateinit var commentsAdapter: DefaultDelegateAdapter @@ -113,10 +113,13 @@ class CommentsActivity : }, showHomeButton = true ) - centeredToolbar.overflowIcon = AppCompatResources + commentsBinding.appBarLayout.centeredToolbarContainer.centeredToolbar.overflowIcon = AppCompatResources .getDrawable(this, R.drawable.ic_comments_ordering) ?.setTintList(this, R.attr.colorControlNormal) + val emptyComments = findViewById(R.id.emptyComments) + val reportProblem = findViewById(R.id.reportProblem) + commentsAdapter = DefaultDelegateAdapter() commentsAdapter += CommentPlaceholderAdapterDelegate() commentsAdapter += CommentDataAdapterDelegate( @@ -155,7 +158,7 @@ class CommentsActivity : ) commentsAdapter += CommentLoadMoreRepliesAdapterDelegate(commentsPresenter::onLoadMoreReplies) - with(commentsRecycler) { + with(commentsBinding.commentsRecycler) { adapter = commentsAdapter layoutManager = LinearLayoutManager(context) @@ -181,23 +184,23 @@ class CommentsActivity : viewStateDelegate = ViewStateDelegate() viewStateDelegate.addState() - viewStateDelegate.addState(commentsRecycler) + viewStateDelegate.addState(commentsBinding.commentsRecycler) viewStateDelegate.addState(reportProblem) - viewStateDelegate.addState(commentsRecycler, emptyComments, composeCommentButton) + viewStateDelegate.addState(commentsBinding.commentsRecycler, emptyComments, commentsBinding.composeCommentButton) commentsViewStateDelegate = ViewStateDelegate() - commentsViewStateDelegate.addState(commentsRecycler) - commentsViewStateDelegate.addState(commentsRecycler) + commentsViewStateDelegate.addState(commentsBinding.commentsRecycler) + commentsViewStateDelegate.addState(commentsBinding.commentsRecycler) commentsViewStateDelegate.addState(emptyComments) setDataToPresenter() if (discussionThread.thread == DiscussionThread.THREAD_SOLUTIONS) { - emptyComments.placeholderMessage.setText(R.string.step_solutions_empty) + emptyComments.findViewById(R.id.placeholderMessage).setText(R.string.step_solutions_empty) } - composeCommentButton.setOnClickListener { commentsPresenter.onComposeCommentClicked(step) } - commentsSwipeRefresh.setOnRefreshListener { setDataToPresenter(forceUpdate = true) } + commentsBinding.composeCommentButton.setOnClickListener { commentsPresenter.onComposeCommentClicked(step) } + commentsBinding.commentsSwipeRefresh.setOnRefreshListener { setDataToPresenter(forceUpdate = true) } } private fun injectComponent() { @@ -258,8 +261,8 @@ class CommentsActivity : } override fun setState(state: CommentsView.State) { - commentsSwipeRefresh.isRefreshing = false - commentsSwipeRefresh.isEnabled = + commentsBinding.commentsSwipeRefresh.isRefreshing = false + commentsBinding.commentsSwipeRefresh.isEnabled = state is CommentsView.State.NetworkError || state is CommentsView.State.DiscussionLoaded @@ -320,24 +323,24 @@ class CommentsActivity : } override fun focusDiscussion(discussionId: Long) { - commentsRecycler.post { + commentsBinding.commentsRecycler.post { val itemIndex = commentsAdapter .items .indexOfFirst { it is CommentItem.Data && it.id == discussionId } if (itemIndex > 0) { - commentsRecycler.layoutManager + commentsBinding.commentsRecycler.layoutManager ?.scrollToPosition(itemIndex) } } } override fun showNetworkError() { - root.snackbar(messageRes = R.string.no_connection) + commentsBinding.root.snackbar(messageRes = R.string.no_connection) } override fun showAuthRequired() { - root.snackbar(messageRes = R.string.comment_auth_required) + commentsBinding.root.snackbar(messageRes = R.string.comment_auth_required) } override fun onCommentReplaced(commentsData: CommentsData, isCommentCreated: Boolean) { @@ -361,4 +364,4 @@ class CommentsActivity : return hasQuiz && isTeacher } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/comment/ui/adapter/delegate/CommentDataAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/comment/ui/adapter/delegate/CommentDataAdapterDelegate.kt index 7317c2645c..237bd7069d 100644 --- a/app/src/main/java/org/stepik/android/view/comment/ui/adapter/delegate/CommentDataAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/comment/ui/adapter/delegate/CommentDataAdapterDelegate.kt @@ -10,9 +10,9 @@ import androidx.appcompat.widget.PopupMenu import androidx.core.view.isVisible import androidx.core.widget.TextViewCompat import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.item_comment.view.* -import kotlinx.android.synthetic.main.layout_comment_actions.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemCommentBinding import org.stepik.android.view.glide.ui.extension.wrapWithGlide import org.stepic.droid.util.DateTimeHelper import org.stepic.droid.util.resolveColorAttribute @@ -38,23 +38,9 @@ class CommentDataAdapterDelegate( ViewHolder(createView(parent, R.layout.item_comment)) private inner class ViewHolder(root: View) : DelegateViewHolder(root), View.OnClickListener { - private val commentUserIcon = root.commentUserIcon - private val commentUserIconWrapper = commentUserIcon.wrapWithGlide() - private val commentUserName = root.commentUserName + private val viewBinding: ItemCommentBinding by viewBinding { ItemCommentBinding.bind(root) } - private val commentText = root.commentText - private val commentMenu = root.commentMenu - private val commentTags = root.commentTags - - private val commentTime = root.commentTime - private val commentReply = root.commentReply - private val commentLike = root.commentLike - private val commentDislike = root.commentDislike - - private val commentSolution = root.commentSolution - - private val replyOffset = - context.resources.getDimensionPixelOffset(R.dimen.comment_item_reply_offset) + private val commentUserIconWrapper = viewBinding.commentUserIcon.wrapWithGlide() private val voteStatusViewStateDelegate: ViewStateDelegate = ViewStateDelegate() @@ -62,23 +48,23 @@ class CommentDataAdapterDelegate( private val commentTagsAdapter = DefaultDelegateAdapter() init { - TextViewCompat.setLineHeight(commentText.textView, context.resources.getDimensionPixelOffset(R.dimen.comment_item_text_line)) + TextViewCompat.setLineHeight(viewBinding.commentText.textView, context.resources.getDimensionPixelOffset(R.dimen.comment_item_text_line)) - commentReply.setOnClickListener(this) - commentLike.setOnClickListener(this) - commentDislike.setOnClickListener(this) - commentMenu.setOnClickListener(this) - commentSolution.setOnClickListener(this) + viewBinding.commentActions.commentReply.setOnClickListener(this) + viewBinding.commentActions.commentLike.setOnClickListener(this) + viewBinding.commentActions.commentDislike.setOnClickListener(this) + viewBinding.commentMenu.setOnClickListener(this) + viewBinding.commentSolution.setOnClickListener(this) - commentUserIcon.setOnClickListener(this) - commentUserName.setOnClickListener(this) + viewBinding.commentUserIcon.setOnClickListener(this) + viewBinding.commentUserName.setOnClickListener(this) - voteStatusViewStateDelegate.addState(commentLike, commentDislike) - voteStatusViewStateDelegate.addState(root.commentVoteProgress) - voteStatusViewStateDelegate.addState(commentLike, commentDislike) + voteStatusViewStateDelegate.addState(viewBinding.commentActions.commentLike, viewBinding.commentActions.commentDislike) + voteStatusViewStateDelegate.addState(viewBinding.commentActions.commentVoteProgress) + voteStatusViewStateDelegate.addState(viewBinding.commentActions.commentLike, viewBinding.commentActions.commentDislike) commentTagsAdapter += CommentTagsAdapterDelegate() - with(commentTags) { + with(viewBinding.commentTags) { itemAnimator = null isNestedScrollingEnabled = false layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) @@ -95,18 +81,18 @@ class CommentDataAdapterDelegate( if (data.comment.parent == null) { 0 } else { - replyOffset + context.resources.getDimensionPixelOffset(R.dimen.comment_item_reply_offset) } } itemView.isActivated = data.isFocused - commentUserName.text = data.user.fullName + viewBinding.commentUserName.text = data.user.fullName commentUserIconWrapper.setImagePath(data.user.avatar ?: "", AppCompatResources.getDrawable(context, R.drawable.general_placeholder)) - commentText.latexData = data.textData + viewBinding.commentText.latexData = data.textData - commentMenu.isVisible = + viewBinding.commentMenu.isVisible = data.comment.actions?.delete == true || data.comment.actions?.edit == true commentTagsAdapter.items = listOfNotNull( @@ -115,21 +101,21 @@ class CommentDataAdapterDelegate( CommentTag.PINNED.takeIf { data.comment.isPinned }, CommentTag.MODERATOR.takeIf { data.comment.userRole == UserRole.MODERATOR } ) - commentTags.isVisible = commentTagsAdapter.itemCount > 0 + viewBinding.commentTags.isVisible = commentTagsAdapter.itemCount > 0 - commentTime.text = DateMapper.mapToRelativeDate(context, DateTimeHelper.nowUtc(), data.comment.time?.time ?: 0) + viewBinding.commentActions.commentTime.text = DateMapper.mapToRelativeDate(context, DateTimeHelper.nowUtc(), data.comment.time?.time ?: 0) voteStatusViewStateDelegate.switchState(data.voteStatus) - commentLike.text = data.comment.epicCount.toString() - commentDislike.text = data.comment.abuseCount.toString() + viewBinding.commentActions.commentLike.text = data.comment.epicCount.toString() + viewBinding.commentActions.commentDislike.text = data.comment.abuseCount.toString() - commentLike.isEnabled = data.comment.actions?.vote == true && data.voteStatus is CommentItem.Data.VoteStatus.Resolved - commentDislike.isEnabled = data.comment.actions?.vote == true && data.voteStatus is CommentItem.Data.VoteStatus.Resolved + viewBinding.commentActions.commentLike.isEnabled = data.comment.actions?.vote == true && data.voteStatus is CommentItem.Data.VoteStatus.Resolved + viewBinding.commentActions.commentDislike.isEnabled = data.comment.actions?.vote == true && data.voteStatus is CommentItem.Data.VoteStatus.Resolved when (data.voteStatus) { is CommentItem.Data.VoteStatus.Resolved -> { - commentLike.alpha = + viewBinding.commentActions.commentLike.alpha = when (data.voteStatus.vote.value) { Vote.Value.LIKE -> 1f @@ -137,7 +123,7 @@ class CommentDataAdapterDelegate( 0.5f } - commentDislike.alpha = + viewBinding.commentActions.commentDislike.alpha = when (data.voteStatus.vote.value) { Vote.Value.DISLIKE -> 1f @@ -146,14 +132,14 @@ class CommentDataAdapterDelegate( } } is CommentItem.Data.VoteStatus.Unavailable -> { - commentLike.alpha = 0.5f - commentDislike.alpha = 0.5f + viewBinding.commentActions.commentLike.alpha = 0.5f + viewBinding.commentActions.commentDislike.alpha = 0.5f } else -> {} } // solution - commentSolution.setSubmission(data.solution?.submission) + viewBinding.commentSolution.setSubmission(data.solution?.submission) } private fun showItemMenu(view: View) { @@ -243,4 +229,4 @@ class CommentDataAdapterDelegate( fun onEditCommentClicked(commentDataItem: CommentItem.Data) fun onRemoveCommentClicked(commentDataItem: CommentItem.Data) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/comment/ui/adapter/delegate/CommentLoadMoreRepliesAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/comment/ui/adapter/delegate/CommentLoadMoreRepliesAdapterDelegate.kt index c7663b42ac..95b977d2a1 100644 --- a/app/src/main/java/org/stepik/android/view/comment/ui/adapter/delegate/CommentLoadMoreRepliesAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/comment/ui/adapter/delegate/CommentLoadMoreRepliesAdapterDelegate.kt @@ -1,9 +1,9 @@ package org.stepik.android.view.comment.ui.adapter.delegate -import android.view.View import android.view.ViewGroup -import kotlinx.android.synthetic.main.item_comment_load_more_replies.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemCommentLoadMoreRepliesBinding import org.stepik.android.presentation.comment.model.CommentItem import ru.nobird.android.ui.adapterdelegates.AdapterDelegate import ru.nobird.android.ui.adapterdelegates.DelegateViewHolder @@ -17,18 +17,17 @@ class CommentLoadMoreRepliesAdapterDelegate( override fun onCreateViewHolder(parent: ViewGroup): DelegateViewHolder = ViewHolder(createView(parent, R.layout.item_comment_load_more_replies)) - private inner class ViewHolder(root: View) : DelegateViewHolder(root) { - - private val commentLoadMoreText = root.commentLoadMoreText + private inner class ViewHolder(containerView: android.view.View) : DelegateViewHolder(containerView) { + private val viewBinding: ItemCommentLoadMoreRepliesBinding by viewBinding { ItemCommentLoadMoreRepliesBinding.bind(itemView) } init { - root.setOnClickListener { (itemData as? CommentItem.LoadMoreReplies)?.let(onItemClick) } + containerView.setOnClickListener { (itemData as? CommentItem.LoadMoreReplies)?.let(onItemClick) } } override fun onBind(data: CommentItem) { data as CommentItem.LoadMoreReplies - commentLoadMoreText.text = context.getString(R.string.comments_load_more_replies, + viewBinding.commentLoadMoreText.text = context.getString(R.string.comments_load_more_replies, context.resources.getQuantityString(R.plurals.replies, data.count, data.count)) } } diff --git a/app/src/main/java/org/stepik/android/view/comment/ui/dialog/ComposeCommentDialogFragment.kt b/app/src/main/java/org/stepik/android/view/comment/ui/dialog/ComposeCommentDialogFragment.kt index 2e844c85e5..d3fd5f7da7 100644 --- a/app/src/main/java/org/stepik/android/view/comment/ui/dialog/ComposeCommentDialogFragment.kt +++ b/app/src/main/java/org/stepik/android/view/comment/ui/dialog/ComposeCommentDialogFragment.kt @@ -10,15 +10,15 @@ import android.view.View import android.view.ViewGroup import android.view.Window import android.view.inputmethod.InputMethodManager +import androidx.appcompat.widget.Toolbar import androidx.core.view.isVisible import androidx.fragment.app.DialogFragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider -import kotlinx.android.synthetic.main.dialog_compose_comment.* -import kotlinx.android.synthetic.main.error_no_connection_with_button.* -import kotlinx.android.synthetic.main.view_centered_toolbar.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.base.App +import org.stepic.droid.databinding.DialogComposeCommentBinding import org.stepic.droid.ui.dialogs.DiscardTextDialogFragment import org.stepic.droid.ui.dialogs.LoadingProgressDialogFragment import org.stepic.droid.ui.util.setTintedNavigationIcon @@ -77,6 +77,8 @@ class ComposeCommentDialogFragment : private val composeCommentPresenter: ComposeCommentPresenter by viewModels { viewModelFactory } + private val composeCommentBinding: DialogComposeCommentBinding by viewBinding(DialogComposeCommentBinding::bind) + private var discussionThread: DiscussionThread by argument() private var step: Step by argument() private val parent: Long? by lazy { arguments?.getLong(ARG_PARENT, -1)?.takeIf { it != -1L } } @@ -119,12 +121,17 @@ class ComposeCommentDialogFragment : inflater.inflate(R.layout.dialog_compose_comment, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + val error = view.findViewById(R.id.error) + val tryAgain = view.findViewById(R.id.tryAgain) + val centeredToolbar = composeCommentBinding.appBarLayout.centeredToolbarContainer.centeredToolbar + val centeredToolbarTitle = composeCommentBinding.appBarLayout.centeredToolbarContainer.centeredToolbarTitle + viewStateDelegate = ViewStateDelegate() - viewStateDelegate.addState(commentContent) - viewStateDelegate.addState(commentContent) + viewStateDelegate.addState(composeCommentBinding.commentContent) + viewStateDelegate.addState(composeCommentBinding.commentContent) viewStateDelegate.addState(error) - viewStateDelegate.addState(commentContent, commentSolution) - viewStateDelegate.addState(commentContent) + viewStateDelegate.addState(composeCommentBinding.commentContent, composeCommentBinding.commentSolution) + viewStateDelegate.addState(composeCommentBinding.commentContent) centeredToolbarTitle.setText( if (discussionThread.thread == DiscussionThread.THREAD_SOLUTIONS) R.string.solutions_compose_title else R.string.comment_compose_title) @@ -141,22 +148,22 @@ class ComposeCommentDialogFragment : } if (savedInstanceState == null) { - commentEditText.setText(comment?.text) + composeCommentBinding.commentEditText.setText(comment?.text) requestFocusNewComment(comment?.text ?: "") } - invalidateMenuState() + invalidateMenuState(centeredToolbar) - commentEditText.addTextChangedListener(object : TextWatcher { + composeCommentBinding.commentEditText.addTextChangedListener(object : TextWatcher { override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun afterTextChanged(s: Editable?) { - invalidateMenuState() + invalidateMenuState(centeredToolbar) } }) tryAgain.setOnClickListener { setDataToPresenter(forceUpdate = true) } - commentSolution.setOnClickListener { showSubmissions() } + composeCommentBinding.commentSolution.setOnClickListener { showSubmissions() } setDataToPresenter() } @@ -164,9 +171,9 @@ class ComposeCommentDialogFragment : composeCommentPresenter.onData(discussionThread, step.id, parent, submission, forceUpdate) } - private fun invalidateMenuState() { + private fun invalidateMenuState(centeredToolbar: Toolbar) { centeredToolbar.menu.findItem(R.id.comment_submit)?.isEnabled = - !commentEditText.text.isNullOrEmpty() + !composeCommentBinding.commentEditText.text.isNullOrEmpty() } override fun onStart() { @@ -193,10 +200,10 @@ class ComposeCommentDialogFragment : } private fun submit() { - commentEditText.hideKeyboard() + composeCommentBinding.commentEditText.hideKeyboard() val oldComment = comment - val text = commentEditText.text?.toString() + val text = composeCommentBinding.commentEditText.text?.toString() if (oldComment == null) { val comment = Comment( @@ -221,9 +228,9 @@ class ComposeCommentDialogFragment : when (state) { is ComposeCommentView.State.Create -> { - commentSolution.isEnabled = comment == null - commentSolution.setSubmission(state.submission, showArrow = commentSolution.isEnabled) - commentSolutionSeparator.isVisible = state.submission != null + composeCommentBinding.commentSolution.isEnabled = comment == null + composeCommentBinding.commentSolution.setSubmission(state.submission, showArrow = composeCommentBinding.commentSolution.isEnabled) + composeCommentBinding.commentSolutionSeparator.root.isVisible = state.submission != null } is ComposeCommentView.State.Complete -> { @@ -248,7 +255,7 @@ class ComposeCommentDialogFragment : } private fun onClose() { - if (commentEditText.text.isNullOrEmpty() || commentEditText.text.toString() == comment?.text) { + if (composeCommentBinding.commentEditText.text.isNullOrEmpty() || composeCommentBinding.commentEditText.text.toString() == comment?.text) { super.dismiss() } else { if (childFragmentManager.findFragmentByTag(DiscardTextDialogFragment.TAG) == null) { @@ -261,9 +268,9 @@ class ComposeCommentDialogFragment : private fun requestFocusNewComment(text: String) { if (text.isNotEmpty()) return - commentEditText.post { - commentEditText.requestFocus() - (requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).showSoftInput(commentEditText, InputMethodManager.SHOW_IMPLICIT) + composeCommentBinding.commentEditText.post { + composeCommentBinding.commentEditText.requestFocus() + (requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).showSoftInput(composeCommentBinding.commentEditText, InputMethodManager.SHOW_IMPLICIT) } } @@ -278,4 +285,4 @@ class ComposeCommentDialogFragment : interface Callback { fun onCommentReplaced(commentsData: CommentsData, isCommentCreated: Boolean) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/comment/ui/dialog/SolutionCommentDialogFragment.kt b/app/src/main/java/org/stepik/android/view/comment/ui/dialog/SolutionCommentDialogFragment.kt index 5f859d3f48..6897258ff5 100644 --- a/app/src/main/java/org/stepik/android/view/comment/ui/dialog/SolutionCommentDialogFragment.kt +++ b/app/src/main/java/org/stepik/android/view/comment/ui/dialog/SolutionCommentDialogFragment.kt @@ -10,14 +10,11 @@ import androidx.annotation.LayoutRes import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.fragment.app.DialogFragment -import kotlinx.android.synthetic.main.dialog_comment_solution.* -import kotlinx.android.synthetic.main.dialog_comment_solution.view.* -import kotlinx.android.synthetic.main.fragment_step_quiz_unsupported.* -import kotlinx.android.synthetic.main.layout_step_quiz_code.* -import kotlinx.android.synthetic.main.view_centered_toolbar.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.DialogCommentSolutionBinding import org.stepic.droid.ui.util.setCompoundDrawables import org.stepic.droid.ui.util.setTintedNavigationIcon import org.stepic.droid.util.AppConstants @@ -85,6 +82,8 @@ class SolutionCommentDialogFragment : DialogFragment() { private val discussionThread: DiscussionThread? by lazy { arguments?.getParcelable(ARG_DISCUSSION_THREAD) } private var discussionId: Long by argument() + private val solutionBinding: DialogCommentSolutionBinding by viewBinding(DialogCommentSolutionBinding::bind) + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val dialog = Dialog(requireContext(), theme) @@ -108,11 +107,12 @@ class SolutionCommentDialogFragment : DialogFragment() { .inject(this) } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = - inflater.inflate(R.layout.dialog_comment_solution, container, false) - .also { - it.solutionContainer.addView(inflater.inflate(getLayoutResForStep(step.block?.name), it.solutionContainer, false), 1) - } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + val view = inflater.inflate(R.layout.dialog_comment_solution, container, false) + val binding = DialogCommentSolutionBinding.bind(view) + binding.solutionContainer.addView(inflater.inflate(getLayoutResForStep(step.block?.name), binding.solutionContainer, false), 1) + return view + } @LayoutRes private fun getLayoutResForStep(blockName: String?): Int = @@ -147,6 +147,9 @@ class SolutionCommentDialogFragment : DialogFragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + val centeredToolbar = solutionBinding.appBarLayout.centeredToolbarContainer.centeredToolbar + val centeredToolbarTitle = solutionBinding.appBarLayout.centeredToolbarContainer.centeredToolbarTitle + centeredToolbarTitle.text = getString(R.string.comment_solution_pattern, submission.id) centeredToolbar.setNavigationOnClickListener { dismiss() } centeredToolbar.setTintedNavigationIcon(R.drawable.ic_close_dark) @@ -162,12 +165,13 @@ class SolutionCommentDialogFragment : DialogFragment() { if (stepQuizFormDelegate != null) { stepQuizFormDelegate.setState(state) - StepQuizFeedbackBlocksDelegate(stepQuizFeedbackBlocks, isTeacher = false, hasReview = false, onReviewClicked = {}) + StepQuizFeedbackBlocksDelegate(solutionBinding.stepQuizFeedbackBlocks.root, isTeacher = false, hasReview = false, onReviewClicked = {}) .setState(StepQuizFeedbackMapper().mapToStepQuizFeedbackState(step.block?.name, state)) - stepQuizCodeContainer?.updateLayoutParams { topMargin = 0 } - codeStepLayout?.updateLayoutParams { height = ViewGroup.LayoutParams.WRAP_CONTENT } + view.findViewById(R.id.stepQuizCodeContainer)?.updateLayoutParams { topMargin = 0 } + view.findViewById(R.id.codeStepLayout)?.updateLayoutParams { height = ViewGroup.LayoutParams.WRAP_CONTENT } } else { + val stepQuizAction = view.findViewById(R.id.stepQuizAction) stepQuizAction.setOnClickListener { val discussionThread = this.discussionThread val url = @@ -186,9 +190,10 @@ class SolutionCommentDialogFragment : DialogFragment() { bottomMargin = resources.getDimensionPixelOffset(R.dimen.space_normal) } + val stepQuizFeedback = view.findViewById(R.id.stepQuizFeedback) stepQuizFeedback.setCompoundDrawables(start = R.drawable.ic_step_quiz_validation) - stepQuizFeedbackBlocks.isVisible = false + solutionBinding.stepQuizFeedbackBlocks.root.isVisible = false } } @@ -249,4 +254,4 @@ class SolutionCommentDialogFragment : DialogFragment() { window.setWindowAnimations(R.style.ThemeOverlay_AppTheme_Dialog_Fullscreen) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course/ui/activity/CourseActivity.kt b/app/src/main/java/org/stepik/android/view/course/ui/activity/CourseActivity.kt index fc976e1d38..a85bc1d719 100644 --- a/app/src/main/java/org/stepik/android/view/course/ui/activity/CourseActivity.kt +++ b/app/src/main/java/org/stepik/android/view/course/ui/activity/CourseActivity.kt @@ -11,13 +11,10 @@ import androidx.appcompat.app.AppCompatDelegate import androidx.fragment.app.DialogFragment import androidx.lifecycle.ViewModelProvider import androidx.viewpager.widget.ViewPager +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.firebase.remoteconfig.FirebaseRemoteConfig import com.google.firebase.remoteconfig.ktx.get -import kotlinx.android.synthetic.main.activity_course.* -import kotlinx.android.synthetic.main.error_course_not_found.* -import kotlinx.android.synthetic.main.error_no_connection_with_button.* -import kotlinx.android.synthetic.main.header_course.* -import kotlinx.android.synthetic.main.header_course_placeholder.* +import org.stepic.droid.databinding.ActivityCourseBinding import org.stepic.droid.R import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.analytic.Analytic @@ -150,6 +147,8 @@ class CourseActivity : private var viewPagerScrollState: Int = ViewPager.SCROLL_STATE_IDLE + private val courseBinding: ActivityCourseBinding by viewBinding(ActivityCourseBinding::bind) + private var isInSwipeableViewState = false private var hasSavedInstanceState: Boolean = false @@ -173,7 +172,7 @@ class CourseActivity : super.onCreate(savedInstanceState) setContentView(R.layout.activity_course) - setSupportActionBar(courseToolbar) + setSupportActionBar(courseBinding.courseToolbar) val actionBar = this.supportActionBar ?: throw IllegalStateException("support action bar should be set") @@ -195,7 +194,7 @@ class CourseActivity : if (course != null) { courseTitle = course.title.orEmpty() - courseToolbarTitle.text = course.title + courseBinding.courseToolbarTitle.text = course.title } courseId = intent.getLongExtra(EXTRA_COURSE_ID, NO_ID) @@ -224,6 +223,7 @@ class CourseActivity : courseHeaderDelegateFactory .create( courseActivity = this, + courseBinding = courseBinding, coursePresenter = coursePresenter, courseViewSource = courseViewSource, isAuthorized = sharedPreferenceHelper.authResponseFromStore != null, @@ -250,11 +250,11 @@ class CourseActivity : setDataToPresenter(courseViewSource) - courseSwipeRefresh.setOnRefreshListener { + courseBinding.courseSwipeRefresh.setOnRefreshListener { setDataToPresenter(courseViewSource, forceUpdate = true) } - tryAgain.setOnClickListener { setDataToPresenter(courseViewSource, forceUpdate = true) } - goToCatalog.setOnClickListener { + courseBinding.errorNoConnection.tryAgain.setOnClickListener { setDataToPresenter(courseViewSource, forceUpdate = true) } + courseBinding.courseEmpty.goToCatalog.setOnClickListener { screenManager.showCatalog(this) finish() } @@ -296,14 +296,14 @@ class CourseActivity : if (coursePurchaseWebviewSplitTest.currentGroup != CoursePurchaseWebviewSplitTest.Group.InAppWebview) { coursePresenter.handleCoursePurchasePressed() } - coursePager.addOnPageChangeListener(analyticsOnPageChangeListener) + courseBinding.coursePager.addOnPageChangeListener(analyticsOnPageChangeListener) if (!hasSavedInstanceState) { setCurrentTab() } } override fun onPause() { - coursePager.removeOnPageChangeListener(analyticsOnPageChangeListener) + courseBinding.coursePager.removeOnPageChangeListener(analyticsOnPageChangeListener) super.onPause() } @@ -318,40 +318,40 @@ class CourseActivity : .getOrNull(intent.getIntExtra(EXTRA_TAB, -1)) ?: intent.getCourseTabFromDeepLink() - coursePager.currentItem = + courseBinding.coursePager.currentItem = when (tab) { CourseScreenTab.REVIEWS -> 1 CourseScreenTab.NEWS -> 2 CourseScreenTab.SYLLABUS -> 3 else -> 0 } - if (coursePager.currentItem == 0) { + if (courseBinding.coursePager.currentItem == 0) { analyticsOnPageChangeListener.onPageSelected(0) } } private fun initViewPager(courseId: Long, courseTitle: String) { coursePagerAdapter = CoursePagerAdapter(courseId, courseTitle, this, supportFragmentManager) - coursePager.adapter = coursePagerAdapter + courseBinding.coursePager.adapter = coursePagerAdapter val onPageChangeListener = object : ViewPager.SimpleOnPageChangeListener() { override fun onPageScrollStateChanged(scrollState: Int) { viewPagerScrollState = scrollState resolveSwipeRefreshState() } } - coursePager.addOnPageChangeListener(FragmentDelegateScrollStateChangeListener(coursePager, coursePagerAdapter)) - coursePager.addOnPageChangeListener(onPageChangeListener) + courseBinding.coursePager.addOnPageChangeListener(FragmentDelegateScrollStateChangeListener(courseBinding.coursePager, coursePagerAdapter)) + courseBinding.coursePager.addOnPageChangeListener(onPageChangeListener) - courseTabs.setupWithViewPager(coursePager) + courseBinding.courseTabs.setupWithViewPager(courseBinding.coursePager) } private fun initViewStateDelegate() { - viewStateDelegate.addState(courseEmpty) - viewStateDelegate.addState(errorNoConnection) - viewStateDelegate.addState(courseHeader, courseTabs, coursePager) - viewStateDelegate.addState(courseHeader, courseTabs, coursePager) - viewStateDelegate.addState(courseHeaderPlaceholder, courseTabs, coursePager) - viewStateDelegate.addState(courseHeaderPlaceholder, courseTabs, coursePager) + viewStateDelegate.addState(courseBinding.courseEmpty.root) + viewStateDelegate.addState(courseBinding.errorNoConnection.root) + viewStateDelegate.addState(courseBinding.headerCourse.root, courseBinding.courseTabs, courseBinding.coursePager) + viewStateDelegate.addState(courseBinding.headerCourse.root, courseBinding.courseTabs, courseBinding.coursePager) + viewStateDelegate.addState(courseBinding.headerCoursePlaceholder.root, courseBinding.courseTabs, courseBinding.coursePager) + viewStateDelegate.addState(courseBinding.headerCoursePlaceholder.root, courseBinding.courseTabs, courseBinding.coursePager) } override fun onCreateOptionsMenu(menu: Menu): Boolean { @@ -373,13 +373,13 @@ class CourseActivity : } private fun resolveSwipeRefreshState() { - courseSwipeRefresh.isEnabled = + courseBinding.courseSwipeRefresh.isEnabled = viewPagerScrollState == ViewPager.SCROLL_STATE_IDLE && isInSwipeableViewState } override fun setState(state: CourseView.State) { - courseSwipeRefresh.isRefreshing = false + courseBinding.courseSwipeRefresh.isRefreshing = false isInSwipeableViewState = (state is CourseView.State.CourseLoaded || state is CourseView.State.NetworkError) resolveSwipeRefreshState() @@ -469,7 +469,7 @@ class CourseActivity : R.string.course_purchase_billing_no_purchases_to_restore } - coursePager.snackbar(messageRes = errorMessage) + courseBinding.coursePager.snackbar(messageRes = errorMessage) } override fun shareCourse(course: Course) { @@ -496,7 +496,7 @@ class CourseActivity : UserCourseAction.REMOVE_ARCHIVE -> R.string.course_action_archive_remove_success } - coursePager.snackbar(messageRes = successMessage) + courseBinding.coursePager.snackbar(messageRes = successMessage) } override fun showSaveUserCourseError(userCourseAction: UserCourseAction) { @@ -515,7 +515,7 @@ class CourseActivity : UserCourseAction.REMOVE_ARCHIVE -> R.string.course_action_archive_remove_failure } - coursePager.snackbar(messageRes = errorMessage) + courseBinding.coursePager.snackbar(messageRes = errorMessage) } override fun showWishlistActionSuccess(wishlistAction: WishlistAction) { @@ -526,7 +526,7 @@ class CourseActivity : } else { R.string.wishlist_action_remove_success } - coursePager.snackbar(messageRes = successMessage) + courseBinding.coursePager.snackbar(messageRes = successMessage) } override fun showWishlistActionFailure(wishlistAction: WishlistAction) { @@ -537,7 +537,7 @@ class CourseActivity : } else { R.string.wishlist_action_remove_failure } - coursePager.snackbar(messageRes = errorMessage) + courseBinding.coursePager.snackbar(messageRes = errorMessage) } override fun openCoursePurchaseInWeb(courseId: Long, queryParams: Map>?) { @@ -560,7 +560,7 @@ class CourseActivity : if (isAdaptive) { screenManager.continueAdaptiveCourse(this, course) } else { - coursePager.snackbar(messageRes = R.string.course_error_continue_learning) + courseBinding.coursePager.snackbar(messageRes = R.string.course_error_continue_learning) } } diff --git a/app/src/main/java/org/stepik/android/view/course/ui/delegates/CourseHeaderDelegate.kt b/app/src/main/java/org/stepik/android/view/course/ui/delegates/CourseHeaderDelegate.kt index b3a45593eb..fe81a7c59e 100644 --- a/app/src/main/java/org/stepik/android/view/course/ui/delegates/CourseHeaderDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course/ui/delegates/CourseHeaderDelegate.kt @@ -21,11 +21,9 @@ import com.google.android.material.appbar.AppBarLayout import dagger.assisted.Assisted import dagger.assisted.AssistedInject import jp.wasabeef.glide.transformations.BlurTransformation -import kotlinx.android.synthetic.main.activity_course.* -import kotlinx.android.synthetic.main.header_course.* -import kotlinx.android.synthetic.main.view_discounted_purchase_button.* import org.stepic.droid.R import org.stepic.droid.analytic.AmplitudeAnalytic +import org.stepic.droid.databinding.ActivityCourseBinding import org.stepic.droid.analytic.Analytic import org.stepic.droid.analytic.experiments.DiscountButtonAppearanceSplitTest import org.stepic.droid.ui.util.PopupHelper @@ -61,6 +59,7 @@ class CourseHeaderDelegate @AssistedInject constructor( @Assisted private val courseActivity: Activity, + @Assisted private val courseBinding: ActivityCourseBinding, private val analytic: Analytic, @Assisted private val coursePresenter: CoursePresenter, private val discountButtonAppearanceSplitTest: DiscountButtonAppearanceSplitTest, @@ -101,10 +100,10 @@ constructor( private var shareCourseMenuItem: MenuItem? = null private var restorePurchaseCourseMenuItem: MenuItem? = null - private val courseStatsDelegate = CourseStatsDelegate(courseActivity.courseStats) - private val courseProgressDelegate = CourseProgressDelegate(courseActivity.courseProgress, onSubmissionCountClicked, isLocalSubmissionsEnabled) + private val courseStatsDelegate = CourseStatsDelegate(courseBinding.headerCourse.courseStats) + private val courseProgressDelegate = CourseProgressDelegate(courseBinding.headerCourse.courseProgress, onSubmissionCountClicked, isLocalSubmissionsEnabled) - private val courseCollapsingToolbar = courseActivity.courseCollapsingToolbar + private val courseCollapsingToolbar = courseBinding.courseCollapsingToolbar private val viewStateDelegate = ViewStateDelegate() @@ -115,18 +114,16 @@ constructor( } private fun initCollapsingAnimation() { - with(courseActivity) { - courseToolbarScrim.setBackgroundColor(ColorExtensions.colorSurfaceWithElevationOverlay(courseCollapsingToolbar.context, 4)) + courseBinding.courseToolbarScrim.setBackgroundColor(ColorExtensions.colorSurfaceWithElevationOverlay(courseCollapsingToolbar.context, 4)) - courseAppBar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { _, verticalOffset -> - val ratio = abs(verticalOffset).toFloat() / (courseCollapsingToolbar.height - courseToolbar.height) - courseToolbarScrim.alpha = ratio * 1.5f - }) - } + courseBinding.courseAppBar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { _, verticalOffset -> + val ratio = abs(verticalOffset).toFloat() / (courseCollapsingToolbar.height - courseBinding.courseToolbar.height) + courseBinding.courseToolbarScrim.alpha = ratio * 1.5f + }) } private fun initActions() { - with(courseActivity) { + with(courseBinding.headerCourse) { courseEnrollAction.setOnClickListener { coursePresenter.enrollCourse() @@ -171,7 +168,7 @@ constructor( courseHeaderData?.let(::setupBuyAction) } - courseBuyInWebActionDiscounted.setOnClickListener { + courseBuyInWebActionDiscounted.root.setOnClickListener { courseHeaderData?.let(::setupBuyAction) } @@ -195,7 +192,7 @@ constructor( } private fun initViewStateDelegate() { - with(courseActivity) { + with(courseBinding.headerCourse) { viewStateDelegate.addState(courseContinueAction) viewStateDelegate.addState(courseEnrollAction) viewStateDelegate.addState(courseEnrollmentProgress) @@ -208,171 +205,168 @@ constructor( } } - private fun setCourseData(courseHeaderData: CourseHeaderData) = - with(courseActivity) { - val multi = MultiTransformation(BlurTransformation(), CenterCrop()) - Glide - .with(this) - .load(courseHeaderData.cover) - .placeholder(R.drawable.general_placeholder) - .apply(RequestOptions.bitmapTransform(multi)) - .into(courseCover) - - courseToolbarTitle.text = courseHeaderData.title - - val isNeedShowProgress = courseHeaderData.stats.progress != null - courseProgress.isVisible = isNeedShowProgress - courseProgressSeparator.isVisible = isNeedShowProgress - courseStats.isVisible = !isNeedShowProgress - - if (courseHeaderData.stats.progress != null) { - courseProgressDelegate.setProgress(courseHeaderData.stats.progress) - courseProgressDelegate.setSolutionsCount(courseHeaderData.localSubmissionsCount) - } else { - courseStatsDelegate.setStats(courseHeaderData.stats) - } + private fun setCourseData(courseHeaderData: CourseHeaderData) { + val multi = MultiTransformation(BlurTransformation(), CenterCrop()) + Glide + .with(courseActivity) + .load(courseHeaderData.cover) + .placeholder(R.drawable.general_placeholder) + .apply(RequestOptions.bitmapTransform(multi)) + .into(courseBinding.courseCover) + + courseBinding.courseToolbarTitle.text = courseHeaderData.title + + val headerBinding = courseBinding.headerCourse + val isNeedShowProgress = courseHeaderData.stats.progress != null + headerBinding.courseProgress.root.isVisible = isNeedShowProgress + headerBinding.courseProgressSeparator.root.isVisible = isNeedShowProgress + headerBinding.courseStats.root.isVisible = !isNeedShowProgress + + if (courseHeaderData.stats.progress != null) { + courseProgressDelegate.setProgress(courseHeaderData.stats.progress) + courseProgressDelegate.setSolutionsCount(courseHeaderData.localSubmissionsCount) + } else { + courseStatsDelegate.setStats(courseHeaderData.stats) + } - setupWishlistAction(courseHeaderData) - /** - * Purchase setup section - */ + setupWishlistAction(courseHeaderData) + /** + * Purchase setup section + */ - if (courseHeaderData.stats.enrollmentState is EnrollmentState.NotEnrolledMobileTier) { - setupIAP(courseHeaderData) - } else { - setupWeb(courseHeaderData) - } + if (courseHeaderData.stats.enrollmentState is EnrollmentState.NotEnrolledMobileTier) { + setupIAP(courseHeaderData) + } else { + setupWeb(courseHeaderData) + } - courseDefaultPromoInfo.text = courseHeaderData.defaultPromoCode.defaultPromoCodeExpireDate?.let { - val formattedDate = DateTimeHelper.getPrintableDate(it, DateTimeHelper.DISPLAY_DAY_MONTH_PATTERN, TimeZone.getDefault()) - getString(R.string.course_promo_code_date, formattedDate) - } + headerBinding.courseDefaultPromoInfo.text = courseHeaderData.defaultPromoCode.defaultPromoCodeExpireDate?.let { + val formattedDate = DateTimeHelper.getPrintableDate(it, DateTimeHelper.DISPLAY_DAY_MONTH_PATTERN, TimeZone.getDefault()) + courseActivity.getString(R.string.course_promo_code_date, formattedDate) + } - courseDefaultPromoInfo.isVisible = (courseHeaderData.defaultPromoCode.defaultPromoCodeExpireDate?.time ?: -1L) > DateTimeHelper.nowUtc() && - courseHeaderData.course.enrollment == 0L && - (courseHeaderData.deeplinkPromoCode == DeeplinkPromoCode.EMPTY || courseHeaderData.deeplinkPromoCode.name == courseHeaderData.defaultPromoCode.defaultPromoCodeName) + headerBinding.courseDefaultPromoInfo.isVisible = (courseHeaderData.defaultPromoCode.defaultPromoCodeExpireDate?.time ?: -1L) > DateTimeHelper.nowUtc() && + courseHeaderData.course.enrollment == 0L && + (courseHeaderData.deeplinkPromoCode == DeeplinkPromoCode.EMPTY || courseHeaderData.deeplinkPromoCode.name == courseHeaderData.defaultPromoCode.defaultPromoCodeName) - with(courseHeaderData.stats.enrollmentState) { - viewStateDelegate.switchState(this) + with(courseHeaderData.stats.enrollmentState) { + viewStateDelegate.switchState(this) - dropCourseMenuItem?.isVisible = this is EnrollmentState.Enrolled - restorePurchaseCourseMenuItem?.isVisible = this is EnrollmentState.NotEnrolledMobileTier - } + dropCourseMenuItem?.isVisible = this is EnrollmentState.Enrolled + restorePurchaseCourseMenuItem?.isVisible = this is EnrollmentState.NotEnrolledMobileTier + } - courseTryFree.isVisible = courseHeaderData.course.previewLesson != 0L && - courseHeaderData.course.enrollment == 0L && - courseHeaderData.course.isPaid && - (courseHeaderData.stats.enrollmentState is EnrollmentState.NotEnrolledMobileTier || - courseHeaderData.stats.enrollmentState is EnrollmentState.NotEnrolledWeb || - courseHeaderData.stats.enrollmentState is EnrollmentState.NotEnrolledUnavailableIAP) + headerBinding.courseTryFree.isVisible = courseHeaderData.course.previewLesson != 0L && + courseHeaderData.course.enrollment == 0L && + courseHeaderData.course.isPaid && + (courseHeaderData.stats.enrollmentState is EnrollmentState.NotEnrolledMobileTier || + courseHeaderData.stats.enrollmentState is EnrollmentState.NotEnrolledWeb || + courseHeaderData.stats.enrollmentState is EnrollmentState.NotEnrolledUnavailableIAP) - shareCourseMenuItem?.isVisible = true - setupPurchaseFeedback(courseHeaderData) - } + shareCourseMenuItem?.isVisible = true + setupPurchaseFeedback(courseHeaderData) + } private fun setupPurchaseFeedback(courseHeaderData: CourseHeaderData) { - with(courseActivity) { - ViewCompat.setBackgroundTintList(coursePurchaseFeedbackUnder, AppCompatResources.getColorStateList(courseActivity, R.color.black_alpha_30)) - coursePurchaseFeedback.text = - when (courseHeaderData.stats.enrollmentState) { - is EnrollmentState.NotEnrolledUnavailableIAP -> - getString(R.string.course_purchase_unavailable) - is EnrollmentState.NotEnrolledEnded -> - if (courseHeaderData.course.endDate != null) { - getString( - R.string.course_payments_not_available_ended, - getPrintableOfIsoDate(courseHeaderData.course.endDate, DateTimeHelper.DISPLAY_DAY_MONTH_YEAR_GENITIVE_PATTERN, TimeZone.getDefault()) - ) - } else { - getString(R.string.course_payments_not_available) - } - is EnrollmentState.NotEnrolledCantBeBought -> - getString(R.string.course_payments_cant_be_bought) - else -> "" - } - } + val headerBinding = courseBinding.headerCourse + ViewCompat.setBackgroundTintList(headerBinding.coursePurchaseFeedbackUnder, AppCompatResources.getColorStateList(courseActivity, R.color.black_alpha_30)) + headerBinding.coursePurchaseFeedback.text = + when (courseHeaderData.stats.enrollmentState) { + is EnrollmentState.NotEnrolledUnavailableIAP -> + courseActivity.getString(R.string.course_purchase_unavailable) + is EnrollmentState.NotEnrolledEnded -> + if (courseHeaderData.course.endDate != null) { + courseActivity.getString( + R.string.course_payments_not_available_ended, + getPrintableOfIsoDate(courseHeaderData.course.endDate, DateTimeHelper.DISPLAY_DAY_MONTH_YEAR_GENITIVE_PATTERN, TimeZone.getDefault()) + ) + } else { + courseActivity.getString(R.string.course_payments_not_available) + } + is EnrollmentState.NotEnrolledCantBeBought -> + courseActivity.getString(R.string.course_payments_cant_be_bought) + else -> "" + } } private fun setupIAP(courseHeaderData: CourseHeaderData) { - with(courseActivity) { - val notEnrolledMobileTierState = courseHeaderData.stats.enrollmentState as EnrollmentState.NotEnrolledMobileTier - val promoCodeSku = when { - courseHeaderData.deeplinkPromoCodeSku != PromoCodeSku.EMPTY -> - courseHeaderData.deeplinkPromoCodeSku - - notEnrolledMobileTierState.promoLightSku != null -> { - PromoCodeSku(courseHeaderData.course.defaultPromoCodeName.orEmpty(), notEnrolledMobileTierState.promoLightSku) - } - - else -> - PromoCodeSku.EMPTY + val headerBinding = courseBinding.headerCourse + val notEnrolledMobileTierState = courseHeaderData.stats.enrollmentState as EnrollmentState.NotEnrolledMobileTier + val promoCodeSku = when { + courseHeaderData.deeplinkPromoCodeSku != PromoCodeSku.EMPTY -> + courseHeaderData.deeplinkPromoCodeSku + + notEnrolledMobileTierState.promoLightSku != null -> { + PromoCodeSku(courseHeaderData.course.defaultPromoCodeName.orEmpty(), notEnrolledMobileTierState.promoLightSku) } - courseBuyInWebAction.text = - if (courseHeaderData.course.displayPrice != null) { - if (promoCodeSku.lightSku != null) { - displayPriceMapper.mapToDiscountedDisplayPriceSpannedString(notEnrolledMobileTierState.standardLightSku.price, promoCodeSku.lightSku.price) - } else { - getString(R.string.course_payments_purchase_in_web_with_price, notEnrolledMobileTierState.standardLightSku.price) - } + else -> + PromoCodeSku.EMPTY + } + + headerBinding.courseBuyInWebAction.text = + if (courseHeaderData.course.displayPrice != null) { + if (promoCodeSku.lightSku != null) { + displayPriceMapper.mapToDiscountedDisplayPriceSpannedString(notEnrolledMobileTierState.standardLightSku.price, promoCodeSku.lightSku.price) } else { - getString(R.string.course_payments_purchase_in_web) + courseActivity.getString(R.string.course_payments_purchase_in_web_with_price, notEnrolledMobileTierState.standardLightSku.price) } + } else { + courseActivity.getString(R.string.course_payments_purchase_in_web) + } - courseBuyInWebActionDiscountedNewPrice.text = - getString(R.string.course_payments_purchase_in_web_with_price, promoCodeSku.lightSku?.price) + headerBinding.courseBuyInWebActionDiscounted.courseBuyInWebActionDiscountedNewPrice.text = + courseActivity.getString(R.string.course_payments_purchase_in_web_with_price, promoCodeSku.lightSku?.price) - courseBuyInWebActionDiscountedOldPrice.text = - buildSpannedString { - strikeThrough { - append(notEnrolledMobileTierState.standardLightSku.price) - } + headerBinding.courseBuyInWebActionDiscounted.courseBuyInWebActionDiscountedOldPrice.text = + buildSpannedString { + strikeThrough { + append(notEnrolledMobileTierState.standardLightSku.price) } + } - setupDiscountButtons(hasDiscount = promoCodeSku.lightSku != null) - } + setupDiscountButtons(hasDiscount = promoCodeSku.lightSku != null) } private fun setupWeb(courseHeaderData: CourseHeaderData) { - with(courseActivity) { - val (_, currencyCode, promoPrice, hasPromo) = coursePromoCodeResolver.resolvePromoCodeInfo( - courseHeaderData.deeplinkPromoCode, - courseHeaderData.defaultPromoCode, - courseHeaderData.course - ) - - val courseDisplayPrice = courseHeaderData.course.displayPrice - - courseBuyInWebAction.text = - if (courseDisplayPrice != null) { - if (hasPromo) { - displayPriceMapper.mapToDiscountedDisplayPriceSpannedString( - courseDisplayPrice, - promoPrice, - currencyCode - ) - } else { - getString( - R.string.course_payments_purchase_in_web_with_price, - courseDisplayPrice - ) - } + val headerBinding = courseBinding.headerCourse + val (_, currencyCode, promoPrice, hasPromo) = coursePromoCodeResolver.resolvePromoCodeInfo( + courseHeaderData.deeplinkPromoCode, + courseHeaderData.defaultPromoCode, + courseHeaderData.course + ) + + val courseDisplayPrice = courseHeaderData.course.displayPrice + + headerBinding.courseBuyInWebAction.text = + if (courseDisplayPrice != null) { + if (hasPromo) { + displayPriceMapper.mapToDiscountedDisplayPriceSpannedString( + courseDisplayPrice, + promoPrice, + currencyCode + ) } else { - getString(R.string.course_payments_purchase_in_web) + courseActivity.getString( + R.string.course_payments_purchase_in_web_with_price, + courseDisplayPrice + ) } + } else { + courseActivity.getString(R.string.course_payments_purchase_in_web) + } - courseBuyInWebActionDiscountedNewPrice.text = - getString(R.string.course_payments_purchase_in_web_with_price, displayPriceMapper.mapToDisplayPriceWithCurrency(currencyCode, promoPrice)) + headerBinding.courseBuyInWebActionDiscounted.courseBuyInWebActionDiscountedNewPrice.text = + courseActivity.getString(R.string.course_payments_purchase_in_web_with_price, displayPriceMapper.mapToDisplayPriceWithCurrency(currencyCode, promoPrice)) - courseBuyInWebActionDiscountedOldPrice.text = - buildSpannedString { - strikeThrough { - append(courseHeaderData.course.displayPrice) - } + headerBinding.courseBuyInWebActionDiscounted.courseBuyInWebActionDiscountedOldPrice.text = + buildSpannedString { + strikeThrough { + append(courseHeaderData.course.displayPrice) } + } - setupDiscountButtons(hasDiscount = courseHeaderData.course.displayPrice != null && hasPromo) - } + setupDiscountButtons(hasDiscount = courseHeaderData.course.displayPrice != null && hasPromo) } private fun setupBuyAction(courseHeaderData: CourseHeaderData) { @@ -388,68 +382,65 @@ constructor( } private fun setupDiscountButtons(hasDiscount: Boolean) { - with(courseActivity) { - if (hasDiscount) { - when (discountButtonAppearanceSplitTest.currentGroup) { - DiscountButtonAppearanceSplitTest.Group.DiscountTransparent -> { - courseBuyInWebAction.isVisible = false - courseBuyInWebActionDiscounted.isVisible = true - } - DiscountButtonAppearanceSplitTest.Group.DiscountGreen -> { - courseBuyInWebAction.isVisible = true - courseBuyInWebActionDiscounted.isVisible = false - ViewCompat.setBackgroundTintList(courseBuyInWebAction, AppCompatResources.getColorStateList(courseActivity, R.color.color_overlay_green)) - } - DiscountButtonAppearanceSplitTest.Group.DiscountPurple -> { - courseBuyInWebAction.isVisible = true - courseBuyInWebActionDiscounted.isVisible = false - ViewCompat.setBackgroundTintList(courseBuyInWebAction, AppCompatResources.getColorStateList(courseActivity, R.color.color_overlay_violet)) - } + val headerBinding = courseBinding.headerCourse + if (hasDiscount) { + when (discountButtonAppearanceSplitTest.currentGroup) { + DiscountButtonAppearanceSplitTest.Group.DiscountTransparent -> { + headerBinding.courseBuyInWebAction.isVisible = false + headerBinding.courseBuyInWebActionDiscounted.root.isVisible = true + } + DiscountButtonAppearanceSplitTest.Group.DiscountGreen -> { + headerBinding.courseBuyInWebAction.isVisible = true + headerBinding.courseBuyInWebActionDiscounted.root.isVisible = false + ViewCompat.setBackgroundTintList(headerBinding.courseBuyInWebAction, AppCompatResources.getColorStateList(courseActivity, R.color.color_overlay_green)) + } + DiscountButtonAppearanceSplitTest.Group.DiscountPurple -> { + headerBinding.courseBuyInWebAction.isVisible = true + headerBinding.courseBuyInWebActionDiscounted.root.isVisible = false + ViewCompat.setBackgroundTintList(headerBinding.courseBuyInWebAction, AppCompatResources.getColorStateList(courseActivity, R.color.color_overlay_violet)) } - } else { - courseBuyInWebAction.isVisible = true - courseBuyInWebActionDiscounted.isVisible = false } + } else { + headerBinding.courseBuyInWebAction.isVisible = true + headerBinding.courseBuyInWebActionDiscounted.root.isVisible = false } } private fun setupWishlistAction(courseHeaderData: CourseHeaderData) { - with(courseActivity) { - courseWishlistAction.isEnabled = !courseHeaderData.course.isInWishlist && !courseHeaderData.isWishlistUpdating + val headerBinding = courseBinding.headerCourse + headerBinding.courseWishlistAction.isEnabled = !courseHeaderData.course.isInWishlist && !courseHeaderData.isWishlistUpdating - val wishlistText = if (courseHeaderData.isWishlistUpdating) { - if (courseHeaderData.course.isInWishlist) { - getString(R.string.course_purchase_wishlist_removing) - } else { - getString(R.string.course_purchase_wishlist_adding) - } + val wishlistText = if (courseHeaderData.isWishlistUpdating) { + if (courseHeaderData.course.isInWishlist) { + courseActivity.getString(R.string.course_purchase_wishlist_removing) } else { - if (courseHeaderData.course.isInWishlist) { - getString(R.string.course_purchase_wishlist_added) - } else { - getString(R.string.course_purchase_wishlist_add) - } + courseActivity.getString(R.string.course_purchase_wishlist_adding) } - courseWishlistAction.text = wishlistText - - if (courseHeaderData.isWishlistUpdating) { - val evaluationDrawable = AnimationDrawable() - evaluationDrawable.addFrame(getDrawableCompat(R.drawable.ic_step_quiz_evaluation_frame_1), EVALUATION_FRAME_DURATION_MS) - evaluationDrawable.addFrame(getDrawableCompat(R.drawable.ic_step_quiz_evaluation_frame_2), EVALUATION_FRAME_DURATION_MS) - evaluationDrawable.addFrame(getDrawableCompat(R.drawable.ic_step_quiz_evaluation_frame_3), EVALUATION_FRAME_DURATION_MS) - evaluationDrawable.isOneShot = false - - courseWishlistAction.icon = evaluationDrawable - evaluationDrawable.start() + } else { + if (courseHeaderData.course.isInWishlist) { + courseActivity.getString(R.string.course_purchase_wishlist_added) } else { - courseWishlistAction.icon = null + courseActivity.getString(R.string.course_purchase_wishlist_add) } } + headerBinding.courseWishlistAction.text = wishlistText + + if (courseHeaderData.isWishlistUpdating) { + val evaluationDrawable = AnimationDrawable() + evaluationDrawable.addFrame(courseActivity.getDrawableCompat(R.drawable.ic_step_quiz_evaluation_frame_1), EVALUATION_FRAME_DURATION_MS) + evaluationDrawable.addFrame(courseActivity.getDrawableCompat(R.drawable.ic_step_quiz_evaluation_frame_2), EVALUATION_FRAME_DURATION_MS) + evaluationDrawable.addFrame(courseActivity.getDrawableCompat(R.drawable.ic_step_quiz_evaluation_frame_3), EVALUATION_FRAME_DURATION_MS) + evaluationDrawable.isOneShot = false + + headerBinding.courseWishlistAction.icon = evaluationDrawable + evaluationDrawable.start() + } else { + headerBinding.courseWishlistAction.icon = null + } } fun showCourseShareTooltip() { - val menuItemView = courseActivity - .courseToolbar + val menuItemView = courseBinding.courseToolbar .findViewById(R.id.share_course) ?: return diff --git a/app/src/main/java/org/stepik/android/view/course/ui/delegates/CourseProgressDelegate.kt b/app/src/main/java/org/stepik/android/view/course/ui/delegates/CourseProgressDelegate.kt index bf1026f1a6..9e9527e955 100644 --- a/app/src/main/java/org/stepik/android/view/course/ui/delegates/CourseProgressDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course/ui/delegates/CourseProgressDelegate.kt @@ -1,24 +1,23 @@ package org.stepik.android.view.course.ui.delegates -import android.view.View import androidx.core.view.isVisible -import kotlinx.android.synthetic.main.layout_course_progress.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.LayoutCourseProgressBinding import org.stepic.droid.util.toFixed import org.stepik.android.model.Progress class CourseProgressDelegate( - view: View, + binding: LayoutCourseProgressBinding, onSubmissionCountClicked: () -> Unit, private val isLocalSubmissionsEnabled: Boolean ) { - private val context = view.context + private val context = binding.root.context - private val courseProgressCircle = view.courseProgressCircle - private val courseProgressValue = view.courseProgressValue + private val courseProgressCircle = binding.courseProgressCircle + private val courseProgressValue = binding.courseProgressValue - private val courseSolutionsTitle = view.courseSolutionsTitle - private val courseSolutionsValue = view.courseSolutionsValue + private val courseSolutionsTitle = binding.courseSolutionsTitle + private val courseSolutionsValue = binding.courseSolutionsValue init { courseSolutionsValue.setOnClickListener { onSubmissionCountClicked() } @@ -49,4 +48,4 @@ class CourseProgressDelegate( courseSolutionsValue.isVisible = isNeedShowSolutions courseSolutionsValue.text = count.toString() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course/ui/delegates/CourseStatsDelegate.kt b/app/src/main/java/org/stepik/android/view/course/ui/delegates/CourseStatsDelegate.kt index 63b95dec1e..09a7bdca70 100644 --- a/app/src/main/java/org/stepik/android/view/course/ui/delegates/CourseStatsDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course/ui/delegates/CourseStatsDelegate.kt @@ -1,21 +1,20 @@ package org.stepik.android.view.course.ui.delegates -import android.view.View import androidx.core.view.isVisible -import kotlinx.android.synthetic.main.layout_course_stats.view.* +import org.stepic.droid.databinding.LayoutCourseStatsBinding import org.stepik.android.domain.course.model.CourseStats import kotlin.math.roundToInt class CourseStatsDelegate( - view: View + binding: LayoutCourseStatsBinding ) { companion object { private const val MIN_FEATURED_READINESS = 0.9 } - private val courseRating = view.courseRating - private val courseLearnersCount = view.courseLearnersCount - private val courseFeatured = view.courseFeatured + private val courseRating = binding.courseRating + private val courseLearnersCount = binding.courseLearnersCount + private val courseFeatured = binding.courseFeatured fun setStats(courseStats: CourseStats) { courseRating.total = 5 @@ -25,4 +24,4 @@ class CourseStatsDelegate( courseLearnersCount.text = courseStats.learnersCount.toString() courseFeatured.isVisible = courseStats.readiness > MIN_FEATURED_READINESS } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_complete/ui/dialog/CourseCompleteBottomSheetDialogFragment.kt b/app/src/main/java/org/stepik/android/view/course_complete/ui/dialog/CourseCompleteBottomSheetDialogFragment.kt index 9b5d0a157f..b125ea5ef2 100644 --- a/app/src/main/java/org/stepik/android/view/course_complete/ui/dialog/CourseCompleteBottomSheetDialogFragment.kt +++ b/app/src/main/java/org/stepik/android/view/course_complete/ui/dialog/CourseCompleteBottomSheetDialogFragment.kt @@ -18,9 +18,9 @@ import androidx.lifecycle.ViewModelProvider import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.button.MaterialButton -import kotlinx.android.synthetic.main.bottom_sheet_dialog_course_complete.* -import kotlinx.android.synthetic.main.error_no_connection_with_button_small.* +import org.stepic.droid.databinding.BottomSheetDialogCourseCompleteBinding import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App @@ -76,6 +76,8 @@ class CourseCompleteBottomSheetDialogFragment : BottomSheetDialogFragment(), private lateinit var viewStateDelegate: ViewStateDelegate + private val courseCompleteBinding: BottomSheetDialogCourseCompleteBinding by viewBinding(BottomSheetDialogCourseCompleteBinding::bind) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) injectComponent() @@ -96,7 +98,7 @@ class CourseCompleteBottomSheetDialogFragment : BottomSheetDialogFragment(), super.onViewCreated(view, savedInstanceState) analytic.report(FinishedStepsScreenOpenedAnalyticEvent(course)) - courseCompleteFeedback.background = AppCompatResources + courseCompleteBinding.courseCompleteFeedback.background = AppCompatResources .getDrawable(requireContext(), R.drawable.bg_shape_rounded) ?.mutate() ?.let { DrawableCompat.wrap(it) } @@ -106,22 +108,22 @@ class CourseCompleteBottomSheetDialogFragment : BottomSheetDialogFragment(), } viewStateDelegate = ViewStateDelegate() viewStateDelegate.addState() - viewStateDelegate.addState(courseCompleteProgressbar) + viewStateDelegate.addState(courseCompleteBinding.courseCompleteProgressbar) viewStateDelegate.addState( - courseCompleteHeader, - courseCompleteTitle, - courseCompleteFeedback, - courseCompleteSubtitle, - viewCertificateAction, - shareResultAction, - courseCompleteDivider, - primaryAction, - secondaryAction + courseCompleteBinding.courseCompleteHeader, + courseCompleteBinding.courseCompleteTitle, + courseCompleteBinding.courseCompleteFeedback, + courseCompleteBinding.courseCompleteSubtitle, + courseCompleteBinding.viewCertificateAction, + courseCompleteBinding.shareResultAction, + courseCompleteBinding.courseCompleteDivider.root, + courseCompleteBinding.primaryAction, + courseCompleteBinding.secondaryAction ) - viewStateDelegate.addState(courseCompleteNetworkError) + viewStateDelegate.addState(courseCompleteBinding.courseCompleteNetworkError.root) viewModel.onNewMessage(CourseCompleteFeature.Message.Init(course)) - tryAgain.setOnClickListener { viewModel.onNewMessage(CourseCompleteFeature.Message.Init(course, forceUpdate = true)) } + courseCompleteBinding.courseCompleteNetworkError.tryAgain.setOnClickListener { viewModel.onNewMessage(CourseCompleteFeature.Message.Init(course, forceUpdate = true)) } } override fun onStart() { @@ -548,15 +550,15 @@ class CourseCompleteBottomSheetDialogFragment : BottomSheetDialogFragment(), * Setup functions for dialog view */ private fun setupDialogView(courseCompleteInfo: CourseCompleteInfo, courseCompleteDialogViewInfo: CourseCompleteDialogViewInfo) { - courseCompleteLogo.setImageResource(courseCompleteDialogViewInfo.headerImage) - courseCompleteHeader.setBackgroundResource(courseCompleteDialogViewInfo.gradientRes) - courseCompleteTitle.text = courseCompleteDialogViewInfo.title - courseCompleteFeedback.text = courseCompleteDialogViewInfo.feedbackText - courseCompleteFeedback.isVisible = courseCompleteDialogViewInfo.feedbackText.isNotEmpty() - courseCompleteSubtitle.text = courseCompleteDialogViewInfo.subtitle + courseCompleteBinding.courseCompleteLogo.setImageResource(courseCompleteDialogViewInfo.headerImage) + courseCompleteBinding.courseCompleteHeader.setBackgroundResource(courseCompleteDialogViewInfo.gradientRes) + courseCompleteBinding.courseCompleteTitle.text = courseCompleteDialogViewInfo.title + courseCompleteBinding.courseCompleteFeedback.text = courseCompleteDialogViewInfo.feedbackText + courseCompleteBinding.courseCompleteFeedback.isVisible = courseCompleteDialogViewInfo.feedbackText.isNotEmpty() + courseCompleteBinding.courseCompleteSubtitle.text = courseCompleteDialogViewInfo.subtitle - viewCertificateAction.isVisible = courseCompleteDialogViewInfo.isViewCertificateVisible - shareResultAction.isVisible = courseCompleteDialogViewInfo.isShareVisible + courseCompleteBinding.viewCertificateAction.isVisible = courseCompleteDialogViewInfo.isViewCertificateVisible + courseCompleteBinding.shareResultAction.isVisible = courseCompleteDialogViewInfo.isShareVisible val score = courseCompleteInfo .courseProgress @@ -567,13 +569,13 @@ class CourseCompleteBottomSheetDialogFragment : BottomSheetDialogFragment(), val cost = courseCompleteInfo.courseProgress.cost val completeRate = (score * 100 / cost) / 100f - viewCertificateAction.setOnClickListener { + courseCompleteBinding.viewCertificateAction.setOnClickListener { if (courseCompleteInfo.certificate == null) return@setOnClickListener analytic.report(FinishedStepsViewCertificatePressedAnalyticEvent(courseCompleteInfo.course, completeRate)) screenManager.showPdfInBrowserByGoogleDocs(requireActivity(), courseCompleteInfo.certificate.url) } - shareResultAction.setOnClickListener { + courseCompleteBinding.shareResultAction.setOnClickListener { analytic.report(FinishedStepsSharePressedAnalyticEvent(courseCompleteInfo.course, completeRate)) if (courseCompleteInfo.certificate != null) { val message = getString(R.string.course_complete_share_result_with_certificate, courseCompleteInfo.course.title.toString()) @@ -585,32 +587,32 @@ class CourseCompleteBottomSheetDialogFragment : BottomSheetDialogFragment(), } if (courseCompleteDialogViewInfo.primaryActionStringRes != -1) { - primaryAction.isVisible = true - primaryAction.setText(courseCompleteDialogViewInfo.primaryActionStringRes) + courseCompleteBinding.primaryAction.isVisible = true + courseCompleteBinding.primaryAction.setText(courseCompleteDialogViewInfo.primaryActionStringRes) setupOnActionClickListener( courseCompleteDialogViewInfo.primaryActionStringRes, courseCompleteInfo.course, completeRate, - primaryAction + courseCompleteBinding.primaryAction ) } else { - primaryAction.isVisible = false + courseCompleteBinding.primaryAction.isVisible = false } if (courseCompleteDialogViewInfo.secondaryActionStringRes != -1) { - secondaryAction.isVisible = true - secondaryAction.setText(courseCompleteDialogViewInfo.secondaryActionStringRes) + courseCompleteBinding.secondaryAction.isVisible = true + courseCompleteBinding.secondaryAction.setText(courseCompleteDialogViewInfo.secondaryActionStringRes) setupOnActionClickListener( courseCompleteDialogViewInfo.secondaryActionStringRes, courseCompleteInfo.course, completeRate, - secondaryAction + courseCompleteBinding.secondaryAction ) } else { - secondaryAction.isVisible = false + courseCompleteBinding.secondaryAction.isVisible = false } - courseCompleteDivider.isVisible = primaryAction.isVisible || secondaryAction.isVisible + courseCompleteBinding.courseCompleteDivider.root.isVisible = courseCompleteBinding.primaryAction.isVisible || courseCompleteBinding.secondaryAction.isVisible } private fun setupOnActionClickListener(actionStringRes: Int, course: Course, completeRate: Float, actionButton: MaterialButton) { diff --git a/app/src/main/java/org/stepik/android/view/course_content/model/RequiredSection.kt b/app/src/main/java/org/stepik/android/view/course_content/model/RequiredSection.kt index 4fd5219da4..4fdc0a078f 100644 --- a/app/src/main/java/org/stepik/android/view/course_content/model/RequiredSection.kt +++ b/app/src/main/java/org/stepik/android/view/course_content/model/RequiredSection.kt @@ -1,7 +1,7 @@ package org.stepik.android.view.course_content.model import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import org.stepik.android.model.Progress import org.stepik.android.model.Section diff --git a/app/src/main/java/org/stepik/android/view/course_content/ui/adapter/CourseContentTimelineAdapter.kt b/app/src/main/java/org/stepik/android/view/course_content/ui/adapter/CourseContentTimelineAdapter.kt index 838031dc97..b65ab0a974 100644 --- a/app/src/main/java/org/stepik/android/view/course_content/ui/adapter/CourseContentTimelineAdapter.kt +++ b/app/src/main/java/org/stepik/android/view/course_content/ui/adapter/CourseContentTimelineAdapter.kt @@ -4,8 +4,9 @@ import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.view_course_content_section_date.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ViewCourseContentSectionDateBinding import org.stepic.droid.util.DateTimeHelper import org.stepic.droid.util.safeDiv import org.stepik.android.view.course_content.model.CourseContentSectionDate @@ -33,25 +34,22 @@ class CourseContentTimelineAdapter : RecyclerView.Adapter= data.date + viewBinding.dateDot.isEnabled = now >= data.date } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_content/ui/adapter/delegates/control_bar/CourseContentControlBarDelegate.kt b/app/src/main/java/org/stepik/android/view/course_content/ui/adapter/delegates/control_bar/CourseContentControlBarDelegate.kt index 6c9705900a..9420c36e97 100644 --- a/app/src/main/java/org/stepik/android/view/course_content/ui/adapter/delegates/control_bar/CourseContentControlBarDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course_content/ui/adapter/delegates/control_bar/CourseContentControlBarDelegate.kt @@ -7,8 +7,9 @@ import android.widget.TextView import androidx.appcompat.widget.PopupMenu import androidx.collection.LongSparseArray import androidx.swiperefreshlayout.widget.CircularProgressDrawable -import kotlinx.android.synthetic.main.view_course_content_control_bar.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ViewCourseContentControlBarBinding import org.stepic.droid.persistence.model.DownloadProgress import org.stepic.droid.ui.util.setHeight import org.stepic.droid.util.TextUtil @@ -32,8 +33,9 @@ class CourseContentControlBarDelegate( data is CourseContentItem.ControlBar inner class ViewHolder(root: View) : DelegateViewHolder(root) { + private val viewBinding: ViewCourseContentControlBarBinding by viewBinding { ViewCourseContentControlBarBinding.bind(root) } - private val controlBar = root.controlBar + private val controlBar = viewBinding.controlBar private lateinit var status: DownloadProgress.Status private lateinit var downloadControl: View private lateinit var downloadDrawable: ImageView @@ -163,4 +165,4 @@ class CourseContentControlBarDelegate( } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_content/ui/adapter/delegates/section/CourseContentSectionDelegate.kt b/app/src/main/java/org/stepik/android/view/course_content/ui/adapter/delegates/section/CourseContentSectionDelegate.kt index 4d45579bf0..d278d3202d 100644 --- a/app/src/main/java/org/stepik/android/view/course_content/ui/adapter/delegates/section/CourseContentSectionDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course_content/ui/adapter/delegates/section/CourseContentSectionDelegate.kt @@ -14,8 +14,9 @@ import androidx.core.content.ContextCompat import androidx.core.graphics.drawable.DrawableCompat import androidx.core.view.isVisible import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.view_course_content_section.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ViewCourseContentSectionBinding import org.stepic.droid.persistence.model.DownloadProgress import org.stepic.droid.ui.util.StartSnapHelper import org.stepic.droid.util.toFixed @@ -40,41 +41,32 @@ class CourseContentSectionDelegate( data is CourseContentItem.SectionItem inner class ViewHolder(root: View) : DelegateViewHolder(root) { - private val sectionExamType = root.sectionExamType - private val sectionExamStatus = root.sectionExamStatus - private val sectionTitle = root.sectionTitle - private val sectionPosition = root.sectionPosition - private val sectionTimeline = root.sectionTimeline - private val sectionProgress = root.sectionProgress - private val sectionTextProgress = root.sectionTextProgress - private val sectionDownloadStatus = root.sectionDownloadStatus - private val sectionRequirementsDescription = root.sectionRequirementsDescription - private val sectionExamAction = root.sectionExamAction + private val viewBinding: ViewCourseContentSectionBinding by viewBinding { ViewCourseContentSectionBinding.bind(root) } private val sectionTimeLineAdapter = CourseContentTimelineAdapter() init { - with(sectionTimeline) { + with(viewBinding.sectionTimeline) { adapter = sectionTimeLineAdapter layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) StartSnapHelper().attachToRecyclerView(this) addItemDecoration(CourseContentTimelineDecorator()) - this@ViewHolder.sectionTitle.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener { + this@ViewHolder.viewBinding.sectionTitle.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener { override fun onPreDraw(): Boolean { - setPadding(this@ViewHolder.sectionTitle.left, paddingTop, paddingRight, paddingBottom) + setPadding(this@ViewHolder.viewBinding.sectionTitle.left, paddingTop, paddingRight, paddingBottom) layoutManager?.scrollToPosition(0) - this@ViewHolder.sectionTitle.viewTreeObserver.removeOnPreDrawListener(this) + this@ViewHolder.viewBinding.sectionTitle.viewTreeObserver.removeOnPreDrawListener(this) return true } }) } - sectionDownloadStatus.setOnClickListener { + viewBinding.sectionDownloadStatus.setOnClickListener { val item = (itemData as? CourseContentItem.SectionItem) ?: return@setOnClickListener - when (sectionDownloadStatus.status) { + when (viewBinding.sectionDownloadStatus.status) { DownloadProgress.Status.NotCached -> sectionClickListener.onItemDownloadClicked(item) @@ -88,7 +80,7 @@ class CourseContentSectionDelegate( } } - sectionExamAction.setOnClickListener { + viewBinding.sectionExamAction.setOnClickListener { val item = (itemData as? CourseContentItem.SectionItem) ?: return@setOnClickListener sectionClickListener.onItemClicked(item) } @@ -99,11 +91,11 @@ class CourseContentSectionDelegate( setupExamViews(data) - sectionExamType.isVisible = section.isExam - sectionExamStatus.isVisible = section.isExam + viewBinding.sectionExamType.isVisible = section.isExam + viewBinding.sectionExamStatus.isVisible = section.isExam - sectionTitle.text = section.title - sectionPosition.text = section.position.toString() + viewBinding.sectionTitle.text = section.title + viewBinding.sectionPosition.text = section.position.toString() if (progress != null) { when { @@ -113,61 +105,61 @@ class CourseContentSectionDelegate( ?.toFloatOrNull() ?: 0f - sectionProgress.progress = score / progress.cost.toFloat() - sectionTextProgress.text = context.resources.getString(R.string.course_content_text_progress_points, + viewBinding.sectionProgress.progress = score / progress.cost.toFloat() + viewBinding.sectionTextProgress.text = context.resources.getString(R.string.course_content_text_progress_points, score.toFixed(context.resources.getInteger(R.integer.score_decimal_count)), progress.cost) - sectionTextProgress.visibility = View.VISIBLE + viewBinding.sectionTextProgress.visibility = View.VISIBLE } progress.cost == 0L && data.section.isExam && data.examStatus == ExamStatus.FINISHED -> { - sectionTextProgress.text = context.resources.getString(R.string.section_syllabus_exam_no_score_title) - sectionProgress.progress = 0f - sectionTextProgress.visibility = View.VISIBLE + viewBinding.sectionTextProgress.text = context.resources.getString(R.string.section_syllabus_exam_no_score_title) + viewBinding.sectionProgress.progress = 0f + viewBinding.sectionTextProgress.visibility = View.VISIBLE } else -> { - sectionProgress.progress = 0f - sectionTextProgress.visibility = View.GONE + viewBinding.sectionProgress.progress = 0f + viewBinding.sectionTextProgress.visibility = View.GONE } } } else { - sectionProgress.progress = 0f - sectionTextProgress.visibility = View.GONE + viewBinding.sectionProgress.progress = 0f + viewBinding.sectionTextProgress.visibility = View.GONE } - sectionDownloadStatus.status = sectionDownloadStatuses[data.section.id] ?: DownloadProgress.Status.Pending + viewBinding.sectionDownloadStatus.status = sectionDownloadStatuses[data.section.id] ?: DownloadProgress.Status.Pending sectionTimeLineAdapter.dates = dates - sectionTimeline.isVisible = dates.isNotEmpty() + viewBinding.sectionTimeline.isVisible = dates.isNotEmpty() - sectionDownloadStatus.isVisible = isEnabled && !section.isExam + viewBinding.sectionDownloadStatus.isVisible = isEnabled && !section.isExam val alpha = if (isEnabled) 1f else 0.4f - sectionTitle.alpha = alpha - sectionPosition.alpha = alpha - sectionTimeline.alpha = alpha + viewBinding.sectionTitle.alpha = alpha + viewBinding.sectionPosition.alpha = alpha + viewBinding.sectionTimeline.alpha = alpha if (requiredSection != null) { val requiredPoints = (requiredSection.progress.cost * section.requiredPercent / 100f).roundToInt() - sectionRequirementsDescription.text = + viewBinding.sectionRequirementsDescription.text = context.getString( R.string.course_content_section_requirements, context.resources.getQuantityString(R.plurals.points, requiredPoints.toInt(), requiredPoints), requiredSection.section.title ) - sectionRequirementsDescription.isVisible = true + viewBinding.sectionRequirementsDescription.isVisible = true } else { - sectionRequirementsDescription.isVisible = false + viewBinding.sectionRequirementsDescription.isVisible = false } } } private fun setupExamViews(sectionItem: CourseContentItem.SectionItem) { - sectionExamType.text = if (sectionItem.isProctored) { + viewBinding.sectionExamType.text = if (sectionItem.isProctored) { context.getString(R.string.section_syllabus_exam_chip_proctored_title) } else { context.getString(R.string.section_syllabus_exam_chip_simple_title) } - sectionExamType.background = getColoredDrawable( + viewBinding.sectionExamType.background = getColoredDrawable( R.drawable.bg_shape_rounded_16dp, ContextCompat.getColor(context, R.color.color_overlay_violet_alpha_12) ) @@ -178,13 +170,13 @@ class CourseContentSectionDelegate( R.drawable.ic_clock, ContextCompat.getColor(context, R.color.color_overlay_green) ) - sectionExamStatus.setCompoundDrawablesWithIntrinsicBounds(clockDrawable, null, null, null) - sectionExamStatus.setTextColor(ContextCompat.getColor(context, R.color.color_overlay_green)) - sectionExamStatus.text = sectionItem.section.examDurationMinutes?.let { + viewBinding.sectionExamStatus.setCompoundDrawablesWithIntrinsicBounds(clockDrawable, null, null, null) + viewBinding.sectionExamStatus.setTextColor(ContextCompat.getColor(context, R.color.color_overlay_green)) + viewBinding.sectionExamStatus.text = sectionItem.section.examDurationMinutes?.let { context.resources.getQuantityString(R.plurals.minutes, it, sectionItem.section.examDurationMinutes) } - sectionExamStatus.background = getColoredDrawable( + viewBinding.sectionExamStatus.background = getColoredDrawable( R.drawable.bg_shape_rounded_16dp, ContextCompat.getColor(context, R.color.color_overlay_green_alpha_12) ) @@ -196,12 +188,12 @@ class CourseContentSectionDelegate( evaluationDrawable.addFrame(context.getDrawableCompat(R.drawable.ic_step_quiz_evaluation_frame_3), 250) evaluationDrawable.isOneShot = false DrawableCompat.setTint(evaluationDrawable, ContextCompat.getColor(context, R.color.white)) - sectionExamStatus.setCompoundDrawablesWithIntrinsicBounds(evaluationDrawable, null, null, null) + viewBinding.sectionExamStatus.setCompoundDrawablesWithIntrinsicBounds(evaluationDrawable, null, null, null) evaluationDrawable.start() - sectionExamStatus.setTextColor(ContextCompat.getColor(context, R.color.white)) - sectionExamStatus.text = context.getString(R.string.section_syllabus_exam_in_progress) - sectionExamStatus.background = getColoredDrawable( + viewBinding.sectionExamStatus.setTextColor(ContextCompat.getColor(context, R.color.white)) + viewBinding.sectionExamStatus.text = context.getString(R.string.section_syllabus_exam_in_progress) + viewBinding.sectionExamStatus.background = getColoredDrawable( R.drawable.bg_shape_rounded_16dp, ContextCompat.getColor(context, R.color.color_overlay_violet) ) @@ -211,10 +203,10 @@ class CourseContentSectionDelegate( R.drawable.ic_exam_finished, ContextCompat.getColor(context, R.color.white) ) - sectionExamStatus.setCompoundDrawablesWithIntrinsicBounds(checkDrawable, null, null, null) - sectionExamStatus.setTextColor(ContextCompat.getColor(context, R.color.white)) - sectionExamStatus.text = context.getString(R.string.section_syllabus_exam_finished) - sectionExamStatus.background = getColoredDrawable( + viewBinding.sectionExamStatus.setCompoundDrawablesWithIntrinsicBounds(checkDrawable, null, null, null) + viewBinding.sectionExamStatus.setTextColor(ContextCompat.getColor(context, R.color.white)) + viewBinding.sectionExamStatus.text = context.getString(R.string.section_syllabus_exam_finished) + viewBinding.sectionExamStatus.background = getColoredDrawable( R.drawable.bg_shape_rounded_16dp, ContextCompat.getColor(context, R.color.color_overlay_green) ) @@ -235,8 +227,8 @@ class CourseContentSectionDelegate( null -> "" } - sectionExamAction.text = examActionTitle - sectionExamAction.isVisible = examActionTitle.isNotEmpty() && sectionItem.isEnabled + viewBinding.sectionExamAction.text = examActionTitle + viewBinding.sectionExamAction.isVisible = examActionTitle.isNotEmpty() && sectionItem.isEnabled } private fun getColoredDrawable(@DrawableRes resId: Int, @ColorInt color: Int): Drawable? = @@ -249,4 +241,4 @@ class CourseContentSectionDelegate( DrawableCompat.setTintMode(it, PorterDuff.Mode.SRC_IN) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_content/ui/adapter/delegates/unit/CourseContentUnitDelegate.kt b/app/src/main/java/org/stepik/android/view/course_content/ui/adapter/delegates/unit/CourseContentUnitDelegate.kt index fb8d99f964..c27c18de7d 100644 --- a/app/src/main/java/org/stepik/android/view/course_content/ui/adapter/delegates/unit/CourseContentUnitDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course_content/ui/adapter/delegates/unit/CourseContentUnitDelegate.kt @@ -5,9 +5,10 @@ import android.view.ViewGroup import androidx.annotation.DrawableRes import androidx.collection.LongSparseArray import androidx.core.view.isVisible +import by.kirich1409.viewbindingdelegate.viewBinding import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.view_course_content_unit.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.ViewCourseContentUnitBinding import org.stepic.droid.persistence.model.DownloadProgress import org.stepic.droid.util.toFixed import org.stepik.android.view.course_content.model.CourseContentItem @@ -27,29 +28,16 @@ class CourseContentUnitDelegate( data is CourseContentItem.UnitItem inner class ViewHolder(root: View) : DelegateViewHolder(root) { - private val unitIcon = root.unitIcon - private val unitTitle = root.unitTitle - private val unitDemoAccess = root.unitDemoAccess - private val unitTextProgress = root.unitTextProgress - private val unitProgress = root.unitProgress - - private val unitViewCount = root.unitViewCount - private val unitViewCountIcon = root.unitViewCountIcon - private val unitRating = root.unitRating - private val unitRatingIcon = root.unitRatingIcon - - private val unitTimeToComplete = root.unitTimeToComplete - - private val unitDownloadStatus = root.unitDownloadStatus + private val viewBinding: ViewCourseContentUnitBinding by viewBinding { ViewCourseContentUnitBinding.bind(root) } init { root.setOnClickListener { (itemData as? CourseContentItem.UnitItem)?.let(unitClickListener::onItemClicked) } - unitDownloadStatus.setOnClickListener { + viewBinding.unitDownloadStatus.setOnClickListener { val item = (itemData as? CourseContentItem.UnitItem) ?: return@setOnClickListener - when (unitDownloadStatus.status) { + when (viewBinding.unitDownloadStatus.status) { DownloadProgress.Status.NotCached -> unitClickListener.onItemDownloadClicked(item) @@ -66,7 +54,7 @@ class CourseContentUnitDelegate( override fun onBind(data: CourseContentItem) { with(data as CourseContentItem.UnitItem) { - unitTitle.text = context.resources.getString(R.string.course_content_unit_title, + viewBinding.unitTitle.text = context.resources.getString(R.string.course_content_unit_title, section.position, unit.position, lesson.title) if (progress != null && progress.cost > 0) { val score = progress @@ -74,20 +62,20 @@ class CourseContentUnitDelegate( ?.toFloatOrNull() ?: 0f - unitTextProgress.text = context.resources.getString(R.string.course_content_text_progress_points, + viewBinding.unitTextProgress.text = context.resources.getString(R.string.course_content_text_progress_points, score.toFixed(context.resources.getInteger(R.integer.score_decimal_count)), progress.cost) - unitProgress.progress = score / progress.cost.toFloat() - unitTextProgress.isVisible = true + viewBinding.unitProgress.progress = score / progress.cost.toFloat() + viewBinding.unitTextProgress.isVisible = true } else { - unitProgress.progress = 0f - unitTextProgress.isVisible = false + viewBinding.unitProgress.progress = 0f + viewBinding.unitTextProgress.isVisible = false } val timeToComplete = lesson.timeToComplete.takeIf { it > 60 } ?: lesson.steps.size * 60L if (timeToComplete > 0) { - unitTimeToComplete.isVisible = true + viewBinding.unitTimeToComplete.isVisible = true val timeToCompleteString = if (timeToComplete in 0 until 3600) { val timeValue = timeToComplete / 60 @@ -96,21 +84,21 @@ class CourseContentUnitDelegate( context.resources.getString(R.string.course_content_time_to_complete_hours_unit, timeToComplete / 3600) } - unitTimeToComplete.text = context.getString(R.string.course_content_time_to_complete, timeToCompleteString) + viewBinding.unitTimeToComplete.text = context.getString(R.string.course_content_time_to_complete, timeToCompleteString) } else { - unitTimeToComplete.isVisible = false + viewBinding.unitTimeToComplete.isVisible = false } - unitDownloadStatus.status = unitDownloadStatuses[data.unit.id] ?: DownloadProgress.Status.Pending + viewBinding.unitDownloadStatus.status = unitDownloadStatuses[data.unit.id] ?: DownloadProgress.Status.Pending - Glide.with(unitIcon.context) + Glide.with(viewBinding.unitIcon.context) .asBitmap() .load(lesson.coverUrl) .placeholder(R.drawable.general_placeholder) .centerCrop() - .into(unitIcon) + .into(viewBinding.unitIcon) - unitViewCount.text = lesson.passedBy.toString() + viewBinding.unitViewCount.text = lesson.passedBy.toString() @DrawableRes val unitRatingDrawableRes = @@ -120,22 +108,22 @@ class CourseContentUnitDelegate( R.drawable.ic_course_content_like } - unitRatingIcon.setImageResource(unitRatingDrawableRes) - unitRating.text = abs(lesson.voteDelta).toString() + viewBinding.unitRatingIcon.setImageResource(unitRatingDrawableRes) + viewBinding.unitRating.text = abs(lesson.voteDelta).toString() - unitDownloadStatus.isVisible = access == CourseContentItem.UnitItem.Access.FULL_ACCESS - unitDemoAccess.isVisible = access == CourseContentItem.UnitItem.Access.DEMO + viewBinding.unitDownloadStatus.isVisible = access == CourseContentItem.UnitItem.Access.FULL_ACCESS + viewBinding.unitDemoAccess.isVisible = access == CourseContentItem.UnitItem.Access.DEMO itemView.isEnabled = access != CourseContentItem.UnitItem.Access.NO_ACCESS val alpha = if (access != CourseContentItem.UnitItem.Access.NO_ACCESS) 1f else 0.4f - unitTitle.alpha = alpha - unitRatingIcon.alpha = alpha - unitRating.alpha = alpha - unitViewCount.alpha = alpha - unitViewCountIcon.alpha = alpha - unitTimeToComplete.alpha = alpha - unitTextProgress.alpha = alpha + viewBinding.unitTitle.alpha = alpha + viewBinding.unitRatingIcon.alpha = alpha + viewBinding.unitRating.alpha = alpha + viewBinding.unitViewCount.alpha = alpha + viewBinding.unitViewCountIcon.alpha = alpha + viewBinding.unitTimeToComplete.alpha = alpha + viewBinding.unitTextProgress.alpha = alpha } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_content/ui/fragment/CourseContentFragment.kt b/app/src/main/java/org/stepik/android/view/course_content/ui/fragment/CourseContentFragment.kt index 0ade6522d1..d8e4df017f 100644 --- a/app/src/main/java/org/stepik/android/view/course_content/ui/fragment/CourseContentFragment.kt +++ b/app/src/main/java/org/stepik/android/view/course_content/ui/fragment/CourseContentFragment.kt @@ -18,19 +18,18 @@ import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.snackbar.Snackbar import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.Observables.zip import io.reactivex.rxkotlin.plusAssign import io.reactivex.subjects.BehaviorSubject -import kotlinx.android.synthetic.main.empty_default.* -import kotlinx.android.synthetic.main.error_no_connection.* -import kotlinx.android.synthetic.main.fragment_course_content.* import org.stepic.droid.R import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.FragmentCourseContentBinding import org.stepic.droid.persistence.model.DownloadProgress import org.stepic.droid.ui.dialogs.LoadingProgressDialogFragment import org.stepic.droid.ui.dialogs.VideoQualityDetailedDialog @@ -100,6 +99,8 @@ class CourseContentFragment : private lateinit var contentAdapter: CourseContentAdapter private var courseId: Long by argument() + private val binding: FragmentCourseContentBinding by viewBinding(FragmentCourseContentBinding::bind) + private val courseContentPresenter: CourseContentPresenter by viewModels { viewModelFactory } private lateinit var viewStateDelegate: ViewStateDelegate @@ -137,7 +138,7 @@ class CourseContentFragment : inflater.inflate(R.layout.fragment_course_content, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - with(courseContentRecycler) { + with(binding.courseContentRecycler) { contentAdapter = CourseContentAdapter( sectionClickListener = @@ -204,11 +205,11 @@ class CourseContentFragment : } viewStateDelegate = ViewStateDelegate() - viewStateDelegate.addState(courseContentPlaceholder) - viewStateDelegate.addState(courseContentPlaceholder) - viewStateDelegate.addState(courseContentRecycler) - viewStateDelegate.addState(reportProblem) - viewStateDelegate.addState(report_empty) + viewStateDelegate.addState(binding.courseContentPlaceholder) + viewStateDelegate.addState(binding.courseContentPlaceholder) + viewStateDelegate.addState(binding.courseContentRecycler) + viewStateDelegate.addState(binding.errorNoConnection.reportProblem) + viewStateDelegate.addState(binding.emptyDefault.reportEmpty) } override fun onStart() { @@ -292,7 +293,7 @@ class CourseContentFragment : .firstElement() .ignoreElement() .subscribe { - val anchorView = courseContentRecycler.findViewById(R.id.course_control_schedule) + val anchorView = binding.courseContentRecycler.findViewById(R.id.course_control_schedule) val deadlinesDescription = getString(R.string.deadlines_banner_description) PopupHelper.showPopupAnchoredToView(requireContext(), anchorView, deadlinesDescription, cancelableOnTouchOutside = true, withArrow = true) } diff --git a/app/src/main/java/org/stepik/android/view/course_content/ui/view/DownloadStatusView.kt b/app/src/main/java/org/stepik/android/view/course_content/ui/view/DownloadStatusView.kt index f60ca0302d..5d2797ea34 100644 --- a/app/src/main/java/org/stepik/android/view/course_content/ui/view/DownloadStatusView.kt +++ b/app/src/main/java/org/stepik/android/view/course_content/ui/view/DownloadStatusView.kt @@ -2,15 +2,15 @@ package org.stepik.android.view.course_content.ui.view import android.content.Context import android.util.AttributeSet +import android.view.LayoutInflater import android.widget.FrameLayout import android.widget.ProgressBar import android.widget.TextView -import kotlinx.android.synthetic.main.view_download_status.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.ViewDownloadStatusBinding import org.stepic.droid.persistence.model.DownloadProgress import org.stepic.droid.util.TextUtil import org.stepik.android.view.ui.delegate.ViewStateDelegate -import ru.nobird.android.view.base.ui.extension.inflate class DownloadStatusView @JvmOverloads @@ -42,14 +42,14 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 private val viewStateDelegate = ViewStateDelegate() init { - val view = inflate(R.layout.view_download_status, true) - statusCached = view.statusCached + val binding = ViewDownloadStatusBinding.inflate(LayoutInflater.from(context), this) + statusCached = binding.statusCached - viewStateDelegate.addState(view.statusNotCached) + viewStateDelegate.addState(binding.statusNotCached) viewStateDelegate.addState(statusCached) - viewStateDelegate.addState(view.statusPending) - viewStateDelegate.addState(view.statusInProgress) + viewStateDelegate.addState(binding.statusPending) + viewStateDelegate.addState(binding.statusInProgress) - statusProgress = view.statusProgress + statusProgress = binding.statusProgress } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_info/ui/adapter/delegates/instructors/CourseInfoInstructorDataAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/course_info/ui/adapter/delegates/instructors/CourseInfoInstructorDataAdapterDelegate.kt index 9f5217b864..0325fc740a 100644 --- a/app/src/main/java/org/stepik/android/view/course_info/ui/adapter/delegates/instructors/CourseInfoInstructorDataAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course_info/ui/adapter/delegates/instructors/CourseInfoInstructorDataAdapterDelegate.kt @@ -4,8 +4,9 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.content.res.AppCompatResources import androidx.core.view.isGone -import kotlinx.android.synthetic.main.view_course_info_instructor_item.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ViewCourseInfoInstructorItemBinding import org.stepik.android.view.glide.ui.extension.wrapWithGlide import org.stepik.android.model.user.User import ru.nobird.android.ui.adapterdelegates.AdapterDelegate @@ -21,14 +22,12 @@ class CourseInfoInstructorDataAdapterDelegate( ViewHolder(createView(parent, R.layout.view_course_info_instructor_item)) private inner class ViewHolder(root: View) : DelegateViewHolder(root) { - private val instructorIcon = root.instructorIcon - private val instructorTitle = root.instructorTitle - private val instructorDescription = root.instructorDescription + private val viewBinding: ViewCourseInfoInstructorItemBinding by viewBinding { ViewCourseInfoInstructorItemBinding.bind(root) } - private val instructorIconWrapper = instructorIcon.wrapWithGlide() + private val instructorIconWrapper = viewBinding.instructorIcon.wrapWithGlide() init { - root.setOnClickListener { itemData?.let(onInstructorClicked) } + viewBinding.root.setOnClickListener { itemData?.let(onInstructorClicked) } } override fun onBind(data: User?) { @@ -39,9 +38,9 @@ class CourseInfoInstructorDataAdapterDelegate( placeholder = AppCompatResources.getDrawable(context, R.drawable.general_placeholder) ) - instructorTitle.text = data.fullName - instructorDescription.text = data.shortBio - instructorDescription.isGone = data.shortBio.isNullOrBlank() + viewBinding.instructorTitle.text = data.fullName + viewBinding.instructorDescription.text = data.shortBio + viewBinding.instructorDescription.isGone = data.shortBio.isNullOrBlank() } } } diff --git a/app/src/main/java/org/stepik/android/view/course_info/ui/fragment/CourseInfoFragment.kt b/app/src/main/java/org/stepik/android/view/course_info/ui/fragment/CourseInfoFragment.kt index f8f4ff9d66..ca6976940c 100644 --- a/app/src/main/java/org/stepik/android/view/course_info/ui/fragment/CourseInfoFragment.kt +++ b/app/src/main/java/org/stepik/android/view/course_info/ui/fragment/CourseInfoFragment.kt @@ -9,8 +9,9 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.fragment_course_info.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.FragmentCourseInfoBinding import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager import org.stepik.android.model.user.User @@ -32,6 +33,7 @@ import ru.nobird.android.view.base.ui.extension.argument import javax.inject.Inject class CourseInfoFragment : Fragment(), CourseInfoView { + private val courseInfoBinding: FragmentCourseInfoBinding by viewBinding(FragmentCourseInfoBinding::bind) companion object { fun newInstance(courseId: Long): Fragment = CourseInfoFragment().apply { @@ -87,10 +89,10 @@ class CourseInfoFragment : Fragment(), CourseInfoView { inflater.inflate(R.layout.fragment_course_info, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - courseInfoRecycler.layoutManager = LinearLayoutManager(context) - courseInfoRecycler.adapter = courseInfoAdapter + courseInfoBinding.courseInfoRecycler.layoutManager = LinearLayoutManager(context) + courseInfoBinding.courseInfoRecycler.adapter = courseInfoAdapter - courseInfoRecycler.addItemDecoration( + courseInfoBinding.courseInfoRecycler.addItemDecoration( CourseInfoDividerDecorator( ContextCompat.getColor(requireContext(), R.color.color_divider), CourseInfoDividerDecorator.SeparatorSize(resources.getDimensionPixelSize(R.dimen.comment_item_separator_small)) @@ -98,8 +100,8 @@ class CourseInfoFragment : Fragment(), CourseInfoView { ) viewStateDelegate = ViewStateDelegate() - viewStateDelegate.addState(courseInfoLoadingPlaceholder) - viewStateDelegate.addState(courseInfoRecycler) + viewStateDelegate.addState(courseInfoBinding.courseInfoLoadingPlaceholder) + viewStateDelegate.addState(courseInfoBinding.courseInfoRecycler) } override fun onStart() { diff --git a/app/src/main/java/org/stepik/android/view/course_list/delegate/CourseCollectionAuthorListAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/course_list/delegate/CourseCollectionAuthorListAdapterDelegate.kt index 39b162a4b0..e021378c87 100644 --- a/app/src/main/java/org/stepik/android/view/course_list/delegate/CourseCollectionAuthorListAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course_list/delegate/CourseCollectionAuthorListAdapterDelegate.kt @@ -4,9 +4,9 @@ import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.LinearSnapHelper import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_collection_horizontal_list.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemCollectionHorizontalListBinding import org.stepik.android.domain.catalog.model.CatalogAuthor import org.stepik.android.domain.course_list.model.CourseListItem import org.stepik.android.view.base.ui.adapter.layoutmanager.TableLayoutManager @@ -26,9 +26,8 @@ class CourseCollectionAuthorListAdapterDelegate( override fun onCreateViewHolder(parent: ViewGroup): DelegateViewHolder = AuthorListViewHolder(createView(parent, R.layout.item_collection_horizontal_list)) - private inner class AuthorListViewHolder( - override val containerView: View - ) : DelegateViewHolder(containerView), LayoutContainer { + private inner class AuthorListViewHolder(root: View) : DelegateViewHolder(root) { + private val viewBinding: ItemCollectionHorizontalListBinding by viewBinding { ItemCollectionHorizontalListBinding.bind(root) } private val adapter = DefaultDelegateAdapter() .also { @@ -37,7 +36,7 @@ class CourseCollectionAuthorListAdapterDelegate( init { val rowCount = context.resources.getInteger(R.integer.author_lists_default_rows) - horizontalListRecycler.layoutManager = + viewBinding.horizontalListRecycler.layoutManager = TableLayoutManager( context, horizontalSpanCount = context.resources.getInteger(R.integer.author_lists_default_columns), @@ -45,18 +44,18 @@ class CourseCollectionAuthorListAdapterDelegate( orientation = RecyclerView.HORIZONTAL, reverseLayout = false ) - horizontalListRecycler.setRecycledViewPool(sharedViewPool) - horizontalListRecycler.setHasFixedSize(true) - horizontalListRecycler.adapter = adapter + viewBinding.horizontalListRecycler.setRecycledViewPool(sharedViewPool) + viewBinding.horizontalListRecycler.setHasFixedSize(true) + viewBinding.horizontalListRecycler.adapter = adapter val snapHelper = LinearSnapHelper() - snapHelper.attachToRecyclerView(horizontalListRecycler) + snapHelper.attachToRecyclerView(viewBinding.horizontalListRecycler) } override fun onBind(data: CourseListItem) { data as CourseListItem.SimilarAuthors - containerTitle.setText(R.string.authors_title) + viewBinding.containerTitle.setText(R.string.authors_title) adapter.items = data.similarAuthors } diff --git a/app/src/main/java/org/stepik/android/view/course_list/delegate/CourseCollectionSimilarCoursesListAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/course_list/delegate/CourseCollectionSimilarCoursesListAdapterDelegate.kt index 9968493d1b..0af7791a70 100644 --- a/app/src/main/java/org/stepik/android/view/course_list/delegate/CourseCollectionSimilarCoursesListAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course_list/delegate/CourseCollectionSimilarCoursesListAdapterDelegate.kt @@ -5,9 +5,9 @@ import android.view.ViewGroup import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearSnapHelper import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_collection_horizontal_list.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemCollectionHorizontalListBinding import org.stepik.android.domain.catalog.model.CatalogCourseList import org.stepik.android.domain.course_list.model.CourseListItem import org.stepik.android.view.base.ui.adapter.layoutmanager.TableLayoutManager @@ -29,9 +29,9 @@ class CourseCollectionSimilarCoursesListAdapterDelegate( override fun onCreateViewHolder(parent: ViewGroup): DelegateViewHolder = ViewHolder(createView(parent, R.layout.item_collection_horizontal_list)) - private inner class ViewHolder( - override val containerView: View - ) : DelegateViewHolder(containerView), LayoutContainer { + private inner class ViewHolder(root: View) : DelegateViewHolder(root) { + private val viewBinding: ItemCollectionHorizontalListBinding by viewBinding { ItemCollectionHorizontalListBinding.bind(root) } + private val adapter = DefaultDelegateAdapter() .also { it += SimpleCourseListDefaultAdapterDelegate(courseCountMapper, onCourseListClicked) @@ -39,7 +39,7 @@ class CourseCollectionSimilarCoursesListAdapterDelegate( init { val rowCount = 1 - horizontalListRecycler.layoutManager = + viewBinding.horizontalListRecycler.layoutManager = TableLayoutManager( context, horizontalSpanCount = context.resources.getInteger(R.integer.simple_course_lists_default_columns), @@ -47,18 +47,18 @@ class CourseCollectionSimilarCoursesListAdapterDelegate( orientation = LinearLayoutManager.HORIZONTAL, reverseLayout = false ) - horizontalListRecycler.setRecycledViewPool(sharedViewPool) - horizontalListRecycler.setHasFixedSize(true) - horizontalListRecycler.adapter = adapter + viewBinding.horizontalListRecycler.setRecycledViewPool(sharedViewPool) + viewBinding.horizontalListRecycler.setHasFixedSize(true) + viewBinding.horizontalListRecycler.adapter = adapter val snapHelper = LinearSnapHelper() - snapHelper.attachToRecyclerView(horizontalListRecycler) + snapHelper.attachToRecyclerView(viewBinding.horizontalListRecycler) } override fun onBind(data: CourseListItem) { data as CourseListItem.SimilarCourses - containerTitle.setText(R.string.similar_courses_title) + viewBinding.containerTitle.setText(R.string.similar_courses_title) adapter.items = data.similarCourses } diff --git a/app/src/main/java/org/stepik/android/view/course_list/delegate/CourseListViewDelegate.kt b/app/src/main/java/org/stepik/android/view/course_list/delegate/CourseListViewDelegate.kt index 42a5dc11a0..bf8e15e797 100644 --- a/app/src/main/java/org/stepik/android/view/course_list/delegate/CourseListViewDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course_list/delegate/CourseListViewDelegate.kt @@ -1,8 +1,8 @@ package org.stepik.android.view.course_list.delegate import android.view.View +import android.widget.TextView import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.item_course_list.view.* import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.ui.custom.StepikSwipeRefreshLayout @@ -38,7 +38,7 @@ class CourseListViewDelegate( itemAdapterDelegateType: ItemAdapterDelegateType = ItemAdapterDelegateType.STANDARD // TODO Hacky way ) : CourseListView, CourseContinueView by courseContinueViewDelegate { - private val courseListCounter = courseListTitleContainer?.containerCarouselCount + private val courseListCounter = courseListTitleContainer?.findViewById(R.id.containerCarouselCount) private val courseItemAdapter: DefaultDelegateAdapter = DefaultDelegateAdapter() private val courseItemsSkeleton: List diff --git a/app/src/main/java/org/stepik/android/view/course_list/ui/activity/CourseListUserActivity.kt b/app/src/main/java/org/stepik/android/view/course_list/ui/activity/CourseListUserActivity.kt index 8f67cba82c..c4b9bdda98 100644 --- a/app/src/main/java/org/stepik/android/view/course_list/ui/activity/CourseListUserActivity.kt +++ b/app/src/main/java/org/stepik/android/view/course_list/ui/activity/CourseListUserActivity.kt @@ -4,7 +4,8 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.view.MenuItem -import kotlinx.android.synthetic.main.activity_course_list_user.* +import com.google.android.material.tabs.TabLayout +import androidx.viewpager.widget.ViewPager import org.stepic.droid.R import org.stepic.droid.base.FragmentActivityBase import org.stepic.droid.ui.util.initCenteredToolbar @@ -36,7 +37,9 @@ class CourseListUserActivity : FragmentActivityBase() { private fun initViewPager() { courseListUserPagerAdapter = CourseListUserPagerAdapter(this, supportFragmentManager) - userCourseListsPager.adapter = courseListUserPagerAdapter - userCourseListsTabs.setupWithViewPager(userCourseListsPager) + val pager = findViewById(R.id.userCourseListsPager) + val tabs = findViewById(R.id.userCourseListsTabs) + pager.adapter = courseListUserPagerAdapter + tabs.setupWithViewPager(pager) } } \ No newline at end of file diff --git a/app/src/main/java/org/stepik/android/view/course_list/ui/adapter/delegate/CourseListItemAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/course_list/ui/adapter/delegate/CourseListItemAdapterDelegate.kt index 5115a87f1e..292a99d945 100644 --- a/app/src/main/java/org/stepik/android/view/course_list/ui/adapter/delegate/CourseListItemAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course_list/ui/adapter/delegate/CourseListItemAdapterDelegate.kt @@ -9,10 +9,11 @@ import androidx.core.text.buildSpannedString import androidx.core.text.strikeThrough import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams +import by.kirich1409.viewbindingdelegate.viewBinding import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.item_course.view.* import org.stepic.droid.R import org.stepic.droid.analytic.Analytic +import org.stepic.droid.databinding.ItemCourseBinding import org.stepic.droid.util.DateTimeHelper import org.stepik.android.domain.course.analytic.CourseCardSeenAnalyticEvent import org.stepik.android.domain.course.analytic.batch.CourseCardSeenAnalyticBatchEvent @@ -49,16 +50,18 @@ class CourseListItemAdapterDelegate( } private inner class ViewHolder(root: View) : DelegateViewHolder(root) { - private val courseCertificateProgress = root.courseCertificateProgress - private val coursePropertiesDelegate = CoursePropertiesDelegate(root, root.coursePropertiesContainer as ViewGroup) - private val courseItemImage = root.courseItemImage - private val courseItemName = root.courseItemName - private val adaptiveCourseMarker = root.adaptiveCourseMarker - private val courseContinueButton = root.courseContinueButton - private val courseDescription = root.courseDescription - private val courseButtonSeparator = root.courseButtonSeparator - private val courseOldPrice = root.courseOldPrice - private val coursePrice = root.coursePrice + private val viewBinding: ItemCourseBinding by viewBinding { ItemCourseBinding.bind(root) } + + private val courseCertificateProgress = viewBinding.courseCertificateProgress + private val coursePropertiesDelegate = CoursePropertiesDelegate(root, viewBinding.coursePropertiesContainer.root as ViewGroup) + private val courseItemImage = viewBinding.courseItemImage + private val courseItemName = viewBinding.courseItemName + private val adaptiveCourseMarker = viewBinding.adaptiveCourseMarker + private val courseContinueButton = viewBinding.courseContinueButton + private val courseDescription = viewBinding.courseDescription + private val courseButtonSeparator = viewBinding.courseButtonSeparator.root + private val courseOldPrice = viewBinding.courseOldPrice + private val coursePrice = viewBinding.coursePrice init { root.setOnClickListener { (itemData as? CourseListItem.Data)?.let(onItemClicked) } @@ -165,4 +168,4 @@ class CourseListItemAdapterDelegate( } else { R.color.color_overlay_violet to enrollmentState?.standardLightSku?.price } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_list/ui/adapter/delegate/CourseListViewAllAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/course_list/ui/adapter/delegate/CourseListViewAllAdapterDelegate.kt index d2cba5af22..4f1735b7c5 100644 --- a/app/src/main/java/org/stepik/android/view/course_list/ui/adapter/delegate/CourseListViewAllAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course_list/ui/adapter/delegate/CourseListViewAllAdapterDelegate.kt @@ -2,7 +2,6 @@ package org.stepik.android.view.course_list.ui.adapter.delegate import android.view.View import android.view.ViewGroup -import kotlinx.android.extensions.LayoutContainer import org.stepic.droid.R import org.stepik.android.domain.course_list.model.CourseListItem import ru.nobird.android.ui.adapterdelegates.AdapterDelegate @@ -18,8 +17,8 @@ class CourseListViewAllAdapterDelegate( ViewHolder(createView(parent, R.layout.item_course_view_all)) private inner class ViewHolder( - override val containerView: View - ) : DelegateViewHolder(containerView), LayoutContainer { + containerView: View + ) : DelegateViewHolder(containerView) { init { containerView.setOnClickListener { onViewClick() } } diff --git a/app/src/main/java/org/stepik/android/view/course_list/ui/adapter/delegate/VisitedCourseListItemAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/course_list/ui/adapter/delegate/VisitedCourseListItemAdapterDelegate.kt index 5ee840a14c..abf4ff9513 100644 --- a/app/src/main/java/org/stepik/android/view/course_list/ui/adapter/delegate/VisitedCourseListItemAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course_list/ui/adapter/delegate/VisitedCourseListItemAdapterDelegate.kt @@ -8,13 +8,11 @@ import androidx.core.text.buildSpannedString import androidx.core.text.strikeThrough import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams +import by.kirich1409.viewbindingdelegate.viewBinding import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.item_course.view.courseItemImage -import kotlinx.android.synthetic.main.item_course.view.courseItemName -import kotlinx.android.synthetic.main.item_visited_course.view.* -import kotlinx.android.synthetic.main.item_visited_course.view.coursePrice import org.stepic.droid.R import org.stepic.droid.analytic.Analytic +import org.stepic.droid.databinding.ItemVisitedCourseBinding import org.stepic.droid.util.DateTimeHelper import org.stepik.android.domain.course.analytic.CourseCardSeenAnalyticEvent import org.stepik.android.domain.course.analytic.batch.CourseCardSeenAnalyticBatchEvent @@ -47,11 +45,7 @@ class VisitedCourseListItemAdapterDelegate( } private inner class ViewHolder(root: View) : DelegateViewHolder(root) { - private val courseItemImage = root.courseItemImage - private val courseListWishlist = root.courseListWishlist - private val courseItemName = root.courseItemName - private val courseItemOldPrice = root.courseOldPrice - private val courseItemPrice = root.coursePrice + private val viewBinding: ItemVisitedCourseBinding by viewBinding { ItemVisitedCourseBinding.bind(root) } init { root.setOnClickListener { (itemData as? CourseListItem.Data)?.let(onItemClicked) } @@ -66,9 +60,9 @@ class VisitedCourseListItemAdapterDelegate( .load(data.course.cover) .placeholder(R.drawable.general_placeholder) .fitCenter() - .into(courseItemImage) + .into(viewBinding.courseItemImage) - courseItemName.text = data.course.title + viewBinding.courseItemName.text = data.course.title val defaultPromoCode = defaultPromoCodeMapper.mapToDefaultPromoCode(data.course) val mustShowDefaultPromoCode = defaultPromoCode != DefaultPromoCode.EMPTY && @@ -92,11 +86,11 @@ class VisitedCourseListItemAdapterDelegate( R.color.color_overlay_green to context.resources.getString(R.string.course_list_free) } - courseItemPrice.setTextColor(AppCompatResources.getColorStateList(context, textColor)) - courseItemPrice.text = displayPrice + viewBinding.coursePrice.setTextColor(AppCompatResources.getColorStateList(context, textColor)) + viewBinding.coursePrice.text = displayPrice - courseItemOldPrice.isVisible = mustShowDefaultPromoCode && !isEnrolled - courseItemOldPrice.text = buildSpannedString { + viewBinding.courseOldPrice.isVisible = mustShowDefaultPromoCode && !isEnrolled + viewBinding.courseOldPrice.text = buildSpannedString { strikeThrough { if (data.courseStats.enrollmentState is EnrollmentState.NotEnrolledMobileTier) { append(data.courseStats.enrollmentState.standardLightSku.price) @@ -106,7 +100,7 @@ class VisitedCourseListItemAdapterDelegate( } } - courseListWishlist.isVisible = !isEnrolled && data.course.isInWishlist + viewBinding.courseListWishlist.isVisible = !isEnrolled && data.course.isInWishlist analytic.report(CourseCardSeenAnalyticEvent(data.course.id, data.source)) analytic.report(CourseCardSeenAnalyticBatchEvent(data.course.id, data.source)) @@ -126,4 +120,4 @@ class VisitedCourseListItemAdapterDelegate( } else { R.color.color_overlay_violet to enrollmentState.standardLightSku.price } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_list/ui/delegate/CoursePropertiesDelegate.kt b/app/src/main/java/org/stepik/android/view/course_list/ui/delegate/CoursePropertiesDelegate.kt index f33b3c2179..3126a640d2 100644 --- a/app/src/main/java/org/stepik/android/view/course_list/ui/delegate/CoursePropertiesDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course_list/ui/delegate/CoursePropertiesDelegate.kt @@ -4,9 +4,9 @@ import android.view.View import android.view.ViewGroup import androidx.core.view.children import androidx.core.view.isVisible -import kotlinx.android.synthetic.main.item_course.view.* -import kotlinx.android.synthetic.main.layout_course_properties.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.ItemCourseBinding +import org.stepic.droid.databinding.LayoutCoursePropertiesBinding import org.stepic.droid.util.TextUtil import org.stepik.android.domain.course.model.CourseStats import org.stepik.android.domain.course.model.EnrollmentState @@ -20,20 +20,23 @@ class CoursePropertiesDelegate( root: View, private val view: ViewGroup ) { - private val learnersCountImage = view.learnersCountImage - private val learnersCountText = view.learnersCountText + private val rootBinding = ItemCourseBinding.bind(root) + private val propertiesBinding = LayoutCoursePropertiesBinding.bind(view) - private val courseRatingImage = view.courseRatingImage - private val courseRatingText = view.courseRatingText + private val learnersCountImage = propertiesBinding.learnersCountImage + private val learnersCountText = propertiesBinding.learnersCountText - private val courseCertificateImage = view.courseCertificateImage - private val courseCertificateText = view.courseCertificateText + private val courseRatingImage = propertiesBinding.courseRatingImage + private val courseRatingText = propertiesBinding.courseRatingText - private val courseArchiveImage = view.courseArchiveImage - private val courseArchiveText = view.courseArchiveText + private val courseCertificateImage = propertiesBinding.courseCertificateImage + private val courseCertificateText = propertiesBinding.courseCertificateText - private val courseFavoriteImage = root.courseListFavorite - private val courseWishlistImage = root.courseListWishlist + private val courseArchiveImage = propertiesBinding.courseArchiveImage + private val courseArchiveText = propertiesBinding.courseArchiveText + + private val courseFavoriteImage = rootBinding.courseListFavorite + private val courseWishlistImage = rootBinding.courseListWishlist fun setStats(courseListItem: CourseListItem.Data) { setLearnersCount(courseListItem.course.learnersCount, courseListItem.course.enrollment > 0L) @@ -81,4 +84,4 @@ class CoursePropertiesDelegate( private fun setWishlist(isEnrolled: Boolean, isWishlisted: Boolean) { courseWishlistImage.isVisible = !isEnrolled && isWishlisted } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListCollectionFragment.kt b/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListCollectionFragment.kt index 1bfb183a3c..058132df61 100644 --- a/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListCollectionFragment.kt +++ b/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListCollectionFragment.kt @@ -6,16 +6,15 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.GridLayoutManager -import kotlinx.android.synthetic.main.empty_search.* -import kotlinx.android.synthetic.main.error_no_connection_with_button.* -import kotlinx.android.synthetic.main.fragment_course_list.* -import kotlinx.android.synthetic.main.view_centered_toolbar.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.FragmentCourseListBinding import org.stepic.droid.model.CollectionDescriptionColors import org.stepic.droid.ui.util.initCenteredToolbar +import org.stepic.droid.ui.util.setTitleToCenteredToolbar import org.stepik.android.domain.course.analytic.CourseViewSource import org.stepik.android.domain.course_payments.mapper.DefaultPromoCodeMapper import org.stepik.android.domain.last_step.model.LastStep @@ -64,6 +63,8 @@ class CourseListCollectionFragment : Fragment(R.layout.fragment_course_list), Co @Inject internal lateinit var displayPriceMapper: DisplayPriceMapper + private val courseListBinding: FragmentCourseListBinding by viewBinding(FragmentCourseListBinding::bind) + private lateinit var courseListViewDelegate: CourseListViewDelegate private val courseListPresenter: CourseListCollectionPresenter by viewModels { viewModelFactory } @@ -80,7 +81,7 @@ class CourseListCollectionFragment : Fragment(R.layout.fragment_course_list), Co initCenteredToolbar(R.string.catalog_title, true) courseListCollectionHeaderDecoration = CourseListCollectionHeaderDecoration() - with(courseListCoursesRecycler) { + with(courseListBinding.courseListCoursesRecycler) { layoutManager = GridLayoutManager(context, resources.getInteger(R.integer.course_list_columns)) itemAnimator = null addItemDecoration(courseListCollectionHeaderDecoration) @@ -92,16 +93,16 @@ class CourseListCollectionFragment : Fragment(R.layout.fragment_course_list), Co enforceSingleScrollDirection() } - goToCatalog.setOnClickListener { screenManager.showCatalog(requireContext()) } - courseListSwipeRefresh.setOnRefreshListener { courseListPresenter.fetchCourses(courseCollectionId = courseCollectionId, forceUpdate = true) } - tryAgain.setOnClickListener { courseListPresenter.fetchCourses(courseCollectionId = courseCollectionId, forceUpdate = true) } + courseListBinding.courseListCoursesEmpty.goToCatalog.setOnClickListener { screenManager.showCatalog(requireContext()) } + courseListBinding.courseListSwipeRefresh.setOnRefreshListener { courseListPresenter.fetchCourses(courseCollectionId = courseCollectionId, forceUpdate = true) } + courseListBinding.courseListCoursesLoadingErrorVertical.tryAgain.setOnClickListener { courseListPresenter.fetchCourses(courseCollectionId = courseCollectionId, forceUpdate = true) } val viewStateDelegate = ViewStateDelegate() viewStateDelegate.addState() - viewStateDelegate.addState(courseListCoursesRecycler) - viewStateDelegate.addState(courseListCoursesRecycler) - viewStateDelegate.addState(courseListCoursesEmpty) - viewStateDelegate.addState(courseListCoursesLoadingErrorVertical) + viewStateDelegate.addState(courseListBinding.courseListCoursesRecycler) + viewStateDelegate.addState(courseListBinding.courseListCoursesRecycler) + viewStateDelegate.addState(courseListBinding.courseListCoursesEmpty.root) + viewStateDelegate.addState(courseListBinding.courseListCoursesLoadingErrorVertical.root) courseListViewDelegate = CourseListViewDelegate( analytic = analytic, @@ -110,8 +111,8 @@ class CourseListCollectionFragment : Fragment(R.layout.fragment_course_list), Co analytic = analytic, screenManager = screenManager ), - courseListSwipeRefresh = courseListSwipeRefresh, - courseItemsRecyclerView = courseListCoursesRecycler, + courseListSwipeRefresh = courseListBinding.courseListSwipeRefresh, + courseItemsRecyclerView = courseListBinding.courseListCoursesRecycler, courseListViewStateDelegate = viewStateDelegate, onContinueCourseClicked = { courseListItem -> courseListPresenter @@ -128,7 +129,7 @@ class CourseListCollectionFragment : Fragment(R.layout.fragment_course_list), Co courseCountMapper = courseCountMapper, isVerticalCourseCollection = true ) - courseListCoursesRecycler.setPadding(0, + courseListBinding.courseListCoursesRecycler.setPadding(0, resources.getDimensionPixelOffset(R.dimen.vertical_course_collection_padding), 0, resources.getDimensionPixelOffset(R.dimen.vertical_course_collection_padding) @@ -153,7 +154,7 @@ class CourseListCollectionFragment : Fragment(R.layout.fragment_course_list), Co courseListCollectionHeaderDecoration.collectionDescriptionColors = CollectionDescriptionColors.ofCollection(state.courseCollection) courseListCollectionHeaderDecoration.headerText = state.courseCollection.description.takeIf { it.isNotEmpty() } - centeredToolbarTitle.text = state.courseCollection.title + setTitleToCenteredToolbar(state.courseCollection.title) courseListViewDelegate.setState(state.courseListViewState) } is CourseListCollectionView.State.NetworkError -> { @@ -187,4 +188,4 @@ class CourseListCollectionFragment : Fragment(R.layout.fragment_course_list), Co courseListPresenter.detachView(this) super.onStop() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListPopularFragment.kt b/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListPopularFragment.kt index f915abbfe8..da3be32281 100644 --- a/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListPopularFragment.kt +++ b/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListPopularFragment.kt @@ -8,11 +8,12 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearSnapHelper import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.item_course_list.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.ItemCourseListBinding import org.stepic.droid.preferences.SharedPreferenceHelper import org.stepik.android.domain.course.analytic.CourseViewSource import org.stepik.android.domain.course_list.model.CourseListQuery @@ -61,6 +62,8 @@ class CourseListPopularFragment : Fragment(R.layout.item_course_list), CourseLis @Inject internal lateinit var tableLayoutHorizontalSpanCountResolver: TableLayoutHorizontalSpanCountResolver + private val courseListBinding: ItemCourseListBinding by viewBinding(ItemCourseListBinding::bind) + private lateinit var courseListViewDelegate: CourseListViewDelegate private val courseListQueryPresenter: CourseListQueryPresenter by viewModels { viewModelFactory } private lateinit var tableLayoutManager: TableLayoutManager @@ -73,14 +76,14 @@ class CourseListPopularFragment : Fragment(R.layout.item_course_list), CourseLis override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - containerCarouselCount.isVisible = false - containerTitle.text = resources.getString(R.string.course_list_popular_toolbar_title) + courseListBinding.containerCarouselCount.isVisible = false + courseListBinding.containerTitle.text = resources.getString(R.string.course_list_popular_toolbar_title) val rowCount = resources.getInteger(R.integer.course_list_rows) val columnsCount = resources.getInteger(R.integer.course_list_columns) tableLayoutManager = TableLayoutManager(requireContext(), columnsCount, rowCount, RecyclerView.HORIZONTAL, false) - with(courseListCoursesRecycler) { + with(courseListBinding.courseListCoursesRecycler) { layoutManager = tableLayoutManager itemAnimator?.changeDuration = 0 val snapHelper = LinearSnapHelper() @@ -99,7 +102,7 @@ class CourseListPopularFragment : Fragment(R.layout.item_course_list), CourseLis filterQuery = CourseListFilterQuery() ) - catalogBlockContainer.setOnClickListener { + courseListBinding.catalogBlockContainer.setOnClickListener { screenManager.showCoursesByQuery( requireContext(), resources.getString(R.string.course_list_popular_toolbar_title), @@ -107,18 +110,18 @@ class CourseListPopularFragment : Fragment(R.layout.item_course_list), CourseLis ) } - courseListPlaceholderEmpty.setOnClickListener { screenManager.showCatalog(requireContext()) } - courseListPlaceholderEmpty.setPlaceholderText(R.string.empty_courses_popular) - courseListPlaceholderNoConnection.setOnClickListener { courseListQueryPresenter.fetchCourses(courseListQuery = courseListQuery, forceUpdate = true) } - courseListPlaceholderNoConnection.setText(R.string.internet_problem) + courseListBinding.courseListPlaceholderEmpty.setOnClickListener { screenManager.showCatalog(requireContext()) } + courseListBinding.courseListPlaceholderEmpty.setPlaceholderText(R.string.empty_courses_popular) + courseListBinding.courseListPlaceholderNoConnection.setOnClickListener { courseListQueryPresenter.fetchCourses(courseListQuery = courseListQuery, forceUpdate = true) } + courseListBinding.courseListPlaceholderNoConnection.setText(R.string.internet_problem) val viewStateDelegate = ViewStateDelegate() - viewStateDelegate.addState(catalogBlockContainer) - viewStateDelegate.addState(catalogBlockContainer, courseListCoursesRecycler) - viewStateDelegate.addState(catalogBlockContainer, courseListCoursesRecycler) - viewStateDelegate.addState(courseListPlaceholderEmpty) - viewStateDelegate.addState(courseListPlaceholderNoConnection) + viewStateDelegate.addState(courseListBinding.catalogBlockContainer) + viewStateDelegate.addState(courseListBinding.catalogBlockContainer, courseListBinding.courseListCoursesRecycler) + viewStateDelegate.addState(courseListBinding.catalogBlockContainer, courseListBinding.courseListCoursesRecycler) + viewStateDelegate.addState(courseListBinding.courseListPlaceholderEmpty) + viewStateDelegate.addState(courseListBinding.courseListPlaceholderNoConnection) courseListViewDelegate = CourseListViewDelegate( analytic = analytic, @@ -127,8 +130,8 @@ class CourseListPopularFragment : Fragment(R.layout.item_course_list), CourseLis analytic = analytic, screenManager = screenManager ), - courseListTitleContainer = catalogBlockContainer, - courseItemsRecyclerView = courseListCoursesRecycler, + courseListTitleContainer = courseListBinding.catalogBlockContainer, + courseItemsRecyclerView = courseListBinding.courseListCoursesRecycler, courseListViewStateDelegate = viewStateDelegate, onContinueCourseClicked = { courseListItem -> courseListQueryPresenter @@ -192,4 +195,4 @@ class CourseListPopularFragment : Fragment(R.layout.item_course_list), CourseLis courseListQueryPresenter.detachView(this) super.onStop() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListQueryFragment.kt b/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListQueryFragment.kt index bc32609c42..5017c0d825 100644 --- a/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListQueryFragment.kt +++ b/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListQueryFragment.kt @@ -9,10 +9,9 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.GridLayoutManager -import kotlinx.android.synthetic.main.empty_search.* -import kotlinx.android.synthetic.main.error_no_connection_with_button.* -import kotlinx.android.synthetic.main.fragment_course_list.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.FragmentCourseListBinding import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager @@ -44,6 +43,8 @@ class CourseListQueryFragment : CourseListQueryView, FilterQueryView, FilterSearchBottomSheetDialogFragment.Callback { + private val binding: FragmentCourseListBinding by viewBinding(FragmentCourseListBinding::bind) + companion object { fun newInstance(courseListTitle: String, courseListQuery: CourseListQuery): Fragment = CourseListQueryFragment().apply { @@ -86,7 +87,7 @@ class CourseListQueryFragment : initCenteredToolbar(courseListTitle, true) - with(courseListCoursesRecycler) { + with(binding.courseListCoursesRecycler) { layoutManager = GridLayoutManager(context, resources.getInteger(R.integer.course_list_columns)) setOnPaginationListener { pageDirection -> if (pageDirection == PaginationDirection.NEXT) { @@ -95,16 +96,16 @@ class CourseListQueryFragment : } } - goToCatalog.setOnClickListener { screenManager.showCatalog(requireContext()) } - courseListSwipeRefresh.setOnRefreshListener { courseListQueryPresenter.fetchCourses(courseListQuery = courseListQuery, forceUpdate = true) } - tryAgain.setOnClickListener { courseListQueryPresenter.fetchCourses(courseListQuery = courseListQuery, forceUpdate = true) } + binding.courseListCoursesEmpty.goToCatalog.setOnClickListener { screenManager.showCatalog(requireContext()) } + binding.courseListSwipeRefresh.setOnRefreshListener { courseListQueryPresenter.fetchCourses(courseListQuery = courseListQuery, forceUpdate = true) } + binding.courseListCoursesLoadingErrorVertical.tryAgain.setOnClickListener { courseListQueryPresenter.fetchCourses(courseListQuery = courseListQuery, forceUpdate = true) } val viewStateDelegate = ViewStateDelegate() viewStateDelegate.addState() - viewStateDelegate.addState(courseListCoursesRecycler) - viewStateDelegate.addState(courseListCoursesRecycler) - viewStateDelegate.addState(courseListCoursesEmpty) - viewStateDelegate.addState(courseListCoursesLoadingErrorVertical) + viewStateDelegate.addState(binding.courseListCoursesRecycler) + viewStateDelegate.addState(binding.courseListCoursesRecycler) + viewStateDelegate.addState(binding.courseListCoursesEmpty.root) + viewStateDelegate.addState(binding.courseListCoursesLoadingErrorVertical.root) courseListViewDelegate = CourseListViewDelegate( analytic = analytic, @@ -113,8 +114,8 @@ class CourseListQueryFragment : analytic = analytic, screenManager = screenManager ), - courseListSwipeRefresh = courseListSwipeRefresh, - courseItemsRecyclerView = courseListCoursesRecycler, + courseListSwipeRefresh = binding.courseListSwipeRefresh, + courseItemsRecyclerView = binding.courseListCoursesRecycler, courseListViewStateDelegate = viewStateDelegate, onContinueCourseClicked = { courseListItem -> courseListQueryPresenter diff --git a/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListSearchFragment.kt b/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListSearchFragment.kt index d6e882deb8..8796e9e60e 100644 --- a/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListSearchFragment.kt +++ b/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListSearchFragment.kt @@ -12,19 +12,14 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.GridLayoutManager -import kotlinx.android.synthetic.main.empty_search.* -import kotlinx.android.synthetic.main.error_no_connection_with_button.* -import kotlinx.android.synthetic.main.fragment_course_list.* -import kotlinx.android.synthetic.main.view_centered_toolbar.* -import kotlinx.android.synthetic.main.view_search_toolbar.backIcon -import kotlinx.android.synthetic.main.view_search_toolbar.filterIcon -import kotlinx.android.synthetic.main.view_search_toolbar.searchViewToolbar +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager import org.stepic.droid.core.presenters.SearchSuggestionsPresenter import org.stepic.droid.core.presenters.contracts.SearchSuggestionsView +import org.stepic.droid.databinding.FragmentCourseListBinding import org.stepic.droid.model.SearchQuery import org.stepic.droid.model.SearchQuerySource import org.stepic.droid.preferences.SharedPreferenceHelper @@ -102,6 +97,23 @@ class CourseListSearchFragment : @Inject lateinit var searchSuggestionsPresenter: SearchSuggestionsPresenter + private val courseListBinding: FragmentCourseListBinding by viewBinding(FragmentCourseListBinding::bind) + + private val searchToolbar + inline get() = courseListBinding.courseListSearchToolbar + + private val centeredToolbar + inline get() = searchToolbar.viewCenteredToolbar.centeredToolbar + + private val searchViewToolbar + inline get() = searchToolbar.searchViewToolbar + + private val backIcon + inline get() = searchToolbar.backIcon + + private val filterIcon + inline get() = searchToolbar.filterIcon + private lateinit var courseListViewDelegate: CourseListViewDelegate private val courseListPresenter: CourseListSearchPresenter by viewModels { viewModelFactory } @@ -118,7 +130,7 @@ class CourseListSearchFragment : searchIcon = searchViewToolbar.findViewById(androidx.appcompat.R.id.search_mag_icon) as ImageView setupSearchBar() - with(courseListCoursesRecycler) { + with(courseListBinding.courseListCoursesRecycler) { layoutManager = GridLayoutManager(context, resources.getInteger(R.integer.course_list_columns)) setOnPaginationListener { pageDirection -> if (pageDirection == PaginationDirection.NEXT) { @@ -127,7 +139,7 @@ class CourseListSearchFragment : } } - goToCatalog.setOnClickListener { screenManager.showCatalog(requireContext()) } + courseListBinding.courseListCoursesEmpty.goToCatalog.setOnClickListener { screenManager.showCatalog(requireContext()) } val searchResultQuery = SearchResultQuery( page = 1, @@ -136,13 +148,13 @@ class CourseListSearchFragment : filterQuery = filterQuery, remoteQueryParams = searchResultRemoteQueryParamsMapper.buildRemoteQueryParams() ) - courseListSwipeRefresh.setOnRefreshListener { + courseListBinding.courseListSwipeRefresh.setOnRefreshListener { courseListPresenter.fetchCourses( searchResultQuery, forceUpdate = true ) } - tryAgain.setOnClickListener { + courseListBinding.courseListCoursesLoadingErrorVertical.tryAgain.setOnClickListener { courseListPresenter.fetchCourses( searchResultQuery, forceUpdate = true @@ -151,10 +163,10 @@ class CourseListSearchFragment : val viewStateDelegate = ViewStateDelegate() viewStateDelegate.addState() - viewStateDelegate.addState(courseListCoursesRecycler) - viewStateDelegate.addState(courseListCoursesRecycler) - viewStateDelegate.addState(courseListCoursesEmpty) - viewStateDelegate.addState(courseListCoursesLoadingErrorVertical) + viewStateDelegate.addState(courseListBinding.courseListCoursesRecycler) + viewStateDelegate.addState(courseListBinding.courseListCoursesRecycler) + viewStateDelegate.addState(courseListBinding.courseListCoursesEmpty.root) + viewStateDelegate.addState(courseListBinding.courseListCoursesLoadingErrorVertical.root) courseListViewDelegate = CourseListViewDelegate( analytic = analytic, @@ -163,8 +175,8 @@ class CourseListSearchFragment : analytic = analytic, screenManager = screenManager ), - courseListSwipeRefresh = courseListSwipeRefresh, - courseItemsRecyclerView = courseListCoursesRecycler, + courseListSwipeRefresh = courseListBinding.courseListSwipeRefresh, + courseItemsRecyclerView = courseListBinding.courseListCoursesRecycler, courseListViewStateDelegate = viewStateDelegate, onContinueCourseClicked = { courseListItem -> courseListPresenter @@ -208,7 +220,7 @@ class CourseListSearchFragment : private fun setupSearchView(searchView: AutoCompleteSearchView) { searchView.setSearchable(requireActivity()) - searchView.initSuggestions(rootView) + searchView.initSuggestions(courseListBinding.rootView) searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String): Boolean { @@ -297,4 +309,4 @@ class CourseListSearchFragment : override fun onQueryTextSubmitSuggestion(query: String) { searchViewToolbar.setQuery(query, true) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListUserFragment.kt b/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListUserFragment.kt index 15f3615a18..f7e0277586 100644 --- a/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListUserFragment.kt +++ b/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListUserFragment.kt @@ -8,14 +8,12 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.SimpleItemAnimator -import kotlinx.android.synthetic.main.empty_search.* -import kotlinx.android.synthetic.main.error_no_connection_with_button.view.* -import kotlinx.android.synthetic.main.fragment_course_list.* -import kotlinx.android.synthetic.main.view_catalog_search_toolbar.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.FragmentCourseListBinding import org.stepik.android.domain.course.analytic.CourseViewSource import org.stepik.android.domain.course_list.model.UserCourseQuery import org.stepik.android.domain.course_payments.mapper.DefaultPromoCodeMapper @@ -59,6 +57,11 @@ class CourseListUserFragment : Fragment(R.layout.fragment_course_list), CourseLi @Inject internal lateinit var displayPriceMapper: DisplayPriceMapper + private val courseListBinding: FragmentCourseListBinding by viewBinding(FragmentCourseListBinding::bind) + + private val searchToolbar + inline get() = courseListBinding.courseListSearchToolbar + private lateinit var courseListViewDelegate: CourseListViewDelegate private val courseListPresenter: CourseListUserPresenter by viewModels { viewModelFactory } private lateinit var wrapperViewStateDelegate: ViewStateDelegate @@ -71,10 +74,10 @@ class CourseListUserFragment : Fragment(R.layout.fragment_course_list), CourseLi override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - appBarLayout.isVisible = false - courseListUserSkeleton.isVisible = true + searchToolbar.root.isVisible = false + courseListBinding.courseListUserSkeleton.isVisible = true - with(courseListCoursesRecycler) { + with(courseListBinding.courseListCoursesRecycler) { layoutManager = GridLayoutManager(context, resources.getInteger(R.integer.course_list_columns)) setOnPaginationListener { pageDirection -> if (pageDirection == PaginationDirection.NEXT) { @@ -86,20 +89,20 @@ class CourseListUserFragment : Fragment(R.layout.fragment_course_list), CourseLi ?.supportsChangeAnimations = false } - goToCatalog.setOnClickListener { screenManager.showCatalog(requireContext()) } - courseListSwipeRefresh.setOnRefreshListener { + courseListBinding.courseListCoursesEmpty.goToCatalog.setOnClickListener { screenManager.showCatalog(requireContext()) } + courseListBinding.courseListSwipeRefresh.setOnRefreshListener { setDataToPresenter(forceUpdate = true) } - courseListCoursesLoadingErrorVertical.tryAgain.setOnClickListener { + courseListBinding.courseListCoursesLoadingErrorVertical.tryAgain.setOnClickListener { setDataToPresenter(forceUpdate = true) } val viewStateDelegate = ViewStateDelegate() viewStateDelegate.addState() - viewStateDelegate.addState(courseListCoursesRecycler) - viewStateDelegate.addState(courseListCoursesRecycler) - viewStateDelegate.addState(courseListCoursesEmpty) - viewStateDelegate.addState(courseListCoursesLoadingErrorVertical) + viewStateDelegate.addState(courseListBinding.courseListCoursesRecycler) + viewStateDelegate.addState(courseListBinding.courseListCoursesRecycler) + viewStateDelegate.addState(courseListBinding.courseListCoursesEmpty.root) + viewStateDelegate.addState(courseListBinding.courseListCoursesLoadingErrorVertical.root) courseListViewDelegate = CourseListViewDelegate( analytic = analytic, @@ -108,8 +111,8 @@ class CourseListUserFragment : Fragment(R.layout.fragment_course_list), CourseLi analytic = analytic, screenManager = screenManager ), - courseListSwipeRefresh = courseListSwipeRefresh, - courseItemsRecyclerView = courseListCoursesRecycler, + courseListSwipeRefresh = courseListBinding.courseListSwipeRefresh, + courseItemsRecyclerView = courseListBinding.courseListCoursesRecycler, courseListViewStateDelegate = viewStateDelegate, onContinueCourseClicked = { courseListItem -> courseListPresenter @@ -125,8 +128,8 @@ class CourseListUserFragment : Fragment(R.layout.fragment_course_list), CourseLi wrapperViewStateDelegate = ViewStateDelegate() wrapperViewStateDelegate.addState() - wrapperViewStateDelegate.addState(courseListUserSkeleton) - wrapperViewStateDelegate.addState(courseListCoursesLoadingErrorVertical) + wrapperViewStateDelegate.addState(courseListBinding.courseListUserSkeleton) + wrapperViewStateDelegate.addState(courseListBinding.courseListCoursesLoadingErrorVertical.root) wrapperViewStateDelegate.addState() setDataToPresenter() @@ -174,4 +177,4 @@ class CourseListUserFragment : Fragment(R.layout.fragment_course_list), CourseLi courseListPresenter.detachView(this) super.onStop() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListUserHorizontalFragment.kt b/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListUserHorizontalFragment.kt index f9d5e6daed..79cc41a112 100644 --- a/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListUserHorizontalFragment.kt +++ b/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListUserHorizontalFragment.kt @@ -7,10 +7,9 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearSnapHelper import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.fragment_user_course_list.* -import kotlinx.android.synthetic.main.view_user_course_list_empty.view.* -import kotlinx.android.synthetic.main.view_user_course_list_network_error.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.FragmentUserCourseListBinding import org.stepic.droid.analytic.Analytic import org.stepic.droid.analytic.experiments.OnboardingSplitTestVersion2 import org.stepic.droid.base.App @@ -36,6 +35,8 @@ import ru.nobird.android.view.base.ui.extension.setOnPaginationListener import javax.inject.Inject class CourseListUserHorizontalFragment : Fragment(R.layout.fragment_user_course_list), CourseListUserView { + private val binding: FragmentUserCourseListBinding by viewBinding(FragmentUserCourseListBinding::bind) + companion object { fun newInstance(): Fragment = CourseListUserHorizontalFragment() @@ -80,12 +81,12 @@ class CourseListUserHorizontalFragment : Fragment(R.layout.fragment_user_course_ override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - containerTitle.text = resources.getString(R.string.course_list_user_courses_title) + binding.containerTitle.text = resources.getString(R.string.course_list_user_courses_title) val rowCount = resources.getInteger(R.integer.course_list_rows) val columnsCount = resources.getInteger(R.integer.course_list_columns) tableLayoutManager = TableLayoutManager(requireContext(), columnsCount, rowCount, RecyclerView.HORIZONTAL, false) - with(courseListCoursesRecycler) { + with(binding.courseListCoursesRecycler) { layoutManager = tableLayoutManager itemAnimator?.changeDuration = 0 val snapHelper = LinearSnapHelper() @@ -97,7 +98,7 @@ class CourseListUserHorizontalFragment : Fragment(R.layout.fragment_user_course_ } } - catalogBlockContainer.setOnClickListener { screenManager.showUserCourses(requireContext()) } + binding.catalogBlockContainer.setOnClickListener { screenManager.showUserCourses(requireContext()) } /** * Empty user courses action @@ -120,21 +121,21 @@ class CourseListUserHorizontalFragment : Fragment(R.layout.fragment_user_course_ R.string.user_courses_catalog_action_message } - with(courseListPlaceholderEmpty.userCoursesListEmptyAction) { + with(binding.courseListPlaceholderEmpty.userCoursesListEmptyAction) { setOnClickListener(userCoursesListEmptyActionListener) setText(userCoursesListEmptyActionText) } - with(courseListPlaceholderEmptyWrapper.userCoursesListEmptyAction) { + with(binding.courseListPlaceholderEmptyWrapper.userCoursesListEmptyAction) { setOnClickListener(userCoursesListEmptyActionListener) setText(userCoursesListEmptyActionText) } - courseListPlaceholderNoConnection + binding.courseListPlaceholderNoConnection .userCoursesListNetworkErrorAction .setOnClickListener { setDataToPresenter(forceUpdate = true) } - courseListPlaceholderNoConnectionWrapper + binding.courseListPlaceholderNoConnectionWrapper .userCoursesListNetworkErrorAction .setOnClickListener { setDataToPresenter(forceUpdate = true) @@ -142,11 +143,11 @@ class CourseListUserHorizontalFragment : Fragment(R.layout.fragment_user_course_ val viewStateDelegate = ViewStateDelegate() - viewStateDelegate.addState(catalogBlockContainer, containerTitle) - viewStateDelegate.addState(catalogBlockContainer, containerTitle, courseListCoursesRecycler) - viewStateDelegate.addState(catalogBlockContainer, containerTitle, containerCarouselCount, containerViewAll, courseListCoursesRecycler) - viewStateDelegate.addState(catalogBlockContainer, containerTitle, courseListPlaceholderEmpty) - viewStateDelegate.addState(catalogBlockContainer, containerTitle, courseListPlaceholderNoConnection) + viewStateDelegate.addState(binding.catalogBlockContainer, binding.containerTitle) + viewStateDelegate.addState(binding.catalogBlockContainer, binding.containerTitle, binding.courseListCoursesRecycler) + viewStateDelegate.addState(binding.catalogBlockContainer, binding.containerTitle, binding.containerCarouselCount, binding.containerViewAll, binding.courseListCoursesRecycler) + viewStateDelegate.addState(binding.catalogBlockContainer, binding.containerTitle, binding.courseListPlaceholderEmpty.root) + viewStateDelegate.addState(binding.catalogBlockContainer, binding.containerTitle, binding.courseListPlaceholderNoConnection.root) courseListViewDelegate = CourseListViewDelegate( analytic = analytic, @@ -155,7 +156,7 @@ class CourseListUserHorizontalFragment : Fragment(R.layout.fragment_user_course_ analytic = analytic, screenManager = screenManager ), - courseItemsRecyclerView = courseListCoursesRecycler, + courseItemsRecyclerView = binding.courseListCoursesRecycler, courseListViewStateDelegate = viewStateDelegate, onContinueCourseClicked = { courseListItem -> courseListPresenter @@ -171,9 +172,9 @@ class CourseListUserHorizontalFragment : Fragment(R.layout.fragment_user_course_ wrapperViewStateDelegate = ViewStateDelegate() wrapperViewStateDelegate.addState() - wrapperViewStateDelegate.addState(courseListUserSkeleton) - wrapperViewStateDelegate.addState(courseListPlaceholderEmptyWrapper) - wrapperViewStateDelegate.addState(courseListPlaceholderNoConnectionWrapper) + wrapperViewStateDelegate.addState(binding.courseListUserSkeleton) + wrapperViewStateDelegate.addState(binding.courseListPlaceholderEmptyWrapper.root) + wrapperViewStateDelegate.addState(binding.courseListPlaceholderNoConnectionWrapper.root) wrapperViewStateDelegate.addState() setDataToPresenter() @@ -191,9 +192,9 @@ class CourseListUserHorizontalFragment : Fragment(R.layout.fragment_user_course_ } override fun setState(state: CourseListUserView.State) { - catalogBlockContainer.isEnabled = (state as? CourseListUserView.State.Data)?.courseListViewState is CourseListView.State.Content + binding.catalogBlockContainer.isEnabled = (state as? CourseListUserView.State.Data)?.courseListViewState is CourseListView.State.Content if (state is CourseListUserView.State.Data) { - containerCarouselCount.text = requireContext().resources.getQuantityString( + binding.containerCarouselCount.text = requireContext().resources.getQuantityString( R.plurals.course_count, state.userCourses.size, state.userCourses.size diff --git a/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListVisitedFragment.kt b/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListVisitedFragment.kt index 3f7bfaa93d..2b25f5a696 100644 --- a/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListVisitedFragment.kt +++ b/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListVisitedFragment.kt @@ -6,12 +6,12 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.GridLayoutManager -import kotlinx.android.synthetic.main.fragment_course_list.* -import kotlinx.android.synthetic.main.fragment_course_list.courseListCoursesRecycler +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.FragmentCourseListBinding import org.stepic.droid.preferences.SharedPreferenceHelper import org.stepic.droid.ui.util.initCenteredToolbar import org.stepik.android.domain.course.analytic.CourseViewSource @@ -49,6 +49,8 @@ class CourseListVisitedFragment : Fragment(R.layout.fragment_course_list) { @Inject internal lateinit var displayPriceMapper: DisplayPriceMapper + private val courseListBinding: FragmentCourseListBinding by viewBinding(FragmentCourseListBinding::bind) + private lateinit var courseListViewDelegate: CourseListViewDelegate private val courseListVisitedPresenter: CourseListVisitedPresenter by viewModels { viewModelFactory } @@ -62,15 +64,15 @@ class CourseListVisitedFragment : Fragment(R.layout.fragment_course_list) { initCenteredToolbar(R.string.visited_courses_title, true) - courseListSwipeRefresh.isEnabled = false - courseListCoursesRecycler.layoutManager = GridLayoutManager(context, resources.getInteger(R.integer.course_list_columns)) + courseListBinding.courseListSwipeRefresh.isEnabled = false + courseListBinding.courseListCoursesRecycler.layoutManager = GridLayoutManager(context, resources.getInteger(R.integer.course_list_columns)) val viewStateDelegate = ViewStateDelegate() viewStateDelegate.addState() - viewStateDelegate.addState(courseListCoursesRecycler) - viewStateDelegate.addState(courseListCoursesRecycler) - viewStateDelegate.addState(courseListCoursesEmpty) - viewStateDelegate.addState(courseListCoursesLoadingErrorVertical) + viewStateDelegate.addState(courseListBinding.courseListCoursesRecycler) + viewStateDelegate.addState(courseListBinding.courseListCoursesRecycler) + viewStateDelegate.addState(courseListBinding.courseListCoursesEmpty.root) + viewStateDelegate.addState(courseListBinding.courseListCoursesLoadingErrorVertical.root) courseListViewDelegate = CourseListViewDelegate( analytic = analytic, @@ -79,7 +81,7 @@ class CourseListVisitedFragment : Fragment(R.layout.fragment_course_list) { analytic = analytic, screenManager = screenManager ), - courseItemsRecyclerView = courseListCoursesRecycler, + courseItemsRecyclerView = courseListBinding.courseListCoursesRecycler, courseListViewStateDelegate = viewStateDelegate, onContinueCourseClicked = { courseListItem -> courseListVisitedPresenter @@ -113,4 +115,4 @@ class CourseListVisitedFragment : Fragment(R.layout.fragment_course_list) { courseListVisitedPresenter.detachView(courseListViewDelegate) super.onStop() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListVisitedHorizontalFragment.kt b/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListVisitedHorizontalFragment.kt index 80a16f3e35..ed735d7b0b 100644 --- a/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListVisitedHorizontalFragment.kt +++ b/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListVisitedHorizontalFragment.kt @@ -7,11 +7,12 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.item_course_list.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.ItemCourseListBinding import org.stepic.droid.preferences.SharedPreferenceHelper import org.stepic.droid.ui.util.CoursesSnapHelper import org.stepik.android.domain.course.analytic.CourseViewSource @@ -49,6 +50,8 @@ class CourseListVisitedHorizontalFragment : Fragment(R.layout.item_course_list) @Inject internal lateinit var displayPriceMapper: DisplayPriceMapper + private val courseListBinding: ItemCourseListBinding by viewBinding(ItemCourseListBinding::bind) + private lateinit var courseListViewDelegate: CourseListViewDelegate private val courseListVisitedPresenter: CourseListVisitedPresenter by viewModels { viewModelFactory } @@ -60,27 +63,27 @@ class CourseListVisitedHorizontalFragment : Fragment(R.layout.item_course_list) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - containerCarouselCount.isVisible = false - courseListPlaceholderNoConnection.isVisible = false - courseListPlaceholderEmpty.isVisible = false - containerTitle.text = resources.getString(R.string.visited_courses_title) + courseListBinding.containerCarouselCount.isVisible = false + courseListBinding.courseListPlaceholderNoConnection.isVisible = false + courseListBinding.courseListPlaceholderEmpty.isVisible = false + courseListBinding.containerTitle.text = resources.getString(R.string.visited_courses_title) - with(courseListCoursesRecycler) { + with(courseListBinding.courseListCoursesRecycler) { layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false) itemAnimator?.changeDuration = 0 val snapHelper = CoursesSnapHelper(1) snapHelper.attachToRecyclerView(this) } - catalogBlockContainer.setOnClickListener { + courseListBinding.catalogBlockContainer.setOnClickListener { screenManager.showVisitedCourses(requireContext()) } val viewStateDelegate = ViewStateDelegate() viewStateDelegate.addState() - viewStateDelegate.addState(view, catalogBlockContainer, courseListCoursesRecycler) - viewStateDelegate.addState(view, catalogBlockContainer, courseListCoursesRecycler) + viewStateDelegate.addState(view, courseListBinding.catalogBlockContainer, courseListBinding.courseListCoursesRecycler) + viewStateDelegate.addState(view, courseListBinding.catalogBlockContainer, courseListBinding.courseListCoursesRecycler) viewStateDelegate.addState() viewStateDelegate.addState() @@ -91,8 +94,8 @@ class CourseListVisitedHorizontalFragment : Fragment(R.layout.item_course_list) analytic = analytic, screenManager = screenManager ), - courseListTitleContainer = catalogBlockContainer, - courseItemsRecyclerView = courseListCoursesRecycler, + courseListTitleContainer = courseListBinding.catalogBlockContainer, + courseItemsRecyclerView = courseListBinding.courseListCoursesRecycler, courseListViewStateDelegate = viewStateDelegate, onContinueCourseClicked = { courseListItem -> courseListVisitedPresenter @@ -126,4 +129,4 @@ class CourseListVisitedHorizontalFragment : Fragment(R.layout.item_course_list) courseListVisitedPresenter.detachView(courseListViewDelegate) super.onStop() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListWishFragment.kt b/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListWishFragment.kt index 9f31e52808..ddfcca9fc7 100644 --- a/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListWishFragment.kt +++ b/app/src/main/java/org/stepik/android/view/course_list/ui/fragment/CourseListWishFragment.kt @@ -6,14 +6,12 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.GridLayoutManager -import kotlinx.android.synthetic.main.empty_search.* -import kotlinx.android.synthetic.main.error_no_connection_with_button.* -import kotlinx.android.synthetic.main.fragment_course_list.* -import kotlinx.android.synthetic.main.fragment_course_list.courseListCoursesRecycler +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.FragmentCourseListBinding import org.stepic.droid.preferences.SharedPreferenceHelper import org.stepic.droid.ui.util.initCenteredToolbar import org.stepik.android.domain.course.analytic.CourseViewSource @@ -57,6 +55,8 @@ class CourseListWishFragment : Fragment(R.layout.fragment_course_list), CourseLi @Inject internal lateinit var displayPriceMapper: DisplayPriceMapper + private val courseListBinding: FragmentCourseListBinding by viewBinding(FragmentCourseListBinding::bind) + private lateinit var courseListViewDelegate: CourseListViewDelegate private val courseListWishPresenter: CourseListWishPresenter by viewModels { viewModelFactory } @@ -71,7 +71,7 @@ class CourseListWishFragment : Fragment(R.layout.fragment_course_list), CourseLi initCenteredToolbar(R.string.wishlist_title, true) - with(courseListCoursesRecycler) { + with(courseListBinding.courseListCoursesRecycler) { layoutManager = GridLayoutManager(context, resources.getInteger(R.integer.course_list_columns)) itemAnimator = null setOnPaginationListener { pageDirection -> @@ -81,16 +81,16 @@ class CourseListWishFragment : Fragment(R.layout.fragment_course_list), CourseLi } } - goToCatalog.setOnClickListener { screenManager.showCatalog(requireContext()) } - courseListSwipeRefresh.setOnRefreshListener { courseListWishPresenter.fetchCourses(forceUpdate = true) } - tryAgain.setOnClickListener { courseListWishPresenter.fetchCourses(forceUpdate = true) } + courseListBinding.courseListCoursesEmpty.goToCatalog.setOnClickListener { screenManager.showCatalog(requireContext()) } + courseListBinding.courseListSwipeRefresh.setOnRefreshListener { courseListWishPresenter.fetchCourses(forceUpdate = true) } + courseListBinding.courseListCoursesLoadingErrorVertical.tryAgain.setOnClickListener { courseListWishPresenter.fetchCourses(forceUpdate = true) } val viewStateDelegate = ViewStateDelegate() viewStateDelegate.addState() - viewStateDelegate.addState(courseListCoursesRecycler) - viewStateDelegate.addState(courseListCoursesRecycler) - viewStateDelegate.addState(courseListCoursesEmpty) - viewStateDelegate.addState(courseListCoursesLoadingErrorVertical) + viewStateDelegate.addState(courseListBinding.courseListCoursesRecycler) + viewStateDelegate.addState(courseListBinding.courseListCoursesRecycler) + viewStateDelegate.addState(courseListBinding.courseListCoursesEmpty.root) + viewStateDelegate.addState(courseListBinding.courseListCoursesLoadingErrorVertical.root) courseListViewDelegate = CourseListViewDelegate( analytic = analytic, @@ -99,8 +99,8 @@ class CourseListWishFragment : Fragment(R.layout.fragment_course_list), CourseLi analytic = analytic, screenManager = screenManager ), - courseListSwipeRefresh = courseListSwipeRefresh, - courseItemsRecyclerView = courseListCoursesRecycler, + courseListSwipeRefresh = courseListBinding.courseListSwipeRefresh, + courseItemsRecyclerView = courseListBinding.courseListCoursesRecycler, courseListViewStateDelegate = viewStateDelegate, onContinueCourseClicked = { courseListItem -> courseListWishPresenter @@ -164,4 +164,4 @@ class CourseListWishFragment : Fragment(R.layout.fragment_course_list), CourseLi courseListWishPresenter.detachView(this) super.onStop() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_purchase/delegate/BuyActionViewDelegate.kt b/app/src/main/java/org/stepik/android/view/course_purchase/delegate/BuyActionViewDelegate.kt index c7756d0abf..0730bbfe72 100644 --- a/app/src/main/java/org/stepik/android/view/course_purchase/delegate/BuyActionViewDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course_purchase/delegate/BuyActionViewDelegate.kt @@ -17,7 +17,7 @@ import org.stepik.android.view.course.mapper.DisplayPriceMapper import ru.nobird.android.view.base.ui.extension.getDrawableCompat class BuyActionViewDelegate( - coursePurchaseBinding: BottomSheetDialogCoursePurchaseBinding, + private val binding: BottomSheetDialogCoursePurchaseBinding, private val coursePurchaseData: CoursePurchaseData, private val displayPriceMapper: DisplayPriceMapper, launchPurchaseFlowAction: () -> Unit, @@ -25,16 +25,6 @@ class BuyActionViewDelegate( launchRestoreAction: () -> Unit, closeDialog: () -> Unit ) { - private val context = coursePurchaseBinding.root.context - private val coursePurchaseBuyActionGreen = coursePurchaseBinding.coursePurchaseBuyActionGreen - private val coursePurchaseBuyActionViolet = coursePurchaseBinding.coursePurchaseBuyActionViolet - private val coursePurchaseTerminalAction = coursePurchaseBinding.coursePurchaseTerminalAction - private val coursePurchasePaymentIcon = coursePurchaseBinding.coursePurchasePaymentIcon - private val coursePurchasePaymentTitle = coursePurchaseBinding.coursePurchasePaymentTitle - private val coursePurchasePaymentFailureFeedback = coursePurchaseBinding.coursePurchasePaymentFailureFeedback - private val coursePurchasePaymentPendingFeedback = coursePurchaseBinding.coursePurchasePaymentPendingFeedback - private val coursePurchaseCommissionNotice = coursePurchaseBinding.coursePurchaseCommissionNotice - private var paymentState: CoursePurchaseFeature.PaymentState = CoursePurchaseFeature.PaymentState.Idle private val idleClickListener = View.OnClickListener { @@ -44,9 +34,9 @@ class BuyActionViewDelegate( } init { - coursePurchaseBuyActionGreen.setOnClickListener(idleClickListener) - coursePurchaseBuyActionViolet.setOnClickListener(idleClickListener) - coursePurchaseTerminalAction.setOnClickListener { + binding.coursePurchaseBuyActionGreen.setOnClickListener(idleClickListener) + binding.coursePurchaseBuyActionViolet.setOnClickListener(idleClickListener) + binding.coursePurchaseTerminalAction.setOnClickListener { when (paymentState) { is CoursePurchaseFeature.PaymentState.PaymentSuccess -> { launchStartStudying() @@ -69,21 +59,23 @@ class BuyActionViewDelegate( state.paymentState is CoursePurchaseFeature.PaymentState.PaymentPending || state.paymentState is CoursePurchaseFeature.PaymentState.PaymentSuccess - coursePurchasePaymentTitle.isVisible = isTerminalState - coursePurchasePaymentIcon.isVisible = isTerminalState - coursePurchasePaymentFailureFeedback.isVisible = state.paymentState is CoursePurchaseFeature.PaymentState.PaymentFailure - coursePurchasePaymentPendingFeedback.isVisible = state.paymentState is CoursePurchaseFeature.PaymentState.PaymentPending - coursePurchaseCommissionNotice.isGone = isTerminalState + binding.coursePurchasePaymentTitle.isVisible = isTerminalState + binding.coursePurchasePaymentIcon.isVisible = isTerminalState + binding.coursePurchasePaymentFailureFeedback.isVisible = state.paymentState is CoursePurchaseFeature.PaymentState.PaymentFailure + binding.coursePurchasePaymentPendingFeedback.isVisible = state.paymentState is CoursePurchaseFeature.PaymentState.PaymentPending + binding.coursePurchaseCommissionNotice.isGone = isTerminalState - coursePurchaseBuyActionGreen.isVisible = state.promoCodeState !is CoursePurchaseFeature.PromoCodeState.Valid && !isTerminalState - coursePurchaseBuyActionViolet.isVisible = state.promoCodeState is CoursePurchaseFeature.PromoCodeState.Valid && !isTerminalState - coursePurchaseTerminalAction.isVisible = isTerminalState - renderIdleButton(state, coursePurchaseBuyActionGreen) - renderIdleButton(state, coursePurchaseBuyActionViolet) + binding.coursePurchaseBuyActionGreen.isVisible = state.promoCodeState !is CoursePurchaseFeature.PromoCodeState.Valid && !isTerminalState + binding.coursePurchaseBuyActionViolet.isVisible = state.promoCodeState is CoursePurchaseFeature.PromoCodeState.Valid && !isTerminalState + binding.coursePurchaseTerminalAction.isVisible = isTerminalState + renderIdleButton(state, binding.coursePurchaseBuyActionGreen) + renderIdleButton(state, binding.coursePurchaseBuyActionViolet) renderTerminalButton(state) } private fun renderIdleButton(state: CoursePurchaseFeature.State.Content, coursePurchaseBuyAction: MaterialButton) { + val context = binding.root.context + when (state.paymentState) { is CoursePurchaseFeature.PaymentState.Idle -> { coursePurchaseBuyAction.icon = null @@ -122,6 +114,8 @@ class BuyActionViewDelegate( } private fun renderTerminalButton(state: CoursePurchaseFeature.State.Content) { + val context = binding.root.context + when (state.paymentState) { is CoursePurchaseFeature.PaymentState.PaymentSuccess -> { val icon = AppCompatResources @@ -132,24 +126,24 @@ class BuyActionViewDelegate( DrawableCompat.setTint(it, context.resolveColorAttribute(R.attr.colorOnPrimary)) DrawableCompat.setTintMode(it, PorterDuff.Mode.SRC_IN) } - coursePurchaseTerminalAction.icon = icon - coursePurchaseTerminalAction.text = context.getString(R.string.course_purchase_payment_learn_action) - coursePurchasePaymentTitle.text = context.getString(R.string.course_purchase_payment_success) - coursePurchasePaymentIcon.setImageDrawable(AppCompatResources.getDrawable(context, R.drawable.ic_purchase_success)) + binding.coursePurchaseTerminalAction.icon = icon + binding.coursePurchaseTerminalAction.text = context.getString(R.string.course_purchase_payment_learn_action) + binding.coursePurchasePaymentTitle.text = context.getString(R.string.course_purchase_payment_success) + binding.coursePurchasePaymentIcon.setImageDrawable(AppCompatResources.getDrawable(context, R.drawable.ic_purchase_success)) } is CoursePurchaseFeature.PaymentState.PaymentFailure -> { - coursePurchaseTerminalAction.icon = null - coursePurchaseTerminalAction.text = context.getString(R.string.course_purchase_payment_restore_action) - coursePurchasePaymentTitle.text = context.getString(R.string.course_purchase_payment_failure) - coursePurchasePaymentIcon.setImageDrawable(AppCompatResources.getDrawable(context, R.drawable.ic_purchase_fail)) + binding.coursePurchaseTerminalAction.icon = null + binding.coursePurchaseTerminalAction.text = context.getString(R.string.course_purchase_payment_restore_action) + binding.coursePurchasePaymentTitle.text = context.getString(R.string.course_purchase_payment_failure) + binding.coursePurchasePaymentIcon.setImageDrawable(AppCompatResources.getDrawable(context, R.drawable.ic_purchase_fail)) } is CoursePurchaseFeature.PaymentState.PaymentPending -> { - coursePurchaseTerminalAction.icon = null - coursePurchaseTerminalAction.text = context.getString(R.string.course_purchase_payment_ok_action) - coursePurchasePaymentTitle.text = context.getString(R.string.course_purchase_payment_pending) - coursePurchasePaymentIcon.setImageDrawable(AppCompatResources.getDrawable(context, R.drawable.ic_purchase_pending)) + binding.coursePurchaseTerminalAction.icon = null + binding.coursePurchaseTerminalAction.text = context.getString(R.string.course_purchase_payment_ok_action) + binding.coursePurchasePaymentTitle.text = context.getString(R.string.course_purchase_payment_pending) + binding.coursePurchasePaymentIcon.setImageDrawable(AppCompatResources.getDrawable(context, R.drawable.ic_purchase_pending)) } else -> {} } diff --git a/app/src/main/java/org/stepik/android/view/course_purchase/delegate/PromoCodeViewDelegate.kt b/app/src/main/java/org/stepik/android/view/course_purchase/delegate/PromoCodeViewDelegate.kt index 61bb36d57e..e4da06900d 100644 --- a/app/src/main/java/org/stepik/android/view/course_purchase/delegate/PromoCodeViewDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course_purchase/delegate/PromoCodeViewDelegate.kt @@ -18,21 +18,13 @@ import ru.nobird.android.view.base.ui.extension.getDrawableCompat import ru.nobird.android.view.base.ui.extension.setTextIfChanged class PromoCodeViewDelegate( - coursePurchaseBinding: BottomSheetDialogCoursePurchaseBinding, + private val binding: BottomSheetDialogCoursePurchaseBinding, private val coursePurchaseViewModel: CoursePurchaseViewModel ) { companion object { private const val EVALUATION_FRAME_DURATION_MS = 250 } - private val context = coursePurchaseBinding.root.context - private val coursePromoCodeAction = coursePurchaseBinding.coursePromoCodeAction - private val coursePromoCodeContainer = coursePurchaseBinding.coursePurchasePromoCodeInputContainer - private val coursePromoCodeInput = coursePurchaseBinding.coursePurchasePromoCodeInput - private val coursePromoCodeDismiss = coursePurchaseBinding.coursePurchasePromoCodeInputDismiss - private val coursePromoCodeSubmitAction = coursePurchaseBinding.coursePurchasePromoCodeSubmitAction - private val coursePurchasePromoCodeResultMessage = coursePurchaseBinding.coursePurchasePromoCodeResultMessage - private val layerListDrawableDelegate = LayerListDrawableDelegate( listOf( R.id.idle_state, @@ -40,19 +32,19 @@ class PromoCodeViewDelegate( R.id.invalid_state, R.id.valid_state ), - (coursePromoCodeSubmitAction.background as RippleDrawable).findDrawableByLayerId(R.id.promo_code_layer_list) as LayerDrawable + (binding.coursePurchasePromoCodeSubmitAction.background as RippleDrawable).findDrawableByLayerId(R.id.promo_code_layer_list) as LayerDrawable ) private var promoCodeState: PromoCodeState = PromoCodeState.Idle init { - coursePromoCodeAction.setOnClickListener { + binding.coursePromoCodeAction.setOnClickListener { coursePurchaseViewModel.onNewMessage(CoursePurchaseFeature.Message.HavePromoCodeMessage) } - coursePromoCodeInput.doAfterTextChanged { text: Editable? -> + binding.coursePurchasePromoCodeInput.doAfterTextChanged { text: Editable? -> val length = text?.length ?: 0 - coursePromoCodeDismiss.isVisible = length != 0 - coursePromoCodeSubmitAction.isVisible = length != 0 + binding.coursePurchasePromoCodeInputDismiss.isVisible = length != 0 + binding.coursePurchasePromoCodeSubmitAction.isVisible = length != 0 if ((promoCodeState as? PromoCodeState.Valid)?.text == text.toString()) { return@doAfterTextChanged @@ -60,38 +52,43 @@ class PromoCodeViewDelegate( coursePurchaseViewModel.onNewMessage(CoursePurchaseFeature.Message.PromoCodeEditingMessage) } - coursePromoCodeDismiss.setOnClickListener { - coursePromoCodeInput.setText("") + binding.coursePurchasePromoCodeInputDismiss.setOnClickListener { + binding.coursePurchasePromoCodeInput.setText("") + } + binding.coursePurchasePromoCodeSubmitAction.setOnClickListener { + coursePurchaseViewModel.onNewMessage( + CoursePurchaseFeature.Message.PromoCodeCheckMessage(binding.coursePurchasePromoCodeInput.text.toString()) + ) } - coursePromoCodeSubmitAction.setOnClickListener { coursePurchaseViewModel.onNewMessage(CoursePurchaseFeature.Message.PromoCodeCheckMessage(coursePromoCodeInput.text.toString())) } } fun setViewVisibility(isVisible: Boolean) { - coursePromoCodeAction.isVisible = isVisible - coursePromoCodeContainer.isVisible = isVisible - coursePromoCodeInput.isVisible = isVisible - coursePromoCodeDismiss.isVisible = isVisible - coursePromoCodeSubmitAction.isVisible = isVisible - coursePurchasePromoCodeResultMessage.isVisible = isVisible + binding.coursePromoCodeAction.isVisible = isVisible + binding.coursePurchasePromoCodeInputContainer.isVisible = isVisible + binding.coursePurchasePromoCodeInput.isVisible = isVisible + binding.coursePurchasePromoCodeInputDismiss.isVisible = isVisible + binding.coursePurchasePromoCodeSubmitAction.isVisible = isVisible + binding.coursePurchasePromoCodeResultMessage.isVisible = isVisible } fun render(state: PromoCodeState) { this.promoCodeState = state - coursePromoCodeAction.isVisible = state is PromoCodeState.Idle - coursePromoCodeContainer.isVisible = state !is PromoCodeState.Idle - coursePromoCodeDismiss.isEnabled = state !is PromoCodeState.Checking - coursePromoCodeSubmitAction.isEnabled = state is PromoCodeState.Editing - coursePromoCodeInput.isEnabled = state !is PromoCodeState.Checking + binding.coursePromoCodeAction.isVisible = state is PromoCodeState.Idle + binding.coursePurchasePromoCodeInputContainer.isVisible = state !is PromoCodeState.Idle + binding.coursePurchasePromoCodeInputDismiss.isEnabled = state !is PromoCodeState.Checking + binding.coursePurchasePromoCodeSubmitAction.isEnabled = state is PromoCodeState.Editing + binding.coursePurchasePromoCodeInput.isEnabled = state !is PromoCodeState.Checking - coursePurchasePromoCodeResultMessage.isVisible = state is PromoCodeState.Checking || state is PromoCodeState.Valid || state is PromoCodeState.Invalid + binding.coursePurchasePromoCodeResultMessage.isVisible = state is PromoCodeState.Checking || state is PromoCodeState.Valid || state is PromoCodeState.Invalid val (messageRes, colorRes) = getPromoCodeResultMessage(state) if (messageRes != -1 && colorRes != -1) { - coursePurchasePromoCodeResultMessage.text = context.getString(messageRes) - coursePurchasePromoCodeResultMessage.setTextColor(AppCompatResources.getColorStateList(context, colorRes)) + val context = binding.root.context + binding.coursePurchasePromoCodeResultMessage.text = context.getString(messageRes) + binding.coursePurchasePromoCodeResultMessage.setTextColor(AppCompatResources.getColorStateList(context, colorRes)) } - coursePromoCodeSubmitAction.setImageDrawable(getDrawableForSubmitAction(state)) + binding.coursePurchasePromoCodeSubmitAction.setImageDrawable(getDrawableForSubmitAction(state)) setEditTextFromState(state) layerListDrawableDelegate.showLayer(getBackgroundLayer(state)) } @@ -111,10 +108,10 @@ class PromoCodeViewDelegate( private fun setEditTextFromState(state: PromoCodeState) { when (state) { is PromoCodeState.Checking -> { - coursePromoCodeInput.setTextIfChanged(state.text) + binding.coursePurchasePromoCodeInput.setTextIfChanged(state.text) } is PromoCodeState.Valid -> - coursePromoCodeInput.setTextIfChanged(state.text) + binding.coursePurchasePromoCodeInput.setTextIfChanged(state.text) else -> return } @@ -123,9 +120,10 @@ class PromoCodeViewDelegate( private fun getDrawableForSubmitAction(state: PromoCodeState): Drawable? = when (state) { is PromoCodeState.Idle, is PromoCodeState.Editing -> - AppCompatResources.getDrawable(context, R.drawable.ic_arrow_forward) + AppCompatResources.getDrawable(binding.root.context, R.drawable.ic_arrow_forward) is PromoCodeState.Checking -> { val evaluationDrawable = AnimationDrawable() + val context = binding.root.context evaluationDrawable.addFrame(context.getDrawableCompat(R.drawable.ic_step_quiz_evaluation_frame_1), EVALUATION_FRAME_DURATION_MS) evaluationDrawable.addFrame(context.getDrawableCompat(R.drawable.ic_step_quiz_evaluation_frame_2), EVALUATION_FRAME_DURATION_MS) evaluationDrawable.addFrame(context.getDrawableCompat(R.drawable.ic_step_quiz_evaluation_frame_3), EVALUATION_FRAME_DURATION_MS) @@ -134,9 +132,9 @@ class PromoCodeViewDelegate( evaluationDrawable } is PromoCodeState.Invalid -> - AppCompatResources.getDrawable(context, R.drawable.ic_step_quiz_wrong) + AppCompatResources.getDrawable(binding.root.context, R.drawable.ic_step_quiz_wrong) is PromoCodeState.Valid -> - AppCompatResources.getDrawable(context, R.drawable.ic_step_quiz_correct) + AppCompatResources.getDrawable(binding.root.context, R.drawable.ic_step_quiz_correct) } private fun getBackgroundLayer(state: PromoCodeState): Int = diff --git a/app/src/main/java/org/stepik/android/view/course_revenue/ui/activity/CourseRevenueActivity.kt b/app/src/main/java/org/stepik/android/view/course_revenue/ui/activity/CourseRevenueActivity.kt index 1565c9e3cf..42435c0ee2 100644 --- a/app/src/main/java/org/stepik/android/view/course_revenue/ui/activity/CourseRevenueActivity.kt +++ b/app/src/main/java/org/stepik/android/view/course_revenue/ui/activity/CourseRevenueActivity.kt @@ -8,14 +8,14 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.lifecycle.ViewModelProvider import androidx.viewpager2.widget.ViewPager2 +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.appbar.AppBarLayout import com.google.android.material.tabs.TabLayout -import kotlinx.android.synthetic.main.activity_course_benefits.* -import kotlinx.android.synthetic.main.error_no_connection_with_button.* import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.ActivityCourseBenefitsBinding import org.stepic.droid.util.DeviceInfoUtil import org.stepik.android.domain.course_revenue.analytic.CourseBenefitClickedEvent import org.stepik.android.domain.course_revenue.analytic.CourseBenefitsScreenOpenedEvent @@ -70,6 +70,8 @@ class CourseRevenueActivity : AppCompatActivity(), ReduxView() @@ -81,7 +83,7 @@ class CourseRevenueActivity : AppCompatActivity(), ReduxView - val ratio = abs(verticalOffset).toFloat() / (courseBenefitsCollapsingToolbar.height - courseBenefitToolbar.height) - courseBenefitSummaryContainer.alpha = 1f - (ratio * 1.5f) + binding.courseBenefitsAppBar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { _, verticalOffset -> + val ratio = abs(verticalOffset).toFloat() / (binding.courseBenefitsCollapsingToolbar.height - binding.courseBenefitToolbar.height) + binding.courseBenefitSummaryContainer.root.alpha = 1f - (ratio * 1.5f) }) - ViewCompat.setTranslationZ(divider, ViewCompat.getElevation(courseBenefitsAppBar)) + ViewCompat.setTranslationZ(binding.divider.root, ViewCompat.getElevation(binding.courseBenefitsAppBar)) initViewPager() initViewStateDelegate() - courseBenefitSummaryDelegate = CourseBenefitSummaryViewDelegate(courseBenefitSummaryContainer, + courseBenefitSummaryDelegate = CourseBenefitSummaryViewDelegate(binding.courseBenefitSummaryContainer, revenuePriceMapper, onCourseSummaryClicked = { isExpanded -> analytic.report(CourseBenefitsSummaryClicked(courseId, courseTitle, isExpanded)) }, onContactSupportClicked = { @@ -124,7 +126,7 @@ class CourseRevenueActivity : AppCompatActivity(), ReduxView() - viewStateDelegate.addState(courseBenefitsTabs, courseBenefitSummaryContainer, courseBenefitsOperationsViewPager) - viewStateDelegate.addState(coursesBenefitsLoadingError) - viewStateDelegate.addState(courseBenefitsTabs, courseBenefitSummaryContainer, courseBenefitsOperationsViewPager) + viewStateDelegate.addState(binding.courseBenefitsTabs, binding.courseBenefitSummaryContainer.root, binding.courseBenefitsOperationsViewPager) + viewStateDelegate.addState(binding.coursesBenefitsLoadingError.root) + viewStateDelegate.addState(binding.courseBenefitsTabs, binding.courseBenefitSummaryContainer.root, binding.courseBenefitsOperationsViewPager) } private fun initViewPager() { @@ -176,13 +178,13 @@ class CourseRevenueActivity : AppCompatActivity(), ReduxView(containerView), LayoutContainer { + containerView: View + ) : DelegateViewHolder(containerView) { + private val viewBinding: ItemCourseBenefitBinding by viewBinding { ItemCourseBenefitBinding.bind(itemView) } init { itemView.setOnClickListener { (itemData as? CourseBenefitListItem.Data)?.let { onItemClick(it) } } @@ -46,8 +47,8 @@ class CourseBenefitsAdapterDelegate( val decimalFormat = DecimalFormat().apply { setCurrency(currency) } decimalFormat.minimumFractionDigits = 2 - purchaseRefundIcon.setImageDrawable(getIconDrawable(data.courseBenefit)) - purchaseRefundName.text = + viewBinding.purchaseRefundIcon.setImageDrawable(getIconDrawable(data.courseBenefit)) + viewBinding.purchaseRefundName.text = if (data.courseBenefit.buyer == null && !data.courseBenefit.isInvoicePayment) { buildString { append(context.getString(R.string.transaction_manual_channel)) @@ -59,7 +60,7 @@ class CourseBenefitsAdapterDelegate( data.user?.fullName ?: data.courseBenefit.buyer.toString() } - purchaseRefundDate.text = DateTimeHelper.getPrintableDate( + viewBinding.purchaseRefundDate.text = DateTimeHelper.getPrintableDate( data.courseBenefit.time, DateTimeHelper.DISPLAY_DATETIME_PATTERN, TimeZone.getDefault() @@ -82,11 +83,11 @@ class CourseBenefitsAdapterDelegate( } else { ContextCompat.getColor(context, R.color.color_overlay_red) } - purchaseRefundIncomeSum.setTextColor(textColor) - purchaseRefundTransactionSum.text = transactionSum - purchaseRefundIncomeSum.text = amount - purchaseRefundPromocode.text = data.courseBenefit.promoCode - purchaseRefundPromocode.isVisible = data.courseBenefit.promoCode != null + viewBinding.purchaseRefundIncomeSum.setTextColor(textColor) + viewBinding.purchaseRefundTransactionSum.text = transactionSum + viewBinding.purchaseRefundIncomeSum.text = amount + viewBinding.purchaseRefundPromocode.text = data.courseBenefit.promoCode + viewBinding.purchaseRefundPromocode.isVisible = data.courseBenefit.promoCode != null } private fun getIconDrawable(data: CourseBenefit): Drawable? = diff --git a/app/src/main/java/org/stepik/android/view/course_revenue/ui/adapter/delegate/CourseBenefitsListAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/course_revenue/ui/adapter/delegate/CourseBenefitsListAdapterDelegate.kt index 4ef2c702d3..6e1316c9cf 100644 --- a/app/src/main/java/org/stepik/android/view/course_revenue/ui/adapter/delegate/CourseBenefitsListAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course_revenue/ui/adapter/delegate/CourseBenefitsListAdapterDelegate.kt @@ -1,15 +1,13 @@ package org.stepik.android.view.course_revenue.ui.adapter.delegate -import android.view.View import android.view.ViewGroup import androidx.appcompat.content.res.AppCompatResources import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.error_no_connection_with_button_small.* -import kotlinx.android.synthetic.main.item_course_benefits.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemCourseBenefitsBinding import org.stepik.android.domain.course_revenue.model.CourseBenefitListItem import org.stepik.android.presentation.course_revenue.CourseBenefitsFeature import org.stepik.android.view.course_revenue.mapper.RevenuePriceMapper @@ -36,8 +34,10 @@ class CourseBenefitsListAdapterDelegate( ViewHolder(createView(parent, R.layout.item_course_benefits)) private inner class ViewHolder( - override val containerView: View - ) : DelegateViewHolder(containerView), LayoutContainer { + view: android.view.View + ) : DelegateViewHolder(view) { + + private val viewBinding: ItemCourseBenefitsBinding by viewBinding(ItemCourseBenefitsBinding::bind) private val viewStateDelegate = ViewStateDelegate() private val adapter = DefaultDelegateAdapter() @@ -47,25 +47,25 @@ class CourseBenefitsListAdapterDelegate( } init { - viewStateDelegate.addState(courseBenefitsRecycler) - viewStateDelegate.addState(courseBenefitsEmpty) - viewStateDelegate.addState(courseBenefitsError) - viewStateDelegate.addState(courseBenefitsRecycler) + viewStateDelegate.addState(viewBinding.courseBenefitsRecycler) + viewStateDelegate.addState(viewBinding.courseBenefitsEmpty.root) + viewStateDelegate.addState(viewBinding.courseBenefitsError.root) + viewStateDelegate.addState(viewBinding.courseBenefitsRecycler) - courseBenefitsRecycler.adapter = adapter - courseBenefitsRecycler.layoutManager = LinearLayoutManager(context) - courseBenefitsRecycler.setRecycledViewPool(sharedViewPool) - courseBenefitsRecycler.addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL).apply { + viewBinding.courseBenefitsRecycler.adapter = adapter + viewBinding.courseBenefitsRecycler.layoutManager = LinearLayoutManager(context) + viewBinding.courseBenefitsRecycler.setRecycledViewPool(sharedViewPool) + viewBinding.courseBenefitsRecycler.addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL).apply { AppCompatResources.getDrawable(context, R.drawable.bg_divider_vertical)?.let(::setDrawable) }) - courseBenefitsRecycler.setOnPaginationListener { direction -> + viewBinding.courseBenefitsRecycler.setOnPaginationListener { direction -> if (direction == PaginationDirection.NEXT) { onFetchNextPage() } } - tryAgain.setOnClickListener { reloadListAction() } + viewBinding.courseBenefitsError.tryAgain.setOnClickListener { reloadListAction() } } override fun onBind(data: CourseBenefitOperationItem) { @@ -89,4 +89,4 @@ class CourseBenefitsListAdapterDelegate( } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_revenue/ui/adapter/delegate/CourseBenefitsMonthlyAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/course_revenue/ui/adapter/delegate/CourseBenefitsMonthlyAdapterDelegate.kt index 0a7cdddd6e..55fa3c3713 100644 --- a/app/src/main/java/org/stepik/android/view/course_revenue/ui/adapter/delegate/CourseBenefitsMonthlyAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course_revenue/ui/adapter/delegate/CourseBenefitsMonthlyAdapterDelegate.kt @@ -1,12 +1,11 @@ package org.stepik.android.view.course_revenue.ui.adapter.delegate import android.text.SpannedString -import android.view.View import android.view.ViewGroup import androidx.core.content.ContextCompat -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_course_benefit_by_month.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemCourseBenefitByMonthBinding import org.stepic.droid.util.DateTimeHelper import org.stepic.droid.util.resolveResourceIdAttribute import org.stepik.android.domain.course_revenue.model.CourseBenefitByMonthListItem @@ -28,8 +27,10 @@ class CourseBenefitsMonthlyAdapterDelegate( ViewHolder(createView(parent, R.layout.item_course_benefit_by_month)) private inner class ViewHolder( - override val containerView: View - ) : DelegateViewHolder(containerView), LayoutContainer { + view: android.view.View + ) : DelegateViewHolder(view) { + + private val viewBinding: ItemCourseBenefitByMonthBinding by viewBinding(ItemCourseBenefitByMonthBinding::bind) override fun onBind(data: CourseBenefitByMonthListItem) { data as CourseBenefitByMonthListItem.Data @@ -38,21 +39,21 @@ class CourseBenefitsMonthlyAdapterDelegate( val decimalFormat = DecimalFormat().apply { setCurrency(currency) } decimalFormat.minimumFractionDigits = 2 - courseBenefitByMonthCurrentMonth.text = DateTimeHelper.getPrintableDate( + viewBinding.courseBenefitByMonthCurrentMonth.text = DateTimeHelper.getPrintableDate( data.courseBenefitByMonth.date, DateTimeHelper.DISPLAY_MONTH_YEAR_NOMINAL_PATTERN, TimeZone.getDefault() ).capitalize(Locale.ROOT) val (incomeString, incomeStringColor) = resolveIncomeString(data.courseBenefitByMonth.totalUserIncome, data.courseBenefitByMonth.currencyCode, decimalFormat) - courseBenefitByMonthIncome.text = incomeString - courseBenefitByMonthIncome.setTextColor(incomeStringColor) - courseBenefitByMonthSalesValue.text = revenuePriceMapper.mapToDisplayPrice(data.courseBenefitByMonth.currencyCode, decimalFormat.format(data.courseBenefitByMonth.totalTurnover.toDoubleOrNull() ?: 0.0)) - courseBenefitByMonthRefundsValue.text = revenuePriceMapper.mapToDisplayPrice(data.courseBenefitByMonth.currencyCode, decimalFormat.format(data.courseBenefitByMonth.totalRefunds.toDoubleOrNull() ?: 0.0)) - courseBenefitByMonthCountPaymentsCountValue.text = data.courseBenefitByMonth.countPayments.toString() - courseBenefitByMonthStepikPaymentsValue.text = data.courseBenefitByMonth.countNonZPayments.toString() - courseBenefitByMonthZLinkPaymentsValue.text = data.courseBenefitByMonth.countZPayments.toString() - courseBenefitByMonthInvoicePaymentsValue.text = data.courseBenefitByMonth.countInvoicePayments.toString() + viewBinding.courseBenefitByMonthIncome.text = incomeString + viewBinding.courseBenefitByMonthIncome.setTextColor(incomeStringColor) + viewBinding.courseBenefitByMonthSalesValue.text = revenuePriceMapper.mapToDisplayPrice(data.courseBenefitByMonth.currencyCode, decimalFormat.format(data.courseBenefitByMonth.totalTurnover.toDoubleOrNull() ?: 0.0)) + viewBinding.courseBenefitByMonthRefundsValue.text = revenuePriceMapper.mapToDisplayPrice(data.courseBenefitByMonth.currencyCode, decimalFormat.format(data.courseBenefitByMonth.totalRefunds.toDoubleOrNull() ?: 0.0)) + viewBinding.courseBenefitByMonthCountPaymentsCountValue.text = data.courseBenefitByMonth.countPayments.toString() + viewBinding.courseBenefitByMonthStepikPaymentsValue.text = data.courseBenefitByMonth.countNonZPayments.toString() + viewBinding.courseBenefitByMonthZLinkPaymentsValue.text = data.courseBenefitByMonth.countZPayments.toString() + viewBinding.courseBenefitByMonthInvoicePaymentsValue.text = data.courseBenefitByMonth.countInvoicePayments.toString() } private fun resolveIncomeString(totalUserIncome: String, currencyCode: String, decimalFormat: DecimalFormat): Pair { @@ -73,4 +74,4 @@ class CourseBenefitsMonthlyAdapterDelegate( } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_revenue/ui/adapter/delegate/CourseBenefitsMonthlyListAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/course_revenue/ui/adapter/delegate/CourseBenefitsMonthlyListAdapterDelegate.kt index eb8efb9377..38b2920e1a 100644 --- a/app/src/main/java/org/stepik/android/view/course_revenue/ui/adapter/delegate/CourseBenefitsMonthlyListAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course_revenue/ui/adapter/delegate/CourseBenefitsMonthlyListAdapterDelegate.kt @@ -1,15 +1,13 @@ package org.stepik.android.view.course_revenue.ui.adapter.delegate -import android.view.View import android.view.ViewGroup import androidx.appcompat.content.res.AppCompatResources import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.error_no_connection_with_button_small.* -import kotlinx.android.synthetic.main.item_course_benefits.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemCourseBenefitsBinding import org.stepik.android.domain.course_revenue.model.CourseBenefitByMonthListItem import org.stepik.android.presentation.course_revenue.CourseBenefitsMonthlyFeature import org.stepik.android.view.course_revenue.mapper.RevenuePriceMapper @@ -35,8 +33,10 @@ class CourseBenefitsMonthlyListAdapterDelegate( ViewHolder(createView(parent, R.layout.item_course_benefits)) private inner class ViewHolder( - override val containerView: View - ) : DelegateViewHolder(containerView), LayoutContainer { + view: android.view.View + ) : DelegateViewHolder(view) { + + private val viewBinding: ItemCourseBenefitsBinding by viewBinding(ItemCourseBenefitsBinding::bind) private val viewStateDelegate = ViewStateDelegate() private val adapter = DefaultDelegateAdapter() @@ -46,24 +46,24 @@ class CourseBenefitsMonthlyListAdapterDelegate( } init { - viewStateDelegate.addState(courseBenefitsRecycler) - viewStateDelegate.addState(courseBenefitsEmpty) - viewStateDelegate.addState(courseBenefitsError) - viewStateDelegate.addState(courseBenefitsRecycler) + viewStateDelegate.addState(viewBinding.courseBenefitsRecycler) + viewStateDelegate.addState(viewBinding.courseBenefitsEmpty.root) + viewStateDelegate.addState(viewBinding.courseBenefitsError.root) + viewStateDelegate.addState(viewBinding.courseBenefitsRecycler) - courseBenefitsRecycler.adapter = adapter - courseBenefitsRecycler.layoutManager = LinearLayoutManager(context) - courseBenefitsRecycler.setRecycledViewPool(sharedViewPool) - courseBenefitsRecycler.addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL).apply { + viewBinding.courseBenefitsRecycler.adapter = adapter + viewBinding.courseBenefitsRecycler.layoutManager = LinearLayoutManager(context) + viewBinding.courseBenefitsRecycler.setRecycledViewPool(sharedViewPool) + viewBinding.courseBenefitsRecycler.addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL).apply { AppCompatResources.getDrawable(context, R.drawable.bg_divider_vertical)?.let(::setDrawable) }) - courseBenefitsRecycler.setOnPaginationListener { direction -> + viewBinding.courseBenefitsRecycler.setOnPaginationListener { direction -> if (direction == PaginationDirection.NEXT) { onFetchNextPage() } } - tryAgain.setOnClickListener { reloadListAction() } + viewBinding.courseBenefitsError.tryAgain.setOnClickListener { reloadListAction() } } override fun onBind(data: CourseBenefitOperationItem) { @@ -81,4 +81,4 @@ class CourseBenefitsMonthlyListAdapterDelegate( } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_revenue/ui/delegate/CourseBenefitSummaryViewDelegate.kt b/app/src/main/java/org/stepik/android/view/course_revenue/ui/delegate/CourseBenefitSummaryViewDelegate.kt index c8b59bc98a..a30a45af19 100644 --- a/app/src/main/java/org/stepik/android/view/course_revenue/ui/delegate/CourseBenefitSummaryViewDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course_revenue/ui/delegate/CourseBenefitSummaryViewDelegate.kt @@ -1,12 +1,11 @@ package org.stepik.android.view.course_revenue.ui.delegate -import android.view.View import androidx.core.content.ContextCompat import androidx.core.text.bold import androidx.core.text.buildSpannedString import androidx.core.text.color -import kotlinx.android.synthetic.main.view_course_benefit_summary.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.ViewCourseBenefitSummaryBinding import org.stepic.droid.ui.util.collapse import org.stepic.droid.ui.util.expand import org.stepic.droid.util.DateTimeHelper @@ -19,43 +18,21 @@ import java.util.TimeZone import java.util.Locale class CourseBenefitSummaryViewDelegate( - containerView: View, + private val binding: ViewCourseBenefitSummaryBinding, private val revenuePriceMapper: RevenuePriceMapper, private val onCourseSummaryClicked: (Boolean) -> Unit, private val onContactSupportClicked: () -> Unit ) { - private val context = containerView.context - - private val courseBenefitsSummaryLoading = containerView.courseBenefitSummaryLoading - private val courseBenefitSummaryEmpty = containerView.courseBenefitSummaryEmpty - - private val courseBenefitSummaryContainer = containerView.courseBenefitSummaryInformation - private val courseBenefitSummaryInformationExpansion = containerView.courseBenefitSummaryInformationExpansion - - private val courseBenefitSummaryArrow = containerView.courseBenefitSummaryArrow - private val courseBenefitExperimentDisclaimer = containerView.courseBenefitExperimentDisclaimer - private val courseBenefitOperationDisclaimer = containerView.courseBenefitOperationDisclaimer - - private val courseBenefitCurrentEarningsTitle = containerView.courseBenefitSummaryEarningsCurrentMonthText - private val courseBenefitCurrentEarningsValue = containerView.courseBenefitSummaryEarningsCurrentMonthValue - - private val courseBenefitCurrentTurnoverTitle = containerView.courseBenefitSummaryTurnoverCurrentMonthText - private val courseBenefitCurrentTurnoverValue = containerView.courseBenefitSummaryTurnoverCurrentMonthValue - - private val courseBenefitTotalEarningsTitle = containerView.courseBenefitSummaryEarningsTotalText - private val courseBenefitTotalEarningsValue = containerView.courseBenefitSummaryEarningsTotalValue - - private val courseBenefitTotalTurnoverTitle = containerView.courseBenefitSummaryTurnoverTotalText - private val courseBenefitTotalTurnoverValue = containerView.courseBenefitSummaryTurnoverTotalValue + private val context = binding.root.context private val viewStateDelegate = ViewStateDelegate() init { - viewStateDelegate.addState(courseBenefitsSummaryLoading) - viewStateDelegate.addState(courseBenefitSummaryEmpty, courseBenefitOperationDisclaimer) - viewStateDelegate.addState(courseBenefitSummaryContainer, courseBenefitOperationDisclaimer) + viewStateDelegate.addState(binding.courseBenefitSummaryLoading) + viewStateDelegate.addState(binding.courseBenefitSummaryEmpty, binding.courseBenefitOperationDisclaimer) + viewStateDelegate.addState(binding.courseBenefitSummaryInformation, binding.courseBenefitOperationDisclaimer) - courseBenefitExperimentDisclaimer.text = buildSpannedString { + binding.courseBenefitExperimentDisclaimer.text = buildSpannedString { bold { append(context.getString(R.string.course_benefits_contact_support_part_1)) } append(context.getString(R.string.course_benefits_contact_support_part_2)) @@ -65,16 +42,16 @@ class CourseBenefitSummaryViewDelegate( append(".") } - courseBenefitExperimentDisclaimer.setOnClickListener { onContactSupportClicked() } + binding.courseBenefitExperimentDisclaimer.setOnClickListener { onContactSupportClicked() } - courseBenefitSummaryContainer.setOnClickListener { - courseBenefitSummaryArrow.changeState() - val isExpanded = courseBenefitSummaryArrow.isExpanded() + binding.courseBenefitSummaryInformation.setOnClickListener { + binding.courseBenefitSummaryArrow.changeState() + val isExpanded = binding.courseBenefitSummaryArrow.isExpanded() onCourseSummaryClicked(isExpanded) if (isExpanded) { - courseBenefitSummaryInformationExpansion.expand() + binding.courseBenefitSummaryInformationExpansion.expand() } else { - courseBenefitSummaryInformationExpansion.collapse() + binding.courseBenefitSummaryInformationExpansion.collapse() } } } @@ -98,17 +75,17 @@ class CourseBenefitSummaryViewDelegate( TimeZone.getDefault() ).capitalize(Locale.ROOT) - courseBenefitCurrentEarningsTitle.text = context.getString(R.string.course_benefits_earning_current_month, currentMonthDate) - courseBenefitCurrentEarningsValue.text = revenuePriceMapper.mapToDisplayPrice(state.courseBenefitSummary.currencyCode, decimalFormat.format(state.courseBenefitSummary.monthUserIncome.toDoubleOrNull() ?: 0.0)) + binding.courseBenefitSummaryEarningsCurrentMonthText.text = context.getString(R.string.course_benefits_earning_current_month, currentMonthDate) + binding.courseBenefitSummaryEarningsCurrentMonthValue.text = revenuePriceMapper.mapToDisplayPrice(state.courseBenefitSummary.currencyCode, decimalFormat.format(state.courseBenefitSummary.monthUserIncome.toDoubleOrNull() ?: 0.0)) - courseBenefitCurrentTurnoverTitle.text = context.getString(R.string.course_benefits_turnover_current_month, currentMonthDate) - courseBenefitCurrentTurnoverValue.text = revenuePriceMapper.mapToDisplayPrice(state.courseBenefitSummary.currencyCode, decimalFormat.format(state.courseBenefitSummary.monthTurnover.toDoubleOrNull() ?: 0.0)) + binding.courseBenefitSummaryTurnoverCurrentMonthText.text = context.getString(R.string.course_benefits_turnover_current_month, currentMonthDate) + binding.courseBenefitSummaryTurnoverCurrentMonthValue.text = revenuePriceMapper.mapToDisplayPrice(state.courseBenefitSummary.currencyCode, decimalFormat.format(state.courseBenefitSummary.monthTurnover.toDoubleOrNull() ?: 0.0)) - courseBenefitTotalEarningsTitle.text = context.getString(R.string.course_benefits_earnings_total, totalDate) - courseBenefitTotalEarningsValue.text = revenuePriceMapper.mapToDisplayPrice(state.courseBenefitSummary.currencyCode, decimalFormat.format(state.courseBenefitSummary.totalUserIncome.toDoubleOrNull() ?: 0.0)) + binding.courseBenefitSummaryEarningsTotalText.text = context.getString(R.string.course_benefits_earnings_total, totalDate) + binding.courseBenefitSummaryEarningsTotalValue.text = revenuePriceMapper.mapToDisplayPrice(state.courseBenefitSummary.currencyCode, decimalFormat.format(state.courseBenefitSummary.totalUserIncome.toDoubleOrNull() ?: 0.0)) - courseBenefitTotalTurnoverTitle.text = context.getString(R.string.course_beneifts_turnover_total, totalDate) - courseBenefitTotalTurnoverValue.text = revenuePriceMapper.mapToDisplayPrice(state.courseBenefitSummary.currencyCode, decimalFormat.format(state.courseBenefitSummary.totalTurnover.toDoubleOrNull() ?: 0.0)) + binding.courseBenefitSummaryTurnoverTotalText.text = context.getString(R.string.course_beneifts_turnover_total, totalDate) + binding.courseBenefitSummaryTurnoverTotalValue.text = revenuePriceMapper.mapToDisplayPrice(state.courseBenefitSummary.currencyCode, decimalFormat.format(state.courseBenefitSummary.totalTurnover.toDoubleOrNull() ?: 0.0)) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_revenue/ui/dialog/TransactionBottomSheetDialogFragment.kt b/app/src/main/java/org/stepik/android/view/course_revenue/ui/dialog/TransactionBottomSheetDialogFragment.kt index 7c15cbf27a..c87f95819f 100644 --- a/app/src/main/java/org/stepik/android/view/course_revenue/ui/dialog/TransactionBottomSheetDialogFragment.kt +++ b/app/src/main/java/org/stepik/android/view/course_revenue/ui/dialog/TransactionBottomSheetDialogFragment.kt @@ -6,13 +6,14 @@ import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible import androidx.fragment.app.DialogFragment +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import kotlinx.android.synthetic.main.bottom_sheet_dialog_transaction.* import org.stepic.droid.R import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.BottomSheetDialogTransactionBinding import org.stepic.droid.util.DateTimeHelper import org.stepik.android.domain.course_revenue.model.CourseBeneficiary import org.stepik.android.domain.course_revenue.model.CourseBenefit @@ -43,6 +44,8 @@ class TransactionBottomSheetDialogFragment : BottomSheetDialogFragment() { } } + private val binding: BottomSheetDialogTransactionBinding by viewBinding(BottomSheetDialogTransactionBinding::bind) + private var courseBenefit: CourseBenefit by argument() private var courseBeneficiary: CourseBeneficiary by argument() private var user: User? = null @@ -78,36 +81,36 @@ class TransactionBottomSheetDialogFragment : BottomSheetDialogFragment() { val decimalFormat = DecimalFormat().apply { setCurrency(currency) } decimalFormat.minimumFractionDigits = 2 - transactionTitle.text = + binding.transactionTitle.text = if (courseBenefit.status == CourseBenefit.Status.DEBITED) { getString(R.string.transaction_title_purchase) } else { getString(R.string.transaction_title_refund) } - transactionDateValue.text = DateTimeHelper.getPrintableDate(courseBenefit.time, DateTimeHelper.DISPLAY_DATETIME_PATTERN, TimeZone.getDefault()) + binding.transactionDateValue.text = DateTimeHelper.getPrintableDate(courseBenefit.time, DateTimeHelper.DISPLAY_DATETIME_PATTERN, TimeZone.getDefault()) - transactionCourseValue.text = courseTitle.orEmpty() - transactionCourseTitle.isVisible = courseTitle != null - transactionCourseValue.isVisible = courseTitle != null + binding.transactionCourseValue.text = courseTitle.orEmpty() + binding.transactionCourseTitle.isVisible = courseTitle != null + binding.transactionCourseValue.isVisible = courseTitle != null - transactionBuyerValue.text = user?.fullName.orEmpty() - transactionBuyerTitle.isVisible = user != null - transactionBuyerValue.isVisible = user != null - buyerOverlayView.setOnClickListener { user?.let { screenManager.openProfile(requireContext(), it.id) } } + binding.transactionBuyerValue.text = user?.fullName.orEmpty() + binding.transactionBuyerTitle.isVisible = user != null + binding.transactionBuyerValue.isVisible = user != null + binding.buyerOverlayView.setOnClickListener { user?.let { screenManager.openProfile(requireContext(), it.id) } } - transactionPaymentValue.text = + binding.transactionPaymentValue.text = revenuePriceMapper.mapToDisplayPrice(courseBenefit.currencyCode, decimalFormat.format(courseBenefit.paymentAmount?.toDoubleOrNull() ?: 0.0)) - transactionPromoCodeValue.text = courseBenefit.promoCode.orEmpty() - transactionPromoCodeTitle.isVisible = courseBenefit.promoCode != null - transactionPromoCodeValue.isVisible = courseBenefit.promoCode != null + binding.transactionPromoCodeValue.text = courseBenefit.promoCode.orEmpty() + binding.transactionPromoCodeTitle.isVisible = courseBenefit.promoCode != null + binding.transactionPromoCodeValue.isVisible = courseBenefit.promoCode != null val isChannelInfoVisible = courseBenefit.status == CourseBenefit.Status.DEBITED || (courseBenefit.buyer == null && !courseBenefit.isInvoicePayment) - transactionChannelTitle.isVisible = isChannelInfoVisible - transactionChannelValue.isVisible = isChannelInfoVisible - transactionChannelValue.text = + binding.transactionChannelTitle.isVisible = isChannelInfoVisible + binding.transactionChannelValue.isVisible = isChannelInfoVisible + binding.transactionChannelValue.text = when { courseBenefit.isZLinkUsed == true -> getString(R.string.transaction_a_link_channel) @@ -125,11 +128,11 @@ class TransactionBottomSheetDialogFragment : BottomSheetDialogFragment() { getString(R.string.transaction_stepik_channel) } - transactionPercentageValue.text = getString(R.string.transaction_share_value, courseBeneficiary.percent.removeSuffix(PERCENTAGE_SUFFIX)) - transactionIncomeValue.text = revenuePriceMapper.mapToDisplayPrice( + binding.transactionPercentageValue.text = getString(R.string.transaction_share_value, courseBeneficiary.percent.removeSuffix(PERCENTAGE_SUFFIX)) + binding.transactionIncomeValue.text = revenuePriceMapper.mapToDisplayPrice( courseBenefit.currencyCode, decimalFormat.format(courseBenefit.amount.toDoubleOrNull() ?: 0.0), debitPrefixRequired = courseBenefit.status == CourseBenefit.Status.DEBITED ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_reviews/ui/adapter/delegates/CourseReviewDataDelegate.kt b/app/src/main/java/org/stepik/android/view/course_reviews/ui/adapter/delegates/CourseReviewDataDelegate.kt index 0163c5585f..0a901fd5f1 100644 --- a/app/src/main/java/org/stepik/android/view/course_reviews/ui/adapter/delegates/CourseReviewDataDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course_reviews/ui/adapter/delegates/CourseReviewDataDelegate.kt @@ -10,8 +10,8 @@ import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.widget.PopupMenu import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory import androidx.core.view.isVisible -import kotlinx.android.synthetic.main.view_course_reviews_item.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.ViewCourseReviewsItemBinding import org.stepik.android.view.glide.ui.extension.wrapWithGlide import org.stepic.droid.util.DateTimeHelper import org.stepic.droid.util.resolveColorAttribute @@ -34,14 +34,16 @@ class CourseReviewDataDelegate( data is CourseReviewItem.Data inner class ViewHolder(root: View) : DelegateViewHolder(root) { - private val reviewIcon = root.reviewIcon + private val viewBinding = ViewCourseReviewsItemBinding.bind(root) + + private val reviewIcon = viewBinding.reviewIcon private val reviewIconWrapper = reviewIcon.wrapWithGlide() - private val reviewDate = root.reviewDate - private val reviewName = root.reviewName - private val reviewRating = root.reviewRating - private val reviewText = root.reviewText - private val reviewMenu = root.reviewMenu - private val reviewMark = root.reviewMark + private val reviewDate = viewBinding.reviewDate + private val reviewName = viewBinding.reviewName + private val reviewRating = viewBinding.reviewRating + private val reviewText = viewBinding.reviewText + private val reviewMenu = viewBinding.reviewMenu + private val reviewMark = viewBinding.reviewMark private val reviewIconPlaceholder = with(context.resources) { val coursePlaceholderBitmap = BitmapFactory.decodeResource(this, R.drawable.general_placeholder) @@ -109,4 +111,4 @@ class CourseReviewDataDelegate( popupMenu.show() } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_reviews/ui/adapter/delegates/CourseReviewSummaryDelegate.kt b/app/src/main/java/org/stepik/android/view/course_reviews/ui/adapter/delegates/CourseReviewSummaryDelegate.kt index e65cc6d1fc..38fca78c37 100644 --- a/app/src/main/java/org/stepik/android/view/course_reviews/ui/adapter/delegates/CourseReviewSummaryDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course_reviews/ui/adapter/delegates/CourseReviewSummaryDelegate.kt @@ -2,8 +2,8 @@ package org.stepik.android.view.course_reviews.ui.adapter.delegates import android.view.View import android.view.ViewGroup -import kotlinx.android.synthetic.main.view_course_review_summary_item.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.ViewCourseReviewSummaryItemBinding import org.stepic.droid.util.safeDiv import org.stepik.android.domain.course_reviews.model.CourseReviewItem import ru.nobird.android.ui.adapterdelegates.AdapterDelegate @@ -18,16 +18,18 @@ class CourseReviewSummaryDelegate : AdapterDelegate(root) { - private val summaryAverage = root.summaryAverage - private val summaryRating = root.summaryRating - private val summaryCount = root.summaryCount + private val viewBinding = ViewCourseReviewSummaryItemBinding.bind(root) + + private val summaryAverage = viewBinding.summaryAverage + private val summaryRating = viewBinding.summaryRating + private val summaryCount = viewBinding.summaryCount private val summaryDistribution = listOf( - root.summaryCount1Progress to root.summaryCount1Value, - root.summaryCount2Progress to root.summaryCount2Value, - root.summaryCount3Progress to root.summaryCount3Value, - root.summaryCount4Progress to root.summaryCount4Value, - root.summaryCount5Progress to root.summaryCount5Value + viewBinding.summaryCount1Progress to viewBinding.summaryCount1Value, + viewBinding.summaryCount2Progress to viewBinding.summaryCount2Value, + viewBinding.summaryCount3Progress to viewBinding.summaryCount3Value, + viewBinding.summaryCount4Progress to viewBinding.summaryCount4Value, + viewBinding.summaryCount5Progress to viewBinding.summaryCount5Value ) init { @@ -50,4 +52,4 @@ class CourseReviewSummaryDelegate : AdapterDelegate(root) { - private val bannerText = root.bannerText - private val bannerButton = root.bannerButton - private val bannerEmpty = root.bannerEmpty + private val viewBinding = ViewCourseReviewComposeBannerItemBinding.bind(root) + + private val bannerText = viewBinding.bannerText + private val bannerButton = viewBinding.bannerButton + private val bannerEmpty = viewBinding.bannerEmpty.root + private val bannerEmptyPlaceholder = viewBinding.bannerEmpty.placeholderMessage init { bannerText.setBackgroundColor(AppCompatResources.getColorStateList(bannerText.context, R.color.color_on_surface_alpha_12_selector).defaultColor) bannerButton.setOnClickListener { onCreateReviewClicked() } - bannerEmpty.placeholderMessage.setText(R.string.course_reviews_empty) + bannerEmptyPlaceholder.setText(R.string.course_reviews_empty) } override fun onBind(data: CourseReviewItem) { @@ -39,4 +41,4 @@ class CourseReviewsComposeBannerDelegate( bannerEmpty.isVisible = data.isReviewsEmpty } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_reviews/ui/dialog/ComposeCourseReviewDialogFragment.kt b/app/src/main/java/org/stepik/android/view/course_reviews/ui/dialog/ComposeCourseReviewDialogFragment.kt index a60be579f3..bf57fe76b0 100644 --- a/app/src/main/java/org/stepik/android/view/course_reviews/ui/dialog/ComposeCourseReviewDialogFragment.kt +++ b/app/src/main/java/org/stepik/android/view/course_reviews/ui/dialog/ComposeCourseReviewDialogFragment.kt @@ -12,10 +12,10 @@ import androidx.core.widget.doAfterTextChanged import androidx.fragment.app.DialogFragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider -import kotlinx.android.synthetic.main.dialog_compose_course_review.* -import kotlinx.android.synthetic.main.view_centered_toolbar.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.base.App +import org.stepic.droid.databinding.DialogComposeCourseReviewBinding import org.stepic.droid.ui.dialogs.LoadingProgressDialogFragment import org.stepic.droid.ui.util.setTintedNavigationIcon import org.stepic.droid.ui.util.snackbar @@ -52,6 +52,8 @@ class ComposeCourseReviewDialogFragment : DialogFragment(), ComposeCourseReviewV private val composeCourseReviewPresenter: ComposeCourseReviewPresenter by viewModels { viewModelFactory } + private val binding: DialogComposeCourseReviewBinding by viewBinding(DialogComposeCourseReviewBinding::bind) + private var courseId: Long by argument() private var courseReviewViewSource: String by argument() private val courseReview: CourseReview? by lazy { arguments?.getParcelable(ARG_COURSE_REVIEW) } @@ -87,11 +89,12 @@ class ComposeCourseReviewDialogFragment : DialogFragment(), ComposeCourseReviewV inflater.inflate(R.layout.dialog_compose_course_review, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - centeredToolbarTitle.setText(R.string.course_reviews_compose_title) - centeredToolbar.setNavigationOnClickListener { dismiss() } - centeredToolbar.setTintedNavigationIcon(R.drawable.ic_close_dark) - centeredToolbar.inflateMenu(R.menu.compose_course_review_menu) - centeredToolbar.setOnMenuItemClickListener { menuItem -> + val centeredToolbarContainer = binding.appBarLayout.centeredToolbarContainer + centeredToolbarContainer.centeredToolbarTitle.setText(R.string.course_reviews_compose_title) + centeredToolbarContainer.centeredToolbar.setNavigationOnClickListener { dismiss() } + centeredToolbarContainer.centeredToolbar.setTintedNavigationIcon(R.drawable.ic_close_dark) + centeredToolbarContainer.centeredToolbar.inflateMenu(R.menu.compose_course_review_menu) + centeredToolbarContainer.centeredToolbar.setOnMenuItemClickListener { menuItem -> if (menuItem.itemId == R.id.course_review_submit) { submitCourseReview() true @@ -104,16 +107,16 @@ class ComposeCourseReviewDialogFragment : DialogFragment(), ComposeCourseReviewV courseRating .takeIf { it > -1 } ?.let { - courseReviewRating.rating = courseRating + binding.courseReviewRating.rating = courseRating } courseReview?.let { - courseReviewEditText.setText(it.text) - courseReviewRating.rating = it.score.toFloat() + binding.courseReviewEditText.setText(it.text) + binding.courseReviewRating.rating = it.score.toFloat() } } invalidateMenuState() - courseReviewEditText.doAfterTextChanged { invalidateMenuState() } - courseReviewRating.setOnRatingBarChangeListener { _, _, _ -> invalidateMenuState() } + binding.courseReviewEditText.doAfterTextChanged { invalidateMenuState() } + binding.courseReviewRating.setOnRatingBarChangeListener { _, _, _ -> invalidateMenuState() } } override fun onStart() { @@ -134,11 +137,11 @@ class ComposeCourseReviewDialogFragment : DialogFragment(), ComposeCourseReviewV } private fun submitCourseReview() { - courseReviewEditText.hideKeyboard() + binding.courseReviewEditText.hideKeyboard() val oldCourseReview = courseReview - val text = courseReviewEditText.text?.toString() - val score = courseReviewRating.rating.toInt() + val text = binding.courseReviewEditText.text?.toString() + val score = binding.courseReviewRating.rating.toInt() if (oldCourseReview == null) { val courseReview = CourseReview( @@ -158,8 +161,8 @@ class ComposeCourseReviewDialogFragment : DialogFragment(), ComposeCourseReviewV } private fun invalidateMenuState() { - centeredToolbar.menu.findItem(R.id.course_review_submit)?.isEnabled = - !courseReviewEditText.text.isNullOrEmpty() && courseReviewRating.rating > 0 + binding.appBarLayout.centeredToolbarContainer.centeredToolbar.menu.findItem(R.id.course_review_submit)?.isEnabled = + !binding.courseReviewEditText.text.isNullOrEmpty() && binding.courseReviewRating.rating > 0 } override fun setState(state: ComposeCourseReviewView.State) { @@ -186,4 +189,4 @@ class ComposeCourseReviewDialogFragment : DialogFragment(), ComposeCourseReviewV override fun showNetworkError() { view?.snackbar(messageRes = R.string.connectionProblems) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/course_reviews/ui/fragment/CourseReviewsFragment.kt b/app/src/main/java/org/stepik/android/view/course_reviews/ui/fragment/CourseReviewsFragment.kt index be673c56e4..6d8e9c38c5 100644 --- a/app/src/main/java/org/stepik/android/view/course_reviews/ui/fragment/CourseReviewsFragment.kt +++ b/app/src/main/java/org/stepik/android/view/course_reviews/ui/fragment/CourseReviewsFragment.kt @@ -13,11 +13,9 @@ import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.empty_default.* -import kotlinx.android.synthetic.main.empty_default.view.* -import kotlinx.android.synthetic.main.error_no_connection.* -import kotlinx.android.synthetic.main.fragment_course_reviews.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.FragmentCourseReviewsBinding import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager @@ -41,6 +39,7 @@ import ru.nobird.android.view.base.ui.extension.showIfNotExists import javax.inject.Inject class CourseReviewsFragment : Fragment(), CourseReviewsView { + private val courseReviewsBinding: FragmentCourseReviewsBinding by viewBinding(FragmentCourseReviewsBinding::bind) companion object { fun newInstance(courseId: Long, courseTitle: String): Fragment = CourseReviewsFragment().apply { @@ -104,7 +103,7 @@ class CourseReviewsFragment : Fragment(), CourseReviewsView { inflater.inflate(R.layout.fragment_course_reviews, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - with(courseReviewsRecycler) { + with(courseReviewsBinding.courseReviewsRecycler) { layoutManager = LinearLayoutManager(context) adapter = courseReviewsAdapter @@ -132,17 +131,17 @@ class CourseReviewsFragment : Fragment(), CourseReviewsView { }) } - report_empty + courseReviewsBinding.reportEmpty .placeholderMessage .setText(R.string.course_reviews_empty) viewStateDelegate = ViewStateDelegate() - viewStateDelegate.addState(courseReviewsPlaceholder) - viewStateDelegate.addState(courseReviewsPlaceholder) - viewStateDelegate.addState(courseReviewsRecycler) - viewStateDelegate.addState(courseReviewsRecycler) - viewStateDelegate.addState(reportProblem) - viewStateDelegate.addState(report_empty) + viewStateDelegate.addState(courseReviewsBinding.courseReviewsPlaceholder) + viewStateDelegate.addState(courseReviewsBinding.courseReviewsPlaceholder) + viewStateDelegate.addState(courseReviewsBinding.courseReviewsRecycler) + viewStateDelegate.addState(courseReviewsBinding.courseReviewsRecycler) + viewStateDelegate.addState(courseReviewsBinding.reportProblem.root) + viewStateDelegate.addState(courseReviewsBinding.reportEmpty.root) } override fun onStart() { diff --git a/app/src/main/java/org/stepik/android/view/course_search/adapter/delegate/CourseSearchResultAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/course_search/adapter/delegate/CourseSearchResultAdapterDelegate.kt index 555212de51..ab10430706 100644 --- a/app/src/main/java/org/stepik/android/view/course_search/adapter/delegate/CourseSearchResultAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/course_search/adapter/delegate/CourseSearchResultAdapterDelegate.kt @@ -4,10 +4,10 @@ import android.view.View import android.view.ViewGroup import androidx.annotation.DrawableRes import androidx.core.view.isVisible +import by.kirich1409.viewbindingdelegate.viewBinding import com.bumptech.glide.Glide -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_course_search_result.* import org.stepic.droid.R +import org.stepic.droid.databinding.ItemCourseSearchResultBinding import org.stepic.droid.util.toFixed import org.stepik.android.domain.course_search.model.CourseSearchResult import org.stepik.android.domain.course_search.model.CourseSearchResultListItem @@ -29,10 +29,12 @@ class CourseSearchResultAdapterDelegate( ViewHolder(createView(parent, R.layout.item_course_search_result)) private inner class ViewHolder( - override val containerView: View - ) : DelegateViewHolder(containerView), LayoutContainer { + root: View + ) : DelegateViewHolder(root) { + private val viewBinding: ItemCourseSearchResultBinding by viewBinding { ItemCourseSearchResultBinding.bind(root) } + init { - courseSearchResultContainer.setOnClickListener { + viewBinding.courseSearchResultContainer.setOnClickListener { val data = itemData as? CourseSearchResultListItem.Data ?: return@setOnClickListener logEvent(data.courseSearchResult) with(data.courseSearchResult) { @@ -41,7 +43,7 @@ class CourseSearchResultAdapterDelegate( } } - courseSearchCommentContainer.setOnClickListener { + viewBinding.courseSearchCommentContainer.setOnClickListener { val data = itemData as? CourseSearchResultListItem.Data ?: return@setOnClickListener logEvent(data.courseSearchResult) with(data.courseSearchResult) { @@ -57,9 +59,9 @@ class CourseSearchResultAdapterDelegate( } override fun onBind(data: CourseSearchResultListItem) { data as CourseSearchResultListItem.Data - courseSearchTitle.text = data.courseSearchResult.searchResult.lessonTitle + viewBinding.courseSearchTitle.text = data.courseSearchResult.searchResult.lessonTitle - courseSearchTitle.text = buildString { + viewBinding.courseSearchTitle.text = buildString { if (data.courseSearchResult.section != null && data.courseSearchResult.unit != null) { append("${data.courseSearchResult.section.position}.${data.courseSearchResult.unit.position} ") } @@ -69,24 +71,24 @@ class CourseSearchResultAdapterDelegate( } } - Glide.with(courseSearchIcon) + Glide.with(viewBinding.courseSearchIcon) .asBitmap() .load(data.courseSearchResult.searchResult.lessonCoverUrl) .placeholder(R.drawable.general_placeholder) .centerCrop() - .into(courseSearchIcon) + .into(viewBinding.courseSearchIcon) val progress = data.courseSearchResult.progress val lesson = data.courseSearchResult.lesson val isProgressAvailable = progress != null - courseSearchProgressPlaceholder.isVisible = data.courseSearchResult.progress == null - courseSearchTextProgress.isVisible = isProgressAvailable - courseSearchTimeToComplete.isVisible = isProgressAvailable - courseSearchViewCountIcon.isVisible = isProgressAvailable - courseSearchViewCount.isVisible = isProgressAvailable - courseSearchRatingIcon.isVisible = isProgressAvailable - courseSearchRating.isVisible = isProgressAvailable + viewBinding.courseSearchProgressPlaceholder.isVisible = data.courseSearchResult.progress == null + viewBinding.courseSearchTextProgress.isVisible = isProgressAvailable + viewBinding.courseSearchTimeToComplete.isVisible = isProgressAvailable + viewBinding.courseSearchViewCountIcon.isVisible = isProgressAvailable + viewBinding.courseSearchViewCount.isVisible = isProgressAvailable + viewBinding.courseSearchRatingIcon.isVisible = isProgressAvailable + viewBinding.courseSearchRating.isVisible = isProgressAvailable if (progress != null && progress.cost > 0) { val score = progress @@ -94,10 +96,10 @@ class CourseSearchResultAdapterDelegate( ?.toFloatOrNull() ?: 0f - courseSearchTextProgress.text = context.resources.getString(R.string.course_content_text_progress_points, + viewBinding.courseSearchTextProgress.text = context.resources.getString(R.string.course_content_text_progress_points, score.toFixed(context.resources.getInteger(R.integer.score_decimal_count)), progress.cost) } else { - courseSearchTextProgress.isVisible = false + viewBinding.courseSearchTextProgress.isVisible = false } if (lesson != null) { @@ -110,10 +112,10 @@ class CourseSearchResultAdapterDelegate( } else { context.resources.getString(R.string.course_content_time_to_complete_hours_unit, timeToComplete / 3600) } - courseSearchTimeToComplete.text = context.getString(R.string.course_content_time_to_complete, timeToCompleteString) + viewBinding.courseSearchTimeToComplete.text = context.getString(R.string.course_content_time_to_complete, timeToCompleteString) } - courseSearchViewCount.text = lesson.passedBy.toString() + viewBinding.courseSearchViewCount.text = lesson.passedBy.toString() @DrawableRes val unitRatingDrawableRes = @@ -123,25 +125,25 @@ class CourseSearchResultAdapterDelegate( R.drawable.ic_course_content_like } - courseSearchRatingIcon.setImageResource(unitRatingDrawableRes) - courseSearchRating.text = abs(lesson.voteDelta).toString() + viewBinding.courseSearchRatingIcon.setImageResource(unitRatingDrawableRes) + viewBinding.courseSearchRating.text = abs(lesson.voteDelta).toString() } val hasComment = with(data.courseSearchResult.searchResult) { comment != null && commentUser != null } - courseSearchCommentContainer.isVisible = hasComment && data.courseSearchResult.commentOwner != null + viewBinding.courseSearchCommentContainer.isVisible = hasComment && data.courseSearchResult.commentOwner != null if (hasComment && data.courseSearchResult.commentOwner != null) { - Glide.with(courseSearchCommentUserIcon) + Glide.with(viewBinding.courseSearchCommentUserIcon) .asBitmap() .load(data.courseSearchResult.commentOwner.avatar) .placeholder(R.drawable.general_placeholder) .centerCrop() - .into(courseSearchCommentUserIcon) - courseSearchCommentUserName.text = data.courseSearchResult.commentOwner.fullName - courseSearchCommentText.text = data.courseSearchResult.searchResult.commentText + .into(viewBinding.courseSearchCommentUserIcon) + viewBinding.courseSearchCommentUserName.text = data.courseSearchResult.commentOwner.fullName + viewBinding.courseSearchCommentText.text = data.courseSearchResult.searchResult.commentText } } @@ -154,4 +156,4 @@ class CourseSearchResultAdapterDelegate( } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/download/ui/activity/DownloadActivity.kt b/app/src/main/java/org/stepik/android/view/download/ui/activity/DownloadActivity.kt index ec61379d56..b176020baf 100644 --- a/app/src/main/java/org/stepik/android/view/download/ui/activity/DownloadActivity.kt +++ b/app/src/main/java/org/stepik/android/view/download/ui/activity/DownloadActivity.kt @@ -15,11 +15,9 @@ import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.SimpleItemAnimator -import kotlinx.android.synthetic.main.activity_download.* -import kotlinx.android.synthetic.main.empty_certificates.goToCatalog -import kotlinx.android.synthetic.main.empty_downloading.* -import kotlinx.android.synthetic.main.progress_bar_on_empty_screen.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ActivityDownloadBinding import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.base.App import org.stepic.droid.base.FragmentActivityBase @@ -41,6 +39,8 @@ import ru.nobird.android.view.base.ui.extension.showIfNotExists import javax.inject.Inject class DownloadActivity : FragmentActivityBase(), DownloadView, RemoveCachedContentDialog.Callback { + private val binding: ActivityDownloadBinding by viewBinding(ActivityDownloadBinding::bind) + companion object { private const val MB = 1024 * 1024L @@ -74,7 +74,7 @@ class DownloadActivity : FragmentActivityBase(), DownloadView, RemoveCachedConte onItemRemoveClick = ::showRemoveCourseDialog ) - with(downloadsRecyclerView) { + with(binding.downloadsRecyclerView) { (itemAnimator as? SimpleItemAnimator)?.supportsChangeAnimations = false adapter = downloadedCoursesAdapter layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) @@ -82,13 +82,13 @@ class DownloadActivity : FragmentActivityBase(), DownloadView, RemoveCachedConte } initViewStateDelegate() - goToCatalog.setOnClickListener { screenManager.showCatalog(this) } + binding.emptyDownloadsView.goToCatalog.setOnClickListener { screenManager.showCatalog(this) } downloadPresenter.fetchStorage() downloadPresenter.fetchDownloadedCourses() - TextViewCompat.setCompoundDrawableTintList(downloadsOtherApps, ColorStateList.valueOf(ContextCompat.getColor(this, R.color.color_overlay_yellow))) - TextViewCompat.setCompoundDrawableTintList(downloadsStepik, ColorStateList.valueOf(ContextCompat.getColor(this, R.color.color_overlay_green))) - TextViewCompat.setCompoundDrawableTintList(downloadsFree, ColorStateList.valueOf(ContextCompat.getColor(this, R.color.color_elevation_overlay_2dp))) + TextViewCompat.setCompoundDrawableTintList(binding.downloadsOtherApps, ColorStateList.valueOf(ContextCompat.getColor(this, R.color.color_overlay_yellow))) + TextViewCompat.setCompoundDrawableTintList(binding.downloadsStepik, ColorStateList.valueOf(ContextCompat.getColor(this, R.color.color_overlay_green))) + TextViewCompat.setCompoundDrawableTintList(binding.downloadsFree, ColorStateList.valueOf(ContextCompat.getColor(this, R.color.color_elevation_overlay_2dp))) } private fun injectComponent() { @@ -120,9 +120,9 @@ class DownloadActivity : FragmentActivityBase(), DownloadView, RemoveCachedConte private fun initViewStateDelegate() { viewStateDelegate.addState() - viewStateDelegate.addState(loadProgressbarOnEmptyScreen) - viewStateDelegate.addState(emptyDownloading) - viewStateDelegate.addState(downloadStorageContainer, downloadsRecyclerView, downloadsStorageDivider) + viewStateDelegate.addState(binding.progressBarOnEmptyScreen.loadProgressbarOnEmptyScreen) + viewStateDelegate.addState(binding.emptyDownloadsView.root) + viewStateDelegate.addState(binding.downloadStorageContainer, binding.downloadsRecyclerView, binding.downloadsStorageDivider.root) } override fun setState(state: DownloadView.State) { @@ -141,14 +141,14 @@ class DownloadActivity : FragmentActivityBase(), DownloadView, RemoveCachedConte } override fun setStorageInfo(contentSize: Long, avalableSize: Long, totalSize: Long) { - downloadStorageUsed.text = buildSpannedString { + binding.downloadStorageUsed.text = buildSpannedString { bold { append(TextUtil.formatBytes(contentSize, MB)) } append(resources.getString(R.string.downloads_is_used_by_stepik)) } - downloadsFree.text = resources.getString(R.string.downloads_free_space, TextUtil.formatBytes(avalableSize, MB)) - downloadsStorageProgress.max = (totalSize / MB).toInt() - downloadsStorageProgress.progress = ((totalSize - avalableSize) / MB).toInt() - downloadsStorageProgress.secondaryProgress = (downloadsStorageProgress.progress + (contentSize / MB)).toInt() + binding.downloadsFree.text = resources.getString(R.string.downloads_free_space, TextUtil.formatBytes(avalableSize, MB)) + binding.downloadsStorageProgress.max = (totalSize / MB).toInt() + binding.downloadsStorageProgress.progress = ((totalSize - avalableSize) / MB).toInt() + binding.downloadsStorageProgress.secondaryProgress = (binding.downloadsStorageProgress.progress + (contentSize / MB)).toInt() } private fun showRemoveCourseDialog(downloadItem: DownloadItem) { @@ -169,6 +169,6 @@ class DownloadActivity : FragmentActivityBase(), DownloadView, RemoveCachedConte } override fun showRemoveTaskError() { - root.snackbar(messageRes = R.string.downloads_remove_task_error) + binding.root.snackbar(messageRes = R.string.downloads_remove_task_error) } } \ No newline at end of file diff --git a/app/src/main/java/org/stepik/android/view/download/ui/adapter/DownloadedCoursesAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/download/ui/adapter/DownloadedCoursesAdapterDelegate.kt index 539c50a643..4b86e3e933 100644 --- a/app/src/main/java/org/stepik/android/view/download/ui/adapter/DownloadedCoursesAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/download/ui/adapter/DownloadedCoursesAdapterDelegate.kt @@ -2,9 +2,10 @@ package org.stepik.android.view.download.ui.adapter import android.view.View import android.view.ViewGroup +import by.kirich1409.viewbindingdelegate.viewBinding import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.downloaded_course_item.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.DownloadedCourseItemBinding import org.stepic.droid.persistence.model.DownloadItem import org.stepic.droid.persistence.model.DownloadProgress import ru.nobird.android.ui.adapterdelegates.AdapterDelegate @@ -24,30 +25,27 @@ class DownloadedCoursesAdapterDelegate( ViewHolder(createView(parent, R.layout.downloaded_course_item)) private inner class ViewHolder(root: View) : DelegateViewHolder(root) { - - private val downloadedCourseTitle = root.downloadedCourseName - private val downloadedCourseImage = root.downloadedCourseImage - private val downloadedCourseStatus = root.downloadedCourseStatus + private val viewBinding: DownloadedCourseItemBinding by viewBinding { DownloadedCourseItemBinding.bind(itemView) } init { root.setOnClickListener { itemData?.let(onItemClick) } - downloadedCourseStatus.setOnClickListener { - if (downloadedCourseStatus.status is DownloadProgress.Status.Cached) { + viewBinding.downloadedCourseStatus.setOnClickListener { + if (viewBinding.downloadedCourseStatus.status is DownloadProgress.Status.Cached) { itemData?.let(onItemRemoveClick) } } } override fun onBind(data: DownloadItem) { - downloadedCourseTitle.text = data.course.title - downloadedCourseStatus.status = data.status + viewBinding.downloadedCourseName.text = data.course.title + viewBinding.downloadedCourseStatus.status = data.status Glide.with(context) .asBitmap() .load(data.course.cover) .placeholder(R.drawable.general_placeholder) .fitCenter() - .into(downloadedCourseImage) + .into(viewBinding.downloadedCourseImage) } } } \ No newline at end of file diff --git a/app/src/main/java/org/stepik/android/view/fast_continue/ui/fragment/FastContinueFragment.kt b/app/src/main/java/org/stepik/android/view/fast_continue/ui/fragment/FastContinueFragment.kt index df8f20ac3c..f861f67dbe 100644 --- a/app/src/main/java/org/stepik/android/view/fast_continue/ui/fragment/FastContinueFragment.kt +++ b/app/src/main/java/org/stepik/android/view/fast_continue/ui/fragment/FastContinueFragment.kt @@ -8,9 +8,10 @@ import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider +import by.kirich1409.viewbindingdelegate.viewBinding import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.fragment_fast_continue.* import org.stepic.droid.R +import org.stepic.droid.databinding.FragmentFastContinueBinding import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager @@ -43,6 +44,7 @@ class FastContinueFragment : Fragment(R.layout.fragment_fast_continue), FastCont @Inject internal lateinit var screenManager: ScreenManager + private val fastContinueBinding: FragmentFastContinueBinding by viewBinding(FragmentFastContinueBinding::bind) private val fastContinuePresenter: FastContinuePresenter by viewModels { viewModelFactory } private lateinit var viewStateDelegate: ViewStateDelegate @@ -66,13 +68,13 @@ class FastContinueFragment : Fragment(R.layout.fragment_fast_continue), FastCont super.onViewCreated(view, savedInstanceState) viewStateDelegate = ViewStateDelegate() viewStateDelegate.addState() - viewStateDelegate.addState(fastContinueProgress) - viewStateDelegate.addState(fastContinuePlaceholder) - viewStateDelegate.addState(fastContinuePlaceholder) - viewStateDelegate.addState(fastContinueMask) + viewStateDelegate.addState(fastContinueBinding.fastContinueProgress) + viewStateDelegate.addState(fastContinueBinding.fastContinuePlaceholder) + viewStateDelegate.addState(fastContinueBinding.fastContinuePlaceholder) + viewStateDelegate.addState(fastContinueBinding.fastContinueMask) - fastContinueOverlay.isEnabled = true - fastContinueAction.isEnabled = true + fastContinueBinding.fastContinueOverlay.isEnabled = true + fastContinueBinding.fastContinueAction.isEnabled = true } override fun onStart() { @@ -105,8 +107,8 @@ class FastContinueFragment : Fragment(R.layout.fragment_fast_continue), FastCont is FastContinueView.State.Content -> { analytic.reportEvent(Analytic.FastContinue.CONTINUE_SHOWN) setCourse(state.courseListItem) - fastContinueOverlay.setOnClickListener { handleContinueCourseClick(state.courseListItem.course) } - fastContinueAction.setOnClickListener { handleContinueCourseClick(state.courseListItem.course) } + fastContinueBinding.fastContinueOverlay.setOnClickListener { handleContinueCourseClick(state.courseListItem.course) } + fastContinueBinding.fastContinueAction.setOnClickListener { handleContinueCourseClick(state.courseListItem.course) } } else -> Unit } @@ -119,9 +121,9 @@ class FastContinueFragment : Fragment(R.layout.fragment_fast_continue), FastCont .load(courseListItem.course.cover) .placeholder(R.drawable.general_placeholder) .fitCenter() - .into(fastContinueCourseCover) + .into(fastContinueBinding.fastContinueCourseCover) - fastContinueCourseName.text = courseListItem.course.title + fastContinueBinding.fastContinueCourseName.text = courseListItem.course.title val progress = courseListItem.courseStats.progress val needShow = if (progress != null && progress.cost > 0f) { @@ -130,19 +132,19 @@ class FastContinueFragment : Fragment(R.layout.fragment_fast_continue), FastCont ?.toFloatOrNull() ?: 0f - fastContinueCourseProgressText.text = getString(R.string.course_current_progress, score.toFixed(resources.getInteger(R.integer.score_decimal_count)), progress.cost) - fastContinueCourseProgress.progress = (score * 100 / progress.cost).toInt() + fastContinueBinding.fastContinueCourseProgressText.text = getString(R.string.course_current_progress, score.toFixed(resources.getInteger(R.integer.score_decimal_count)), progress.cost) + fastContinueBinding.fastContinueCourseProgress.progress = (score * 100 / progress.cost).toInt() true } else { - fastContinueCourseProgress.progress = 0 + fastContinueBinding.fastContinueCourseProgress.progress = 0 false } - fastContinueCourseProgressText.isVisible = needShow + fastContinueBinding.fastContinueCourseProgressText.isVisible = needShow } private fun showPlaceholder(@StringRes stringRes: Int, listener: (view: View) -> Unit) { - fastContinuePlaceholder.setPlaceholderText(stringRes) - fastContinuePlaceholder.setOnClickListener(listener) + fastContinueBinding.fastContinuePlaceholder.setPlaceholderText(stringRes) + fastContinueBinding.fastContinuePlaceholder.setOnClickListener(listener) } private fun handleContinueCourseClick(course: Course) { @@ -163,8 +165,8 @@ class FastContinueFragment : Fragment(R.layout.fragment_fast_continue), FastCont } override fun setBlockingLoading(isLoading: Boolean) { - fastContinueOverlay.isEnabled = !isLoading - fastContinueAction.isEnabled = !isLoading + fastContinueBinding.fastContinueOverlay.isEnabled = !isLoading + fastContinueBinding.fastContinueAction.isEnabled = !isLoading if (isLoading) { ProgressHelper.activate(progressDialogFragment, fragmentManager, LoadingProgressDialogFragment.TAG) } else { diff --git a/app/src/main/java/org/stepik/android/view/fast_continue/ui/fragment/FastContinueNewHomeFragment.kt b/app/src/main/java/org/stepik/android/view/fast_continue/ui/fragment/FastContinueNewHomeFragment.kt index ff21764dc5..8e70b32dab 100644 --- a/app/src/main/java/org/stepik/android/view/fast_continue/ui/fragment/FastContinueNewHomeFragment.kt +++ b/app/src/main/java/org/stepik/android/view/fast_continue/ui/fragment/FastContinueNewHomeFragment.kt @@ -7,11 +7,10 @@ import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider +import by.kirich1409.viewbindingdelegate.viewBinding import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.fragment_fast_continue_new_home.* -import kotlinx.android.synthetic.main.view_fast_continue_information.* -import kotlinx.android.synthetic.main.view_fast_continue_information.fastContinueCourseCover import org.stepic.droid.R +import org.stepic.droid.databinding.FragmentFastContinueNewHomeBinding import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager @@ -30,6 +29,8 @@ import org.stepik.android.view.ui.delegate.ViewStateDelegate import javax.inject.Inject class FastContinueNewHomeFragment : Fragment(R.layout.fragment_fast_continue_new_home), FastContinueView { + private val binding: FragmentFastContinueNewHomeBinding by viewBinding(FragmentFastContinueNewHomeBinding::bind) + companion object { fun newInstance(): Fragment = FastContinueNewHomeFragment() @@ -68,9 +69,9 @@ class FastContinueNewHomeFragment : Fragment(R.layout.fragment_fast_continue_new viewStateDelegate = ViewStateDelegate() viewStateDelegate.addState() viewStateDelegate.addState() - viewStateDelegate.addState(fastContinueEmpty) - viewStateDelegate.addState(fastContinueEmpty) - viewStateDelegate.addState(fastContinueInformation) + viewStateDelegate.addState(binding.fastContinueEmpty.root) + viewStateDelegate.addState(binding.fastContinueEmpty.root) + viewStateDelegate.addState(binding.fastContinueInformation.root) } override fun onStart() { @@ -89,14 +90,14 @@ class FastContinueNewHomeFragment : Fragment(R.layout.fragment_fast_continue_new when (state) { is FastContinueView.State.Empty -> { analytic.reportEvent(Analytic.FastContinue.EMPTY_COURSES_SHOWN) - fastContinueEmpty.setOnClickListener { + binding.fastContinueEmpty.root.setOnClickListener { analytic.reportEvent(Analytic.FastContinue.EMPTY_COURSES_CLICK) screenManager.showCatalog(context) } } is FastContinueView.State.Anonymous -> { analytic.reportEvent(Analytic.FastContinue.AUTH_SHOWN) - fastContinueEmpty.setOnClickListener { + binding.fastContinueEmpty.root.setOnClickListener { analytic.reportEvent(Analytic.FastContinue.AUTH_CLICK) screenManager.showCatalog(context) } @@ -104,7 +105,7 @@ class FastContinueNewHomeFragment : Fragment(R.layout.fragment_fast_continue_new is FastContinueView.State.Content -> { analytic.reportEvent(Analytic.FastContinue.CONTINUE_SHOWN) setCourse(state.courseListItem) - fastContinueInformation.setOnClickListener { handleContinueCourseClick(state.courseListItem.course) } + binding.fastContinueInformation.root.setOnClickListener { handleContinueCourseClick(state.courseListItem.course) } } else -> Unit } @@ -128,9 +129,9 @@ class FastContinueNewHomeFragment : Fragment(R.layout.fragment_fast_continue_new .load(courseListItem.course.cover) .placeholder(R.drawable.general_placeholder) .fitCenter() - .into(fastContinueCourseCover) + .into(binding.fastContinueInformation.fastContinueCourseCover) - fastContinueCourseTitle.text = courseListItem.course.title + binding.fastContinueInformation.fastContinueCourseTitle.text = courseListItem.course.title val progress = courseListItem.courseStats.progress val needShow = if (progress != null && progress.cost > 0f) { @@ -144,8 +145,8 @@ class FastContinueNewHomeFragment : Fragment(R.layout.fragment_fast_continue_new } else { false } - fastContinueProgressView.isVisible = needShow - fastContinueProgressTitle.isVisible = needShow + binding.fastContinueInformation.fastContinueProgressView.isVisible = needShow + binding.fastContinueInformation.fastContinueProgressTitle.isVisible = needShow } private fun handleContinueCourseClick(course: Course) { @@ -166,8 +167,8 @@ class FastContinueNewHomeFragment : Fragment(R.layout.fragment_fast_continue_new } override fun setBlockingLoading(isLoading: Boolean) { - fastContinueInformation.isEnabled = !isLoading - fastContinueInformation.isEnabled = !isLoading + binding.fastContinueInformation.root.isEnabled = !isLoading + binding.fastContinueInformation.root.isEnabled = !isLoading if (isLoading) { ProgressHelper.activate(progressDialogFragment, parentFragmentManager, LoadingProgressDialogFragment.TAG) } else { @@ -176,8 +177,8 @@ class FastContinueNewHomeFragment : Fragment(R.layout.fragment_fast_continue_new } private fun prepareViewForProgress(score: Float, cost: Long) { - fastContinueProgressView.progress = (score * 100 safeDiv cost) / 100f - fastContinueProgressTitle.text = resources + binding.fastContinueInformation.fastContinueProgressView.progress = (score * 100 safeDiv cost) / 100f + binding.fastContinueInformation.fastContinueProgressTitle.text = resources .getString(R.string.course_content_text_progress, score.toFixed(resources.getInteger(R.integer.score_decimal_count)), cost) } diff --git a/app/src/main/java/org/stepik/android/view/filter/ui/dialog/FilterBottomSheetDialogFragment.kt b/app/src/main/java/org/stepik/android/view/filter/ui/dialog/FilterBottomSheetDialogFragment.kt index 7532508b82..e7c934498a 100644 --- a/app/src/main/java/org/stepik/android/view/filter/ui/dialog/FilterBottomSheetDialogFragment.kt +++ b/app/src/main/java/org/stepik/android/view/filter/ui/dialog/FilterBottomSheetDialogFragment.kt @@ -8,11 +8,12 @@ import android.widget.CompoundButton import androidx.appcompat.widget.AppCompatRadioButton import androidx.core.view.isVisible import androidx.fragment.app.DialogFragment +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import kotlinx.android.synthetic.main.bottom_sheet_dialog_filter.* import org.stepic.droid.R +import org.stepic.droid.databinding.BottomSheetDialogFilterBinding import org.stepic.droid.base.App import org.stepic.droid.model.StepikFilter import org.stepic.droid.preferences.SharedPreferenceHelper @@ -23,6 +24,8 @@ import ru.nobird.android.view.base.ui.extension.argument import javax.inject.Inject class FilterBottomSheetDialogFragment : BottomSheetDialogFragment() { + private val binding: BottomSheetDialogFilterBinding by viewBinding(BottomSheetDialogFilterBinding::bind) + companion object { const val TAG = "FilterBottomSheetDialogFragment" @@ -62,37 +65,37 @@ class FilterBottomSheetDialogFragment : BottomSheetDialogFragment() { super.onViewCreated(view, savedInstanceState) defaultLanguageRadioButton = obtainDefaultLanguageRadioButton() - radioButtons = listOf(anyRadioButton, rusRadioButton, engRadioButton) - compoundButtons = radioButtons + listOf(certificatesSwitch, freeSwitch) + radioButtons = listOf(binding.anyRadioButton, binding.rusRadioButton, binding.engRadioButton) + compoundButtons = radioButtons + listOf(binding.certificatesSwitch, binding.freeSwitch) setupFilters(filterQuery) - dismissFilter.isVisible = isMustShowDismiss() + binding.dismissFilter.isVisible = isMustShowDismiss() radioButtons.forEach { it.setOnCheckedChangeListener { buttonView, isChecked -> if (isChecked) { onRadioButtonClicked(buttonView) - dismissFilter.isVisible = isMustShowDismiss() + binding.dismissFilter.isVisible = isMustShowDismiss() } } } - certificatesSwitch.setOnCheckedChangeListener { _, _ -> - dismissFilter.isVisible = isMustShowDismiss() + binding.certificatesSwitch.setOnCheckedChangeListener { _, _ -> + binding.dismissFilter.isVisible = isMustShowDismiss() } - freeSwitch.setOnCheckedChangeListener { _, _ -> - dismissFilter.isVisible = isMustShowDismiss() + binding.freeSwitch.setOnCheckedChangeListener { _, _ -> + binding.dismissFilter.isVisible = isMustShowDismiss() } - dismissFilter.setOnClickListener { + binding.dismissFilter.setOnClickListener { compoundButtons.forEach { compoundButton -> compoundButton.isChecked = false } defaultLanguageRadioButton.isChecked = true it.isVisible = false } - applyFilterAction.setOnClickListener { + binding.applyFilterAction.setOnClickListener { val newFilterQuery = mapFiltersToQuery() if (newFilterQuery != filterQuery || parentFragment is CatalogFragment) { (activity.safeCast() ?: parentFragment.safeCast()) @@ -150,10 +153,10 @@ class FilterBottomSheetDialogFragment : BottomSheetDialogFragment() { private fun obtainDefaultLanguageRadioButton(): AppCompatRadioButton = when (sharedPreferenceHelper.languageForFeatured) { StepikFilter.RUSSIAN.language -> - rusRadioButton + binding.rusRadioButton StepikFilter.ENGLISH.language -> - engRadioButton + binding.engRadioButton else -> throw IllegalStateException() diff --git a/app/src/main/java/org/stepik/android/view/in_app_web_view/ui/dialog/InAppWebViewDialogFragment.kt b/app/src/main/java/org/stepik/android/view/in_app_web_view/ui/dialog/InAppWebViewDialogFragment.kt index db294f4eaf..23d6760d07 100644 --- a/app/src/main/java/org/stepik/android/view/in_app_web_view/ui/dialog/InAppWebViewDialogFragment.kt +++ b/app/src/main/java/org/stepik/android/view/in_app_web_view/ui/dialog/InAppWebViewDialogFragment.kt @@ -24,16 +24,13 @@ import androidx.core.view.isVisible import androidx.fragment.app.DialogFragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.dialog.MaterialAlertDialogBuilder -import kotlinx.android.synthetic.main.dialog_in_app_web_view.* -import kotlinx.android.synthetic.main.dialog_in_app_web_view.view.* -import kotlinx.android.synthetic.main.error_no_connection_with_button.* -import kotlinx.android.synthetic.main.progress_bar_on_empty_screen.* -import kotlinx.android.synthetic.main.view_centered_toolbar.* import org.stepic.droid.R import org.stepic.droid.base.App import org.stepic.droid.configuration.EndpointResolver import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.DialogInAppWebViewBinding import org.stepic.droid.ui.util.setTintedNavigationIcon import org.stepik.android.presentation.in_app_web_view.InAppWebViewPresenter import org.stepik.android.presentation.in_app_web_view.InAppWebViewView @@ -76,6 +73,8 @@ class InAppWebViewDialogFragment : DialogFragment(), InAppWebViewView { @Inject internal lateinit var endpointResolver: EndpointResolver + private val binding: DialogInAppWebViewBinding by viewBinding(DialogInAppWebViewBinding::bind) + private val inAppWebViewPresenter: InAppWebViewPresenter by viewModels { viewModelFactory } private var title: String by argument() @@ -162,40 +161,42 @@ class InAppWebViewDialogFragment : DialogFragment(), InAppWebViewView { } } } - webView?.let { root.containerView.addView(it) } + webView?.let { root.findViewById(R.id.containerView).addView(it) } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { viewStateDelegate = ViewStateDelegate() viewStateDelegate.addState() - viewStateDelegate.addState(loadProgressbarOnEmptyScreen) - viewStateDelegate.addState(loadProgressbarOnEmptyScreen) - viewStateDelegate.addState(error) + viewStateDelegate.addState(binding.progressBarOnEmptyScreen.root) + viewStateDelegate.addState(binding.progressBarOnEmptyScreen.root) + viewStateDelegate.addState(binding.errorNoConnection.error) viewStateDelegate.addState(webView as View) - centeredToolbarTitle.text = title - centeredToolbar.setNavigationOnClickListener { - if (showsDialog) { - dismiss() - } else { - activity?.finish() + with(binding.centeredAppbar.centeredToolbarContainer) { + centeredToolbarTitle.text = title + centeredToolbar.setNavigationOnClickListener { + if (showsDialog) { + dismiss() + } else { + activity?.finish() + } } - } - centeredToolbar.setTintedNavigationIcon(R.drawable.ic_close_dark) - centeredToolbar.inflateMenu(R.menu.in_app_web_view_menu) - centeredToolbar.setOnMenuItemClickListener { menuItem -> - when (menuItem.itemId) { - R.id.menu_item_external -> { - val externalUrl = webView?.url ?: url - screenManager.openLinkInWebBrowser(requireContext(), Uri.parse(externalUrl)) - true + centeredToolbar.setTintedNavigationIcon(R.drawable.ic_close_dark) + centeredToolbar.inflateMenu(R.menu.in_app_web_view_menu) + centeredToolbar.setOnMenuItemClickListener { menuItem -> + when (menuItem.itemId) { + R.id.menu_item_external -> { + val externalUrl = webView?.url ?: url + screenManager.openLinkInWebBrowser(requireContext(), Uri.parse(externalUrl)) + true + } + else -> + super.onOptionsItemSelected(menuItem) } - else -> - super.onOptionsItemSelected(menuItem) } } - tryAgain.setOnClickListener { setDataToPresenter(forceUpdate = true) } + binding.errorNoConnection.tryAgain.setOnClickListener { setDataToPresenter(forceUpdate = true) } setDataToPresenter() } @@ -229,7 +230,7 @@ class InAppWebViewDialogFragment : DialogFragment(), InAppWebViewView { } override fun onDestroyView() { - containerView.removeView(webView) + binding.containerView.removeView(webView) super.onDestroyView() } @@ -319,4 +320,4 @@ class InAppWebViewDialogFragment : DialogFragment(), InAppWebViewView { interface Callback { fun onDismissed() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/injection/course/CourseHeaderDelegateFactory.kt b/app/src/main/java/org/stepik/android/view/injection/course/CourseHeaderDelegateFactory.kt index d6d16dd159..603e4ebe5b 100644 --- a/app/src/main/java/org/stepik/android/view/injection/course/CourseHeaderDelegateFactory.kt +++ b/app/src/main/java/org/stepik/android/view/injection/course/CourseHeaderDelegateFactory.kt @@ -3,6 +3,7 @@ package org.stepik.android.view.injection.course import android.app.Activity import dagger.assisted.Assisted import dagger.assisted.AssistedFactory +import org.stepic.droid.databinding.ActivityCourseBinding import org.stepik.android.domain.course.analytic.CourseViewSource import org.stepik.android.presentation.course.CoursePresenter import org.stepik.android.presentation.course_purchase.model.CoursePurchaseData @@ -12,6 +13,7 @@ import org.stepik.android.view.course.ui.delegates.CourseHeaderDelegate interface CourseHeaderDelegateFactory { fun create( courseActivity: Activity, + courseBinding: ActivityCourseBinding, coursePresenter: CoursePresenter, courseViewSource: CourseViewSource, @Assisted("isAuthorized") diff --git a/app/src/main/java/org/stepik/android/view/learning_actions/ui/adapter/delegate/UserReviewsActionAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/learning_actions/ui/adapter/delegate/UserReviewsActionAdapterDelegate.kt index 12cc3feacb..3aed5c8ea2 100644 --- a/app/src/main/java/org/stepik/android/view/learning_actions/ui/adapter/delegate/UserReviewsActionAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/learning_actions/ui/adapter/delegate/UserReviewsActionAdapterDelegate.kt @@ -8,9 +8,9 @@ import androidx.core.text.buildSpannedString import androidx.core.text.color import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_learning_action_user_reviews.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemLearningActionUserReviewsBinding import org.stepik.android.presentation.user_reviews.UserReviewsFeature import org.stepik.android.view.learning_actions.model.LearningActionsItem import ru.nobird.android.ui.adapterdelegates.AdapterDelegate @@ -31,18 +31,20 @@ class UserReviewsActionAdapterDelegate(private val onClick: () -> Unit) : Adapte } private class ViewHolder( - override val containerView: View, + root: View, private val onClick: () -> Unit - ) : DelegateViewHolder(containerView), LayoutContainer { + ) : DelegateViewHolder(root) { + private val viewBinding: ItemLearningActionUserReviewsBinding by viewBinding { ItemLearningActionUserReviewsBinding.bind(root) } + private val viewStateDelegate = ViewStateDelegate() init { viewStateDelegate.addState() - viewStateDelegate.addState(userReviewsActionTitle, userReviewsActionLoadingView) - viewStateDelegate.addState(userReviewsActionTitle) - viewStateDelegate.addState(userReviewsActionTitle, userReviewsActionCourseCount) - viewStateDelegate.addState(userReviewsActionTitle, userReviewsActionCourseCount) - containerView.setOnClickListener { onClick() } + viewStateDelegate.addState(viewBinding.userReviewsActionTitle, viewBinding.userReviewsActionLoadingView) + viewStateDelegate.addState(viewBinding.userReviewsActionTitle) + viewStateDelegate.addState(viewBinding.userReviewsActionTitle, viewBinding.userReviewsActionCourseCount) + viewStateDelegate.addState(viewBinding.userReviewsActionTitle, viewBinding.userReviewsActionCourseCount) + itemView.setOnClickListener { onClick() } } override fun onBind(data: LearningActionsItem) { @@ -52,8 +54,8 @@ class UserReviewsActionAdapterDelegate(private val onClick: () -> Unit) : Adapte private fun render(state: UserReviewsFeature.State) { viewStateDelegate.switchState(state) - userReviewsPotentialIcon.isVisible = state is UserReviewsFeature.State.Content && state.userCourseReviewsResult.potentialReviewItems.isNotEmpty() - userReviewsActionCourseCount.text = + viewBinding.userReviewsPotentialIcon.isVisible = state is UserReviewsFeature.State.Content && state.userCourseReviewsResult.potentialReviewItems.isNotEmpty() + viewBinding.userReviewsActionCourseCount.text = when (state) { is UserReviewsFeature.State.Empty -> context.getString(R.string.user_review_learning_action_empty) diff --git a/app/src/main/java/org/stepik/android/view/learning_actions/ui/adapter/delegate/WishlistActionAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/learning_actions/ui/adapter/delegate/WishlistActionAdapterDelegate.kt index 526c7fd0ab..65551b26f8 100644 --- a/app/src/main/java/org/stepik/android/view/learning_actions/ui/adapter/delegate/WishlistActionAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/learning_actions/ui/adapter/delegate/WishlistActionAdapterDelegate.kt @@ -3,9 +3,9 @@ package org.stepik.android.view.learning_actions.ui.adapter.delegate import android.view.View import android.view.ViewGroup import androidx.core.view.updateLayoutParams -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_learning_action_wishlist.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemLearningActionWishlistBinding import org.stepik.android.presentation.wishlist.WishlistFeature import org.stepik.android.view.learning_actions.model.LearningActionsItem import ru.nobird.android.ui.adapterdelegates.AdapterDelegate @@ -26,19 +26,20 @@ class WishlistActionAdapterDelegate(private val onClick: () -> Unit) : AdapterDe } private class ViewHolder( - override val containerView: View, + root: View, private val onClick: () -> Unit - ) : DelegateViewHolder(containerView), LayoutContainer { + ) : DelegateViewHolder(root) { + private val viewBinding: ItemLearningActionWishlistBinding by viewBinding { ItemLearningActionWishlistBinding.bind(root) } private val viewStateDelegate = ViewStateDelegate() init { viewStateDelegate.addState() - viewStateDelegate.addState(wishlistActionTitle, wishlistActionCourseCount) - viewStateDelegate.addState(wishlistActionTitle, wishlistActionLoadingView) - viewStateDelegate.addState(wishlistActionTitle) - viewStateDelegate.addState(wishlistActionTitle, wishlistActionCourseCount) - containerView.setOnClickListener { onClick() } + viewStateDelegate.addState(viewBinding.wishlistActionTitle, viewBinding.wishlistActionCourseCount) + viewStateDelegate.addState(viewBinding.wishlistActionTitle, viewBinding.wishlistActionLoadingView) + viewStateDelegate.addState(viewBinding.wishlistActionTitle) + viewStateDelegate.addState(viewBinding.wishlistActionTitle, viewBinding.wishlistActionCourseCount) + itemView.setOnClickListener { onClick() } } override fun onBind(data: LearningActionsItem) { @@ -48,7 +49,7 @@ class WishlistActionAdapterDelegate(private val onClick: () -> Unit) : AdapterDe private fun render(state: WishlistFeature.State) { viewStateDelegate.switchState(state) - wishlistActionCourseCount.text = + viewBinding.wishlistActionCourseCount.text = when (state) { is WishlistFeature.State.Empty -> context.getString(R.string.wishlist_empty) diff --git a/app/src/main/java/org/stepik/android/view/learning_actions/ui/fragment/LearningActionsFragment.kt b/app/src/main/java/org/stepik/android/view/learning_actions/ui/fragment/LearningActionsFragment.kt index c4986976c0..312bd96af8 100644 --- a/app/src/main/java/org/stepik/android/view/learning_actions/ui/fragment/LearningActionsFragment.kt +++ b/app/src/main/java/org/stepik/android/view/learning_actions/ui/fragment/LearningActionsFragment.kt @@ -7,9 +7,10 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.fragment_learning_actions.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.base.App +import org.stepic.droid.databinding.FragmentLearningActionsBinding import org.stepic.droid.core.ScreenManager import org.stepik.android.presentation.user_reviews.UserReviewsFeature import org.stepik.android.presentation.user_reviews.UserReviewsViewModel @@ -26,6 +27,7 @@ import ru.nobird.android.view.redux.ui.extension.reduxViewModel import javax.inject.Inject class LearningActionsFragment : Fragment() { + private val learningActionsBinding: FragmentLearningActionsBinding by viewBinding(FragmentLearningActionsBinding::bind) companion object { private const val INDEX_USER_REVIEWS = 0 @@ -100,7 +102,7 @@ class LearningActionsFragment : Fragment() { super.onViewCreated(view, savedInstanceState) learningActionsItemAdapter += WishlistActionAdapterDelegate { screenManager.showWishlist(requireContext()) } learningActionsItemAdapter += UserReviewsActionAdapterDelegate { screenManager.showUserReviews(requireContext()) } - with(learningActionsRecycler) { + with(learningActionsBinding.learningActionsRecycler) { adapter = learningActionsItemAdapter layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) itemAnimator = null diff --git a/app/src/main/java/org/stepik/android/view/lesson/ui/activity/LessonActivity.kt b/app/src/main/java/org/stepik/android/view/lesson/ui/activity/LessonActivity.kt index 372b61f1fe..372eb412c9 100644 --- a/app/src/main/java/org/stepik/android/view/lesson/ui/activity/LessonActivity.kt +++ b/app/src/main/java/org/stepik/android/view/lesson/ui/activity/LessonActivity.kt @@ -17,15 +17,11 @@ import androidx.core.app.ActivityCompat import androidx.core.view.isVisible import androidx.lifecycle.ViewModelProvider import androidx.viewpager.widget.ViewPager +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.snackbar.Snackbar -import kotlinx.android.synthetic.main.activity_lesson.* -import kotlinx.android.synthetic.main.empty_login.* -import kotlinx.android.synthetic.main.error_lesson_is_exam.* -import kotlinx.android.synthetic.main.error_lesson_not_found.* -import kotlinx.android.synthetic.main.error_no_connection_with_button.* -import kotlinx.android.synthetic.main.layout_step_tab_icon.view.* -import kotlinx.android.synthetic.main.view_subtitled_toolbar.* import org.stepic.droid.R +import org.stepic.droid.databinding.ActivityLessonBinding +import org.stepic.droid.databinding.LayoutStepTabIconBinding import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App @@ -73,6 +69,7 @@ class LessonActivity : FragmentActivityBase(), LessonView, RateAppDialog.Companion.Callback, TimeIntervalPickerDialogFragment.Companion.Callback, StreakNotificationDialogFragment.Callback { + private val lessonBinding: ActivityLessonBinding by viewBinding(ActivityLessonBinding::bind) companion object { private const val EXTRA_SECTION = "section" private const val EXTRA_UNIT = "unit" @@ -159,43 +156,43 @@ class LessonActivity : FragmentActivityBase(), LessonView, initCenteredToolbar(R.string.lesson_title, showHomeButton = true) viewStateDelegate = ViewStateDelegate() - viewStateDelegate.addState(lessonPlaceholder) - viewStateDelegate.addState(lessonPlaceholder) - viewStateDelegate.addState(lessonNotFound) - viewStateDelegate.addState(emptyLogin) - viewStateDelegate.addState(errorNoConnection) - viewStateDelegate.addState(lessonPager) + viewStateDelegate.addState(lessonBinding.lessonPlaceholder) + viewStateDelegate.addState(lessonBinding.lessonPlaceholder) + viewStateDelegate.addState(lessonBinding.lessonNotFound.root) + viewStateDelegate.addState(lessonBinding.emptyLogin.root) + viewStateDelegate.addState(lessonBinding.errorNoConnection.root) + viewStateDelegate.addState(lessonBinding.lessonPager) viewStepStateDelegate = ViewStateDelegate() - viewStepStateDelegate.addState(lessonPlaceholder) - viewStepStateDelegate.addState(lessonPlaceholder) - viewStepStateDelegate.addState(errorNoConnection) - viewStepStateDelegate.addState(emptyLesson) - viewStepStateDelegate.addState(lessonNotFound) - viewStepStateDelegate.addState(lessonIsExam) - viewStepStateDelegate.addState(lessonPager, lessonTab) + viewStepStateDelegate.addState(lessonBinding.lessonPlaceholder) + viewStepStateDelegate.addState(lessonBinding.lessonPlaceholder) + viewStepStateDelegate.addState(lessonBinding.errorNoConnection.root) + viewStepStateDelegate.addState(lessonBinding.emptyLesson) + viewStepStateDelegate.addState(lessonBinding.lessonNotFound.root) + viewStepStateDelegate.addState(lessonBinding.lessonIsExam.root) + viewStepStateDelegate.addState(lessonBinding.lessonPager, lessonBinding.lessonTab) - lessonInfoTooltipDelegate = LessonInfoTooltipDelegate(centeredToolbar) + lessonInfoTooltipDelegate = LessonInfoTooltipDelegate(lessonBinding.viewSubtitledToolbar.centeredToolbar) - tryAgain.setOnClickListener { setDataToPresenter(forceUpdate = true) } - goToCatalog.setOnClickListener { screenManager.showCatalog(this); finish() } - authAction.setOnClickListener { screenManager.showLaunchScreen(this) } + lessonBinding.errorNoConnection.tryAgain.setOnClickListener { setDataToPresenter(forceUpdate = true) } + lessonBinding.lessonNotFound.goToCatalog.setOnClickListener { screenManager.showCatalog(this); finish() } + lessonBinding.emptyLogin.authAction.setOnClickListener { screenManager.showLaunchScreen(this) } stepsAdapter = StepFragmentAdapter(supportFragmentManager, stepTypeResolver) - lessonPager.adapter = stepsAdapter - lessonPager.addOnPageChangeListener(FragmentDelegateScrollStateChangeListener(lessonPager, stepsAdapter)) - lessonPager.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() { + lessonBinding.lessonPager.adapter = stepsAdapter + lessonBinding.lessonPager.addOnPageChangeListener(FragmentDelegateScrollStateChangeListener(lessonBinding.lessonPager, stepsAdapter)) + lessonBinding.lessonPager.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() { override fun onPageSelected(position: Int) { currentFocus?.hideKeyboard() lessonPresenter.onStepOpened(position) - centeredToolbarSubtitle.text = getString( + lessonBinding.viewSubtitledToolbar.centeredToolbarSubtitle.text = getString( R.string.lesson_step_counter, position + 1, stepsAdapter.items.size ) invalidateOptionsMenu() } }) - lessonTab.setupWithViewPager(lessonPager, true) + lessonBinding.lessonTab.setupWithViewPager(lessonBinding.lessonPager, true) setDataToPresenter() } @@ -268,7 +265,7 @@ class LessonActivity : FragmentActivityBase(), LessonView, true } R.id.lesson_menu_item_info -> { - lessonPresenter.onShowLessonInfoClicked(lessonPager.currentItem) + lessonPresenter.onShowLessonInfoClicked(lessonBinding.lessonPager.currentItem) true } else -> @@ -282,7 +279,7 @@ class LessonActivity : FragmentActivityBase(), LessonView, val stepNavigationDirection = intent.getIntExtra(EXTRA_MOVE_STEP_NAVIGATION_DIRECTION, -1) when { stepNavigationDirection != -1 -> { - lessonPager.post { (stepsAdapter.activeFragments[lessonPager.currentItem] as? Moveable)?.move(isAutoplayEnabled = true, stepNavigationDirection = StepNavigationDirection.values()[stepNavigationDirection]) } + lessonBinding.lessonPager.post { (stepsAdapter.activeFragments[lessonBinding.lessonPager.currentItem] as? Moveable)?.move(isAutoplayEnabled = true, stepNavigationDirection = StepNavigationDirection.values()[stepNavigationDirection]) } intent.removeExtra(EXTRA_MOVE_STEP_NAVIGATION_DIRECTION) } @@ -300,8 +297,8 @@ class LessonActivity : FragmentActivityBase(), LessonView, is LessonView.State.LessonLoaded -> { viewStepStateDelegate.switchState(state.stepsState) setupToolbarTitle(state.lessonData) - if (centeredToolbarSubtitle.text.isEmpty()) { - centeredToolbarSubtitle.text = getString( + if (lessonBinding.viewSubtitledToolbar.centeredToolbarSubtitle.text.isEmpty()) { + lessonBinding.viewSubtitledToolbar.centeredToolbarSubtitle.text = getString( R.string.lesson_step_counter, state.lessonData.stepPosition + 1, state.lessonData.lesson.steps.size ) @@ -312,21 +309,21 @@ class LessonActivity : FragmentActivityBase(), LessonView, stepsAdapter.items = state.stepsState.stepItems if (intent.getBooleanExtra(EXTRA_AUTOPLAY, false)) { - lessonPager.post { playCurrentStep() } + lessonBinding.lessonPager.post { playCurrentStep() } intent.removeExtra(EXTRA_AUTOPLAY) } val stepNavigationDirectionExtra = intent.getIntExtra(EXTRA_MOVE_STEP_NAVIGATION_DIRECTION, -1) if (stepNavigationDirectionExtra != -1) { - lessonPager.post { - (stepsAdapter.activeFragments[lessonPager.currentItem] as? Moveable) + lessonBinding.lessonPager.post { + (stepsAdapter.activeFragments[lessonBinding.lessonPager.currentItem] as? Moveable) ?.move(isAutoplayEnabled = true, stepNavigationDirection = StepNavigationDirection.values()[stepNavigationDirectionExtra]) } intent.removeExtra(EXTRA_MOVE_STEP_NAVIGATION_DIRECTION) } } else { if (state.stepsState is LessonView.StepsState.Exam) { - errorLessonIsExamAction.setOnClickListener { + lessonBinding.lessonIsExam.errorLessonIsExamAction.setOnClickListener { val url = courseDeepLinkBuilder .createCourseLink(state.stepsState.courseId, CourseScreenTab.SYLLABUS) @@ -338,7 +335,7 @@ class LessonActivity : FragmentActivityBase(), LessonView, stepsAdapter.items = emptyList() } - centeredToolbarSubtitle.isVisible = stepsAdapter.items.isNotEmpty() + lessonBinding.viewSubtitledToolbar.centeredToolbarSubtitle.isVisible = stepsAdapter.items.isNotEmpty() invalidateTabLayout() } else -> Unit @@ -350,12 +347,12 @@ class LessonActivity : FragmentActivityBase(), LessonView, } private fun setupToolbarTitle(lessonData: LessonData) { - centeredToolbarTitle.text = + lessonBinding.viewSubtitledToolbar.centeredToolbarTitle.text = lessonTitleMapper.mapToLessonTitle(this, lessonData) } private fun invalidateTabLayout() { - for (i in 0 until lessonTab.tabCount) { + for (i in 0 until lessonBinding.lessonTab.tabCount) { val tabFrames = stepsAdapter.getTabDrawable(i) val item = stepsAdapter.items[i] @@ -372,19 +369,22 @@ class LessonActivity : FragmentActivityBase(), LessonView, tabFrames.second } - if (lessonTab.getTabAt(i)?.customView == null) { - lessonTab.getTabAt(i)?.customView = View.inflate(this, R.layout.layout_step_tab_icon, null) + if (lessonBinding.lessonTab.getTabAt(i)?.customView == null) { + lessonBinding.lessonTab.getTabAt(i)?.customView = View.inflate(this, R.layout.layout_step_tab_icon, null) } - lessonTab.getTabAt(i)?.customView?.tabIconDrawable?.apply { - setImageResource(tabIconResource) - isEnabled = isPassed + val customView = lessonBinding.lessonTab.getTabAt(i)?.customView + if (customView != null) { + LayoutStepTabIconBinding.bind(customView).tabIconDrawable.apply { + setImageResource(tabIconResource) + isEnabled = isPassed + } } } } override fun showStepAtPosition(position: Int) { - lessonPager.currentItem = position + lessonBinding.lessonPager.currentItem = position lessonPresenter.onStepOpened(position) } @@ -397,24 +397,24 @@ class LessonActivity : FragmentActivityBase(), LessonView, isAutoplayEnabled: Boolean, stepNavigationDirection: StepNavigationDirection ): Boolean { - val itemCount = lessonPager + val itemCount = lessonBinding.lessonPager .adapter ?.count ?: return false return if (stepNavigationDirection == StepNavigationDirection.NEXT) { - val isNotLastItem = lessonPager.currentItem < itemCount - 1 + val isNotLastItem = lessonBinding.lessonPager.currentItem < itemCount - 1 if (isNotLastItem) { - lessonPager.currentItem++ + lessonBinding.lessonPager.currentItem++ if (isAutoplayEnabled) { playCurrentStep() } } isNotLastItem } else { - val isNotFirstItem = lessonPager.currentItem > 0 + val isNotFirstItem = lessonBinding.lessonPager.currentItem > 0 if (isNotFirstItem) { - lessonPager.currentItem-- + lessonBinding.lessonPager.currentItem-- if (isAutoplayEnabled) { playCurrentStep() } @@ -424,7 +424,7 @@ class LessonActivity : FragmentActivityBase(), LessonView, } private fun playCurrentStep() { - (stepsAdapter.activeFragments[lessonPager.currentItem] as? Playable) + (stepsAdapter.activeFragments[lessonBinding.lessonPager.currentItem] as? Playable) ?.play() } @@ -437,7 +437,7 @@ class LessonActivity : FragmentActivityBase(), LessonView, screenManager.openComments(this, discussionThread, step, discussionId, false, isTeacher) } else { analytic.reportEvent(Analytic.Screens.OPEN_COMMENT_NOT_AVAILABLE) - lessonPager.snackbar(messageRes = R.string.comment_disabled) + lessonBinding.lessonPager.snackbar(messageRes = R.string.comment_disabled) } } @@ -501,7 +501,7 @@ class LessonActivity : FragmentActivityBase(), LessonView, override fun onTimeIntervalPicked(chosenInterval: Int) { lessonPresenter.setStreakTime(chosenInterval) analytic.reportEvent(Analytic.Streak.CHOOSE_INTERVAL, chosenInterval.toString()) - lessonPager.snackbar(messageRes = R.string.streak_notification_enabled_successfully, length = Snackbar.LENGTH_LONG) + lessonBinding.lessonPager.snackbar(messageRes = R.string.streak_notification_enabled_successfully, length = Snackbar.LENGTH_LONG) } private fun setupTextFeedback() { @@ -524,7 +524,7 @@ class LessonActivity : FragmentActivityBase(), LessonView, override fun onStreakNotificationDialogCancelled() { analytic.reportEvent(Analytic.Streak.NEGATIVE_MATERIAL_DIALOG) - lessonPager.snackbar(messageRes = R.string.streak_notification_canceled, length = Snackbar.LENGTH_LONG) + lessonBinding.lessonPager.snackbar(messageRes = R.string.streak_notification_canceled, length = Snackbar.LENGTH_LONG) } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { @@ -536,7 +536,7 @@ class LessonActivity : FragmentActivityBase(), LessonView, if (deniedPermissionIndex != -1) { if (!ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[deniedPermissionIndex])) { - lessonPager.snackbar(messageRes = R.string.notification_permission_error) + lessonBinding.lessonPager.snackbar(messageRes = R.string.notification_permission_error) } } else { TimeIntervalPickerDialogFragment diff --git a/app/src/main/java/org/stepik/android/view/lesson/ui/delegate/LessonInfoTooltipDelegate.kt b/app/src/main/java/org/stepik/android/view/lesson/ui/delegate/LessonInfoTooltipDelegate.kt index 5f23b1a0fd..4ee3ca9555 100644 --- a/app/src/main/java/org/stepik/android/view/lesson/ui/delegate/LessonInfoTooltipDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/lesson/ui/delegate/LessonInfoTooltipDelegate.kt @@ -16,8 +16,8 @@ import androidx.core.content.ContextCompat import androidx.core.graphics.drawable.DrawableCompat import androidx.core.view.doOnPreDraw import androidx.core.widget.PopupWindowCompat -import kotlinx.android.synthetic.main.tooltip_lesson_info.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.TooltipLessonInfoBinding import org.stepik.android.view.progress.ui.mapper.ProgressTextMapper class LessonInfoTooltipDelegate( @@ -34,21 +34,21 @@ class LessonInfoTooltipDelegate( .findViewById(R.id.lesson_menu_item_info) ?: return - val popupView = LayoutInflater - .from(anchorView.context) - .inflate(R.layout.tooltip_lesson_info, null) + val popupBinding = TooltipLessonInfoBinding + .inflate(LayoutInflater.from(anchorView.context)) + val popupView = popupBinding.root if (stepScore > 0f) { - popupView + popupBinding .stepWorth .setItem(stepScore, stepCost, R.string.lesson_info_points_with_score, R.string.lesson_info_points_with_score_fraction, R.plurals.points, R.drawable.ic_check_rounded) } else { if (isExam) { - popupView + popupBinding .stepWorth .setItem(R.string.lesson_info_is_exam, R.drawable.ic_check_rounded) } else { - popupView + popupBinding .stepWorth .setItem(stepCost, R.string.lesson_info_points, R.plurals.points, R.drawable.ic_check_rounded) } @@ -61,11 +61,11 @@ class LessonInfoTooltipDelegate( lessonTimeToCompleteInSeconds / 3600 to R.plurals.hours } - popupView + popupBinding .lessonTimeToComplete .setItem(timeValue, R.string.lesson_info_time_to_complete, timeUnitPlural, R.drawable.ic_duration) - popupView + popupBinding .certificateThreshold .setItem(certificateThreshold, R.string.lesson_info_certificate_threshold, R.plurals.points, R.drawable.ic_lesson_info) @@ -79,7 +79,7 @@ class LessonInfoTooltipDelegate( } popupView.doOnPreDraw { - popupView.arrowView?.x = calcArrowHorizontalOffset(anchorView, popupView, popupView.arrowView) + popupBinding.arrowView?.x = calcArrowHorizontalOffset(anchorView, popupView, popupBinding.arrowView) } anchorView.post { @@ -142,4 +142,4 @@ class LessonInfoTooltipDelegate( popupView.getLocationOnScreen(pos) return anchorOffset.toFloat() - pos[0] - arrowView.measuredWidth / 2 } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/lesson_demo/ui/dialog/LessonDemoCompleteBottomSheetDialogFragment.kt b/app/src/main/java/org/stepik/android/view/lesson_demo/ui/dialog/LessonDemoCompleteBottomSheetDialogFragment.kt index 309068027a..6bdecdf54f 100644 --- a/app/src/main/java/org/stepik/android/view/lesson_demo/ui/dialog/LessonDemoCompleteBottomSheetDialogFragment.kt +++ b/app/src/main/java/org/stepik/android/view/lesson_demo/ui/dialog/LessonDemoCompleteBottomSheetDialogFragment.kt @@ -6,17 +6,17 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.DialogFragment import androidx.lifecycle.ViewModelProvider +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.firebase.remoteconfig.FirebaseRemoteConfig import com.google.firebase.remoteconfig.ktx.get -import kotlinx.android.synthetic.main.bottom_sheet_dialog_lesson_demo_complete.* -import kotlinx.android.synthetic.main.error_no_connection_with_button_small.* import org.stepic.droid.R import org.stepic.droid.base.App import org.stepic.droid.configuration.RemoteConfig import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.BottomSheetDialogLessonDemoCompleteBinding import org.stepik.android.domain.course.analytic.CourseViewSource import org.stepik.android.domain.course_payments.model.DeeplinkPromoCode import org.stepik.android.domain.course_payments.model.DefaultPromoCode @@ -69,6 +69,8 @@ class LessonDemoCompleteBottomSheetDialogFragment : @Inject internal lateinit var firebaseRemoteConfig: FirebaseRemoteConfig + private val binding: BottomSheetDialogLessonDemoCompleteBinding by viewBinding(BottomSheetDialogLessonDemoCompleteBinding::bind) + private val lessonDemoViewModel: LessonDemoViewModel by reduxViewModel(this) { viewModelFactory } private val viewStateDelegate = ViewStateDelegate() private lateinit var wishlistViewDelegate: WishlistViewDelegate @@ -96,19 +98,19 @@ class LessonDemoCompleteBottomSheetDialogFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initViewStateDelegate() - wishlistViewDelegate = WishlistViewDelegate(demoWishlistAction) - demoCompleteTitle.text = getString(R.string.demo_complete_title, course.title) - demoCompleteAction.setOnClickListener { + wishlistViewDelegate = WishlistViewDelegate(binding.demoWishlistAction) + binding.demoCompleteTitle.text = getString(R.string.demo_complete_title, course.title) + binding.demoCompleteAction.setOnClickListener { lessonDemoViewModel.onNewMessage(LessonDemoFeature.Message.BuyActionMessage) } - demoWishlistAction.setOnClickListener { + binding.demoWishlistAction.setOnClickListener { lessonDemoViewModel.onNewMessage( LessonDemoFeature.Message.WishlistMessage( WishlistOperationFeature.Message.WishlistAddMessage(course, CourseViewSource.LessonDemoDialog) ) ) } - tryAgain.setOnClickListener { lessonDemoViewModel.onNewMessage(LessonDemoFeature.Message.InitMessage(forceUpdate = true)) } + binding.demoCompleteNetworkError.tryAgain.setOnClickListener { lessonDemoViewModel.onNewMessage(LessonDemoFeature.Message.InitMessage(forceUpdate = true)) } } override fun onAction(action: LessonDemoFeature.Action.ViewAction) { @@ -157,10 +159,10 @@ class LessonDemoCompleteBottomSheetDialogFragment : private fun initViewStateDelegate() { viewStateDelegate.addState() - viewStateDelegate.addState(demoCompleteProgressbar) - viewStateDelegate.addState(demoCompleteNetworkError) - viewStateDelegate.addState(demoCompleteContent, demoCompleteTitle, demoPurchaseUnavailable, demoWishlistAction) - viewStateDelegate.addState(demoCompleteContent, demoCompleteTitle, demoCompleteInfo, demoCompleteDivider, demoCompleteAction, demoWishlistAction) + viewStateDelegate.addState(binding.demoCompleteProgressbar) + viewStateDelegate.addState(binding.demoCompleteNetworkError.root) + viewStateDelegate.addState(binding.demoCompleteContent, binding.demoCompleteTitle, binding.demoPurchaseUnavailable, binding.demoWishlistAction) + viewStateDelegate.addState(binding.demoCompleteContent, binding.demoCompleteTitle, binding.demoCompleteInfo, binding.demoCompleteDivider.root, binding.demoCompleteAction, binding.demoWishlistAction) } private fun setupWeb(deeplinkPromoCode: DeeplinkPromoCode) { @@ -175,7 +177,7 @@ class LessonDemoCompleteBottomSheetDialogFragment : ), course ) - demoCompleteAction.text = + binding.demoCompleteAction.text = if (courseDisplayPrice != null) { if (hasPromo) { displayPriceMapper.mapToDiscountedDisplayPriceSpannedString(courseDisplayPrice, promoPrice, currencyCode) @@ -189,7 +191,7 @@ class LessonDemoCompleteBottomSheetDialogFragment : private fun setupIAP(coursePurchaseData: CoursePurchaseData) { val courseDisplayPrice = coursePurchaseData.course.displayPrice - demoCompleteAction.text = + binding.demoCompleteAction.text = if (courseDisplayPrice != null) { if (coursePurchaseData.promoCodeSku.lightSku != null) { displayPriceMapper.mapToDiscountedDisplayPriceSpannedString(coursePurchaseData.primarySku.price, coursePurchaseData.promoCodeSku.lightSku.price) @@ -204,4 +206,4 @@ class LessonDemoCompleteBottomSheetDialogFragment : override fun continueLearning() { screenManager.showCourseAfterPurchase(requireContext(), course, CourseViewSource.LessonDemoDialog, CourseScreenTab.INFO) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/onboarding/model/OnboardingCourseList.kt b/app/src/main/java/org/stepik/android/view/onboarding/model/OnboardingCourseList.kt index b2e8e2a90d..2a71005dde 100644 --- a/app/src/main/java/org/stepik/android/view/onboarding/model/OnboardingCourseList.kt +++ b/app/src/main/java/org/stepik/android/view/onboarding/model/OnboardingCourseList.kt @@ -2,7 +2,7 @@ package org.stepik.android.view.onboarding.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Parcelize data class OnboardingCourseList( diff --git a/app/src/main/java/org/stepik/android/view/onboarding/model/OnboardingGoal.kt b/app/src/main/java/org/stepik/android/view/onboarding/model/OnboardingGoal.kt index de11eff418..4ee2f55f5d 100644 --- a/app/src/main/java/org/stepik/android/view/onboarding/model/OnboardingGoal.kt +++ b/app/src/main/java/org/stepik/android/view/onboarding/model/OnboardingGoal.kt @@ -2,7 +2,7 @@ package org.stepik.android.view.onboarding.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Parcelize data class OnboardingGoal( diff --git a/app/src/main/java/org/stepik/android/view/onboarding/ui/activity/OnboardingCourseListsActivity.kt b/app/src/main/java/org/stepik/android/view/onboarding/ui/activity/OnboardingCourseListsActivity.kt index 54cd07b8a1..9aecd07263 100644 --- a/app/src/main/java/org/stepik/android/view/onboarding/ui/activity/OnboardingCourseListsActivity.kt +++ b/app/src/main/java/org/stepik/android/view/onboarding/ui/activity/OnboardingCourseListsActivity.kt @@ -6,12 +6,13 @@ import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.activity_onboarding_course_lists.* -import kotlinx.android.synthetic.main.item_onboarding.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.ActivityOnboardingCourseListsBinding +import org.stepic.droid.databinding.ItemOnboardingBinding import org.stepic.droid.preferences.SharedPreferenceHelper import org.stepic.droid.ui.activities.MainFeedActivity import org.stepik.android.domain.onboarding.analytic.OnboardingOpenedAnalyticEvent @@ -48,6 +49,8 @@ class OnboardingCourseListsActivity : AppCompatActivity(R.layout.activity_onboar @Inject internal lateinit var sharedPreferenceHelper: SharedPreferenceHelper + private val binding: ActivityOnboardingCourseListsBinding by viewBinding(ActivityOnboardingCourseListsBinding::bind) + private val courseListsAdapter: DefaultDelegateAdapter = DefaultDelegateAdapter() override fun onCreate(savedInstanceState: Bundle?) { @@ -55,7 +58,7 @@ class OnboardingCourseListsActivity : AppCompatActivity(R.layout.activity_onboar App.component().inject(this) analytic.report(OnboardingOpenedAnalyticEvent(screen = 2)) - courseListsHeader.text = onboardingGoal.title + binding.courseListsHeader.text = onboardingGoal.title courseListsAdapter += createCourseListsAdapterDelegate { onboardingCourseList -> sharedPreferenceHelper.personalizedCourseList = onboardingCourseList.id @@ -64,14 +67,14 @@ class OnboardingCourseListsActivity : AppCompatActivity(R.layout.activity_onboar closeOnboarding() } courseListsAdapter.items = onboardingGoal.courseLists - courseListsRecycler.layoutManager = LinearLayoutManager(this) - courseListsRecycler.adapter = courseListsAdapter + binding.courseListsRecycler.layoutManager = LinearLayoutManager(this) + binding.courseListsRecycler.adapter = courseListsAdapter - backAction.setOnClickListener { + binding.backAction.setOnClickListener { onBackPressed() } - dismissButton.setOnClickListener { + binding.dismissButton.setOnClickListener { analytic.report(OnboardingClosedAnalyticEvent(screen = 2)) closeOnboarding() } @@ -84,9 +87,10 @@ class OnboardingCourseListsActivity : AppCompatActivity(R.layout.activity_onboar private fun createCourseListsAdapterDelegate(onItemClicked: (OnboardingCourseList) -> Unit) = adapterDelegate(layoutResId = R.layout.item_onboarding) { - val itemContainer = itemView.itemContainer - val itemIcon = itemView.itemIcon - val itemTitle = itemView.itemTitle + val itemBinding = ItemOnboardingBinding.bind(itemView) + val itemContainer = itemBinding.itemContainer + val itemIcon = itemBinding.itemIcon + val itemTitle = itemBinding.itemTitle itemView.setOnClickListener { item?.let(onItemClicked) } @@ -111,4 +115,4 @@ class OnboardingCourseListsActivity : AppCompatActivity(R.layout.activity_onboar } finish() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/onboarding/ui/activity/OnboardingGoalActivity.kt b/app/src/main/java/org/stepik/android/view/onboarding/ui/activity/OnboardingGoalActivity.kt index a75fcb9fc2..c48b5c7793 100644 --- a/app/src/main/java/org/stepik/android/view/onboarding/ui/activity/OnboardingGoalActivity.kt +++ b/app/src/main/java/org/stepik/android/view/onboarding/ui/activity/OnboardingGoalActivity.kt @@ -6,13 +6,13 @@ import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.activity_onboarding_goal.* -import kotlinx.android.synthetic.main.item_onboarding.* -import kotlinx.android.synthetic.main.item_onboarding.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.ActivityOnboardingGoalBinding +import org.stepic.droid.databinding.ItemOnboardingBinding import org.stepic.droid.preferences.SharedPreferenceHelper import org.stepic.droid.ui.activities.MainFeedActivity import org.stepik.android.domain.onboarding.analytic.OnboardingAllCoursesAnalyticEvent @@ -48,6 +48,8 @@ class OnboardingGoalActivity : AppCompatActivity(R.layout.activity_onboarding_go @Inject internal lateinit var onboardingRemoteConfigResolver: OnboardingRemoteConfigResolver + private val binding: ActivityOnboardingGoalBinding by viewBinding(ActivityOnboardingGoalBinding::bind) + private val onboardingGoalsAdapter: DefaultDelegateAdapter = DefaultDelegateAdapter() override fun onCreate(savedInstanceState: Bundle?) { @@ -63,18 +65,18 @@ class OnboardingGoalActivity : AppCompatActivity(R.layout.activity_onboarding_go val items = onboardingRemoteConfigResolver.buildOnboardingGoals() onboardingGoalsAdapter.items = items - goalRecycler.layoutManager = LinearLayoutManager(this) - goalRecycler.adapter = onboardingGoalsAdapter + binding.goalRecycler.layoutManager = LinearLayoutManager(this) + binding.goalRecycler.adapter = onboardingGoalsAdapter val (icon, title) = getString(R.string.onboarding_goal_all_courses).split(' ', limit = 2) - itemIcon.text = icon - itemTitle.text = title + binding.allCoursesAction.itemIcon.text = icon + binding.allCoursesAction.itemTitle.text = title - dismissButton.setOnClickListener { + binding.dismissButton.setOnClickListener { analytic.report(OnboardingClosedAnalyticEvent(screen = 1)) onBackPressed() } - allCoursesAction.setOnClickListener { + binding.allCoursesAction.root.setOnClickListener { analytic.report(OnboardingAllCoursesAnalyticEvent) onBackPressed() } @@ -94,8 +96,9 @@ class OnboardingGoalActivity : AppCompatActivity(R.layout.activity_onboarding_go private fun createGoalsAdapterDelegate(onItemClicked: (OnboardingGoal) -> Unit) = adapterDelegate(layoutResId = R.layout.item_onboarding) { - val itemIcon = itemView.itemIcon - val itemTitle = itemView.itemTitle + val itemBinding = ItemOnboardingBinding.bind(itemView) + val itemIcon = itemBinding.itemIcon + val itemTitle = itemBinding.itemTitle itemView.setOnClickListener { item?.let(onItemClicked) } @@ -106,4 +109,4 @@ class OnboardingGoalActivity : AppCompatActivity(R.layout.activity_onboarding_go itemIcon.setBackgroundResource(backgroundRes) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/personal_deadlines/ui/adapters/EditDeadlinesAdapter.kt b/app/src/main/java/org/stepik/android/view/personal_deadlines/ui/adapters/EditDeadlinesAdapter.kt index 21becdf399..1f5501d442 100644 --- a/app/src/main/java/org/stepik/android/view/personal_deadlines/ui/adapters/EditDeadlinesAdapter.kt +++ b/app/src/main/java/org/stepik/android/view/personal_deadlines/ui/adapters/EditDeadlinesAdapter.kt @@ -2,11 +2,11 @@ package org.stepik.android.view.personal_deadlines.ui.adapters import android.view.View import android.view.ViewGroup -import android.widget.TextView import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.view_edit_deadlines_item.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ViewEditDeadlinesItemBinding import org.stepic.droid.util.DateTimeHelper import org.stepik.android.domain.personal_deadlines.model.Deadline import org.stepik.android.model.Section @@ -27,16 +27,16 @@ class EditDeadlinesAdapter( EditDeadlinesViewHolder(parent.inflate(R.layout.view_edit_deadlines_item)) override fun onBindViewHolder(holder: EditDeadlinesViewHolder, position: Int) { - holder.sectionTitle.text = holder.itemView.context.getString(R.string.section_title_with_number, + holder.viewBinding.sectionTitle.text = holder.itemView.context.getString(R.string.section_title_with_number, position + 1, sections[position].title) val deadline = getDeadlineForPositionOrNull(position) if (deadline != null) { - holder.deadline.text = holder.itemView.context.getString(R.string.deadlines_section, + holder.viewBinding.deadline.text = holder.itemView.context.getString(R.string.deadlines_section, DateTimeHelper.getPrintableDate(deadline.deadline, DateTimeHelper.DISPLAY_DATETIME_PATTERN, TimeZone.getDefault())) - holder.deadline.isVisible = true + holder.viewBinding.deadline.isVisible = true } else { - holder.deadline.isVisible = false + holder.viewBinding.deadline.isVisible = false } } @@ -66,11 +66,10 @@ class EditDeadlinesAdapter( } inner class EditDeadlinesViewHolder(view: View) : RecyclerView.ViewHolder(view) { - internal val sectionTitle: TextView = view.sectionTitle - internal val deadline: TextView = view.deadline + val viewBinding: ViewEditDeadlinesItemBinding by viewBinding { ViewEditDeadlinesItemBinding.bind(view) } init { view.setOnClickListener { onItemClicked(adapterPosition) } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/personal_deadlines/ui/adapters/LearningRateAdapter.kt b/app/src/main/java/org/stepik/android/view/personal_deadlines/ui/adapters/LearningRateAdapter.kt index 5177fea5f1..1c81efb061 100644 --- a/app/src/main/java/org/stepik/android/view/personal_deadlines/ui/adapters/LearningRateAdapter.kt +++ b/app/src/main/java/org/stepik/android/view/personal_deadlines/ui/adapters/LearningRateAdapter.kt @@ -2,11 +2,10 @@ package org.stepik.android.view.personal_deadlines.ui.adapters import android.view.View import android.view.ViewGroup -import android.widget.ImageView -import android.widget.TextView import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.view_learning_rate.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ViewLearningRateBinding import org.stepic.droid.util.AppConstants import org.stepik.android.domain.personal_deadlines.model.LearningRate import ru.nobird.android.view.base.ui.extension.inflate @@ -23,11 +22,11 @@ class LearningRateAdapter( override fun onBindViewHolder(holder: LearningRateViewHolder, position: Int) { val rate = rates[position] - holder.title.setText(rate.title) - holder.icon.setImageResource(rate.icon) + holder.viewBinding.title.setText(rate.title) + holder.viewBinding.icon.setImageResource(rate.icon) val hours = rate.millisPerWeek / AppConstants.MILLIS_IN_1HOUR - holder.rate.text = hours.toString() + holder.viewBinding.rate.text = hours.toString() } private fun onItemClicked(position: Int) { @@ -35,12 +34,10 @@ class LearningRateAdapter( } inner class LearningRateViewHolder(view: View) : RecyclerView.ViewHolder(view) { - internal val title: TextView = view.title - internal val icon: ImageView = view.icon - internal val rate: TextView = view.rate + val viewBinding: ViewLearningRateBinding by viewBinding { ViewLearningRateBinding.bind(view) } init { view.setOnClickListener { onItemClicked(adapterPosition) } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/profile/ui/animation/ProfileHeaderAnimationDelegate.kt b/app/src/main/java/org/stepik/android/view/profile/ui/animation/ProfileHeaderAnimationDelegate.kt index b22ae430ed..d87f106108 100644 --- a/app/src/main/java/org/stepik/android/view/profile/ui/animation/ProfileHeaderAnimationDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/profile/ui/animation/ProfileHeaderAnimationDelegate.kt @@ -3,15 +3,19 @@ package org.stepik.android.view.profile.ui.animation import android.animation.ArgbEvaluator import android.content.res.ColorStateList import android.view.View +import android.widget.TextView import androidx.annotation.ColorInt import androidx.core.view.ViewCompat import androidx.core.view.isVisible -import kotlinx.android.synthetic.main.fragment_profile.view.* -import kotlinx.android.synthetic.main.header_profile.view.* +import com.google.android.material.appbar.AppBarLayout +import org.stepic.droid.databinding.HeaderProfileBinding import org.stepik.android.view.base.ui.extension.ColorExtensions class ProfileHeaderAnimationDelegate( - view: View, + private val headerBinding: HeaderProfileBinding, + private val appbar: AppBarLayout, + private val toolbarTitle: TextView, + private val toolbarSeparator: View, @ColorInt private val menuColorStart: Int, @ColorInt @@ -21,21 +25,12 @@ class ProfileHeaderAnimationDelegate( private val onMenuColorChanged: (ColorStateList) -> Unit ) { - private val profileCover = view.profileCover - private val profileImage = view.profileImage - - private val toolbarTitle = view.toolbarTitle - private val toolbarSeparator = view.toolbarSeparator - - private val appbar = view.appbar - private val header = view.header - private val argbEvaluator = ArgbEvaluator() fun onScroll(scrollY: Int) { - val coverHeight = profileCover.height + val coverHeight = headerBinding.profileCover.height val toolbarHeight = appbar.height - val headerHeight = header.height + val headerHeight = headerBinding.root.height val coverScrollPercent = ((scrollY + 1f) / (coverHeight - toolbarHeight).coerceAtLeast(1)) .coerceIn(0f, 1f) @@ -46,12 +41,12 @@ class ProfileHeaderAnimationDelegate( val menuColor = argbEvaluator.evaluate(coverScrollPercent, menuColorStart, menuColorEnd) as Int onMenuColorChanged(ColorStateList.valueOf(menuColor)) - ViewCompat.setElevation(appbar, if (scrollY > headerHeight - toolbarHeight) ViewCompat.getElevation(header) else 0f) + ViewCompat.setElevation(appbar, if (scrollY > headerHeight - toolbarHeight) ViewCompat.getElevation(headerBinding.root) else 0f) - val scroll = (scrollY - profileImage.top).coerceAtMost(0).toFloat() + val scroll = (scrollY - headerBinding.profileImage.top).coerceAtMost(0).toFloat() toolbarTitle.translationY = -scroll - val separatorBound = coverHeight.takeIf { it > 0 } ?: profileImage.top + 1 + val separatorBound = coverHeight.takeIf { it > 0 } ?: (headerBinding.profileImage.top + 1) toolbarSeparator.isVisible = (scrollY + toolbarHeight) in separatorBound until headerHeight } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/profile/ui/delegate/ProfileStatsDelegate.kt b/app/src/main/java/org/stepik/android/view/profile/ui/delegate/ProfileStatsDelegate.kt index dcd9a366d7..b720ec212e 100644 --- a/app/src/main/java/org/stepik/android/view/profile/ui/delegate/ProfileStatsDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/profile/ui/delegate/ProfileStatsDelegate.kt @@ -2,21 +2,20 @@ package org.stepik.android.view.profile.ui.delegate import android.text.Spannable import android.text.SpannableString -import android.view.View import androidx.core.content.res.ResourcesCompat import androidx.core.text.bold import androidx.core.text.buildSpannedString import androidx.core.view.isVisible -import kotlinx.android.synthetic.main.header_profile.view.* import org.stepic.droid.R import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.analytic.Analytic +import org.stepic.droid.databinding.HeaderProfileBinding import org.stepic.droid.ui.util.hideAllChildren import org.stepik.android.model.user.User import org.stepik.android.view.base.ui.span.TypefaceSpanCompat class ProfileStatsDelegate( - view: View, + headerBinding: HeaderProfileBinding, private val analytic: Analytic ) { companion object { @@ -24,15 +23,15 @@ class ProfileStatsDelegate( private const val MIN_KNOWLEDGE = 10 } - private val context = view.context + private val context = headerBinding.root.context private val resources = context.resources - private val profileStats = view.profileStats + private val profileStats = headerBinding.profileStats - private val profileCertificatesIssued = view.profileCertificatesIssued - private val profileCoursesPublished = view.profileCoursesPublished - private val profileKnowledgeRank = view.profileKnowledgeRank - private val profileReputationRank = view.profileReputationRank + private val profileCertificatesIssued = headerBinding.profileCertificatesIssued + private val profileCoursesPublished = headerBinding.profileCoursesPublished + private val profileKnowledgeRank = headerBinding.profileKnowledgeRank + private val profileReputationRank = headerBinding.profileReputationRank init { profileKnowledgeRank.setOnClickListener { @@ -88,4 +87,4 @@ class ProfileStatsDelegate( } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/profile/ui/fragment/ProfileFragment.kt b/app/src/main/java/org/stepik/android/view/profile/ui/fragment/ProfileFragment.kt index 6ee40a14b5..4bfd80a924 100644 --- a/app/src/main/java/org/stepik/android/view/profile/ui/fragment/ProfileFragment.kt +++ b/app/src/main/java/org/stepik/android/view/profile/ui/fragment/ProfileFragment.kt @@ -16,17 +16,15 @@ import androidx.core.widget.NestedScrollView import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider +import by.kirich1409.viewbindingdelegate.viewBinding import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.empty_login.* -import kotlinx.android.synthetic.main.error_no_connection_with_button.* -import kotlinx.android.synthetic.main.fragment_profile.* -import kotlinx.android.synthetic.main.header_profile.* import org.stepic.droid.R import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager import org.stepic.droid.core.ShareHelper +import org.stepic.droid.databinding.FragmentProfileBinding import org.stepic.droid.ui.activities.MainFeedActivity import org.stepic.droid.ui.activities.contracts.CloseButtonInToolbar import org.stepic.droid.ui.util.snackbar @@ -85,6 +83,8 @@ class ProfileFragment : Fragment(R.layout.fragment_profile), ProfileView { private lateinit var profileComponent: ProfileComponent private val profilePresenter: ProfilePresenter by viewModels { viewModelFactory } + private val profileBinding: FragmentProfileBinding by viewBinding(FragmentProfileBinding::bind) + private lateinit var viewStateDelegate: ViewStateDelegate private lateinit var profileStatsDelegate: ProfileStatsDelegate @@ -117,7 +117,9 @@ class ProfileFragment : Fragment(R.layout.fragment_profile), ProfileView { set(value) { field = value - toolbar?.navigationIcon?.let { DrawableCompat.setTintList(it, value) } + view?.let { + profileBinding.toolbar.navigationIcon?.let { icon -> DrawableCompat.setTintList(icon, value) } + } editMenuItem?.let { MenuItemCompat.setIconTintList(it, value) } shareMenuItem?.let { MenuItemCompat.setIconTintList(it, value) } settingsMenuItem?.let { MenuItemCompat.setIconTintList(it, value) } @@ -140,46 +142,49 @@ class ProfileFragment : Fragment(R.layout.fragment_profile), ProfileView { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { viewStateDelegate = ViewStateDelegate() viewStateDelegate.addState() - viewStateDelegate.addState(profileLoading) - viewStateDelegate.addState(scrollContainer) - viewStateDelegate.addState(profileEmpty) - viewStateDelegate.addState(profileEmptyLogin) - viewStateDelegate.addState(profileNetworkError) + viewStateDelegate.addState(profileBinding.profileLoading) + viewStateDelegate.addState(profileBinding.scrollContainer) + viewStateDelegate.addState(profileBinding.profileEmpty) + viewStateDelegate.addState(profileBinding.profileEmptyLogin.root) + viewStateDelegate.addState(profileBinding.profileNetworkError.root) - profileStatsDelegate = ProfileStatsDelegate(view, analytic) + profileStatsDelegate = ProfileStatsDelegate(profileBinding.header, analytic) if (activity is CloseButtonInToolbar) { - toolbar.setNavigationIcon(com.google.android.material.R.drawable.abc_ic_ab_back_material) - toolbar.navigationIcon?.let { DrawableCompat.setTintList(it, menuTintStateList) } - toolbar.setNavigationOnClickListener { activity?.onBackPressed() } + profileBinding.toolbar.setNavigationIcon(com.google.android.material.R.drawable.abc_ic_ab_back_material) + profileBinding.toolbar.navigationIcon?.let { DrawableCompat.setTintList(it, menuTintStateList) } + profileBinding.toolbar.setNavigationOnClickListener { activity?.onBackPressed() } } - ViewCompat.setElevation(header, resources.getDimension(R.dimen.profile_header_elevation)) + ViewCompat.setElevation(profileBinding.header.root, resources.getDimension(R.dimen.profile_header_elevation)) - toolbar.inflateMenu(R.menu.profile_menu) - initOptionsMenu(toolbar.menu) - toolbar.setOnMenuItemClickListener(::onOptionsItemClicked) + profileBinding.toolbar.inflateMenu(R.menu.profile_menu) + initOptionsMenu(profileBinding.toolbar.menu) + profileBinding.toolbar.setOnMenuItemClickListener(::onOptionsItemClicked) val colorControlNormal = AppCompatResources.getColorStateList(requireContext(), requireContext().resolveResourceIdAttribute(R.attr.colorControlNormal)) headerAnimationDelegate = ProfileHeaderAnimationDelegate( - view, + headerBinding = profileBinding.header, + appbar = profileBinding.appbar, + toolbarTitle = profileBinding.toolbarTitle, + toolbarSeparator = profileBinding.toolbarSeparator.root, menuColorStart = ContextCompat.getColor(requireContext(), R.color.white), menuColorEnd = colorControlNormal?.defaultColor ?: 0x0, toolbarColor = ColorExtensions.colorSurfaceWithElevationOverlay(requireContext(), 4) ) { menuTintStateList = it } - scrollContainer + profileBinding.scrollContainer .setOnScrollChangeListener { _: NestedScrollView, _: Int, scrollY: Int, _: Int, _: Int -> headerAnimationDelegate.onScroll(scrollY) } - view.doOnNextLayout { headerAnimationDelegate.onScroll(scrollContainer.scrollY) } + view.doOnNextLayout { headerAnimationDelegate.onScroll(profileBinding.scrollContainer.scrollY) } - tryAgain.setOnClickListener { profilePresenter.onData(userId, forceUpdate = true) } - authAction.setOnClickListener { screenManager.showLaunchScreen(context, true, MainFeedActivity.PROFILE_INDEX) } + profileBinding.profileNetworkError.tryAgain.setOnClickListener { profilePresenter.onData(userId, forceUpdate = true) } + profileBinding.profileEmptyLogin.authAction.setOnClickListener { screenManager.showLaunchScreen(context, true, MainFeedActivity.PROFILE_INDEX) } - profileImageWrapper = profileImage.wrapWithGlide() + profileImageWrapper = profileBinding.header.profileImage.wrapWithGlide() if (savedInstanceState == null) { childFragmentManager.commitNow { @@ -258,12 +263,12 @@ class ProfileFragment : Fragment(R.layout.fragment_profile), ProfileView { AppCompatResources.getDrawable(requireContext(), R.drawable.general_placeholder) ) - profileName.text = user.fullName - profileBio.text = user.shortBio - profileBio.isVisible = !user.shortBio.isNullOrBlank() + profileBinding.header.profileName.text = user.fullName + profileBinding.header.profileBio.text = user.shortBio + profileBinding.header.profileBio.isVisible = !user.shortBio.isNullOrBlank() - toolbarTitle.text = user.fullName - toolbarTitle.translationY = 1000f + profileBinding.toolbarTitle.text = user.fullName + profileBinding.toolbarTitle.translationY = 1000f isEditMenuItemVisible = isCurrentUser isShareMenuItemVisible = true @@ -271,21 +276,21 @@ class ProfileFragment : Fragment(R.layout.fragment_profile), ProfileView { profileStatsDelegate.setProfileStats(user) - profileCover.isVisible = !user.cover.isNullOrEmpty() + profileBinding.header.profileCover.isVisible = !user.cover.isNullOrEmpty() Glide .with(requireContext()) .asBitmap() .centerCrop() .load(user.cover) - .into(profileCover) + .into(profileBinding.header.profileCover) - view?.doOnNextLayout { headerAnimationDelegate.onScroll(scrollContainer.scrollY) } + view?.doOnNextLayout { headerAnimationDelegate.onScroll(profileBinding.scrollContainer.scrollY) } } } else -> { - toolbarTitle.setText(R.string.profile_title) - toolbarTitle.translationY = 0f + profileBinding.toolbarTitle.setText(R.string.profile_title) + profileBinding.toolbarTitle.translationY = 0f isEditMenuItemVisible = false isShareMenuItemVisible = false @@ -301,4 +306,4 @@ class ProfileFragment : Fragment(R.layout.fragment_profile), ProfileView { override fun shareUser(user: User) { startActivity(shareHelper.getIntentForUserSharing(user)) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/profile_achievements/ui/fragment/ProfileAchievementsFragment.kt b/app/src/main/java/org/stepik/android/view/profile_achievements/ui/fragment/ProfileAchievementsFragment.kt index 239a4c87cd..e7e60ae1cb 100644 --- a/app/src/main/java/org/stepik/android/view/profile_achievements/ui/fragment/ProfileAchievementsFragment.kt +++ b/app/src/main/java/org/stepik/android/view/profile_achievements/ui/fragment/ProfileAchievementsFragment.kt @@ -7,13 +7,13 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.GridLayoutManager -import kotlinx.android.synthetic.main.error_no_connection_with_button_small.* -import kotlinx.android.synthetic.main.fragment_profile_achievements.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.FragmentProfileAchievementsBinding import org.stepik.android.domain.achievement.model.AchievementItem import org.stepik.android.presentation.achievement.AchievementsView import org.stepik.android.presentation.profile_achievements.ProfileAchievementsPresenter @@ -51,6 +51,7 @@ class ProfileAchievementsFragment : Fragment(R.layout.fragment_profile_achieveme private var userId: Long by argument() + private val profileAchievementsBinding: FragmentProfileAchievementsBinding by viewBinding(FragmentProfileAchievementsBinding::bind) private val achievementsPresenter: ProfileAchievementsPresenter by viewModels { viewModelFactory } private lateinit var viewStateDelegate: ViewStateDelegate private lateinit var achievementsAdapter: DefaultDelegateAdapter @@ -82,17 +83,17 @@ class ProfileAchievementsFragment : Fragment(R.layout.fragment_profile_achieveme viewStateDelegate = ViewStateDelegate() viewStateDelegate.addState() viewStateDelegate.addState() - viewStateDelegate.addState(view, achievementsLoadingPlaceholder) - viewStateDelegate.addState(view, achievementsLoadingError) - viewStateDelegate.addState(view, achievementsTilesContainer) + viewStateDelegate.addState(view, profileAchievementsBinding.achievementsLoadingPlaceholder) + viewStateDelegate.addState(view, profileAchievementsBinding.achievementsLoadingError.root) + viewStateDelegate.addState(view, profileAchievementsBinding.achievementsTilesContainer) viewStateDelegate.addState() - tryAgain.setOnClickListener { setDataToPresenter(forceUpdate = true) } - achievementsTitle.setOnClickListener { screenManager.showAchievementsList(requireContext(), profileId, isMyProfile) } + profileAchievementsBinding.achievementsLoadingError.tryAgain.setOnClickListener { setDataToPresenter(forceUpdate = true) } + profileAchievementsBinding.achievementsTitle.setOnClickListener { screenManager.showAchievementsList(requireContext(), profileId, isMyProfile) } - achievementsTilesContainer.layoutManager = GridLayoutManager(context, achievementsToDisplay) - achievementsTilesContainer.isNestedScrollingEnabled = false - achievementsTilesContainer.adapter = achievementsAdapter + profileAchievementsBinding.achievementsTilesContainer.layoutManager = GridLayoutManager(context, achievementsToDisplay) + profileAchievementsBinding.achievementsTilesContainer.isNestedScrollingEnabled = false + profileAchievementsBinding.achievementsTilesContainer.adapter = achievementsAdapter initAchievementsPlaceholders() setDataToPresenter() @@ -104,12 +105,12 @@ class ProfileAchievementsFragment : Fragment(R.layout.fragment_profile_achieveme private fun initAchievementsPlaceholders() { for (i in 0 until achievementsToDisplay) { - val view = layoutInflater.inflate(R.layout.view_achievement_tile_placeholder, achievementsLoadingPlaceholder, false) + val view = layoutInflater.inflate(R.layout.view_achievement_tile_placeholder, profileAchievementsBinding.achievementsLoadingPlaceholder, false) view.layoutParams = (view.layoutParams as LinearLayout.LayoutParams).apply { weight = 1f width = 0 } - achievementsLoadingPlaceholder.addView(view) + profileAchievementsBinding.achievementsLoadingPlaceholder.addView(view) } } @@ -160,4 +161,4 @@ class ProfileAchievementsFragment : Fragment(R.layout.fragment_profile_achieveme achievementsPresenter.onSaveInstanceState(outState) super.onSaveInstanceState(outState) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/profile_activities/ui/fragment/ProfileActivitiesFragment.kt b/app/src/main/java/org/stepik/android/view/profile_activities/ui/fragment/ProfileActivitiesFragment.kt index 8304ce58f7..38f3149423 100644 --- a/app/src/main/java/org/stepik/android/view/profile_activities/ui/fragment/ProfileActivitiesFragment.kt +++ b/app/src/main/java/org/stepik/android/view/profile_activities/ui/fragment/ProfileActivitiesFragment.kt @@ -10,10 +10,10 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import ru.nobird.android.view.base.ui.extension.argument import androidx.lifecycle.ViewModelProvider -import kotlinx.android.synthetic.main.error_no_connection_with_button_small.* -import kotlinx.android.synthetic.main.fragment_profile_activities.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.base.App +import org.stepic.droid.databinding.FragmentProfileActivitiesBinding import org.stepik.android.presentation.profile_activities.ProfileActivitiesPresenter import org.stepik.android.presentation.profile_activities.ProfileActivitiesView import org.stepik.android.view.ui.delegate.ViewStateDelegate @@ -33,6 +33,7 @@ class ProfileActivitiesFragment : Fragment(R.layout.fragment_profile_activities) private var userId by argument() + private val profileActivitiesBinding: FragmentProfileActivitiesBinding by viewBinding(FragmentProfileActivitiesBinding::bind) private val profileActivitiesPresenter: ProfileActivitiesPresenter by viewModels { viewModelFactory } private lateinit var viewStateDelegate: ViewStateDelegate @@ -53,12 +54,12 @@ class ProfileActivitiesFragment : Fragment(R.layout.fragment_profile_activities) viewStateDelegate.addState() viewStateDelegate.addState() viewStateDelegate.addState() - viewStateDelegate.addState(view, streakLoadingPlaceholder) - viewStateDelegate.addState(view, streakLoadingError) - viewStateDelegate.addState(view, streakContainer) + viewStateDelegate.addState(view, profileActivitiesBinding.streakLoadingPlaceholder) + viewStateDelegate.addState(view, profileActivitiesBinding.streakLoadingError.root) + viewStateDelegate.addState(view, profileActivitiesBinding.streakContainer) setDataToPresenter() - tryAgain.setOnClickListener { setDataToPresenter(forceUpdate = true) } + profileActivitiesBinding.streakLoadingError.tryAgain.setOnClickListener { setDataToPresenter(forceUpdate = true) } } private fun setDataToPresenter(forceUpdate: Boolean = false) { @@ -89,10 +90,10 @@ class ProfileActivitiesFragment : Fragment(R.layout.fragment_profile_activities) R.color.color_overlay_yellow } - currentStreak.supportCompoundDrawablesTintList = ColorStateList + profileActivitiesBinding.currentStreak.supportCompoundDrawablesTintList = ColorStateList .valueOf(ContextCompat.getColor(requireContext(), streakTintColorRes)) - currentStreakCount.text = streak + profileActivitiesBinding.currentStreakCount.text = streak .takeIf { it > 0 } ?.toString() .orEmpty() @@ -109,10 +110,10 @@ class ProfileActivitiesFragment : Fragment(R.layout.fragment_profile_activities) else -> R.string.profile_activities_current_streak_start } - currentStreak.setText(currentStreakRes) + profileActivitiesBinding.currentStreak.setText(currentStreakRes) - maxStreakCount.text = maxStreak.toString() + profileActivitiesBinding.maxStreakCount.text = maxStreak.toString() } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/profile_certificates/ui/fragment/ProfileCertificatesFragment.kt b/app/src/main/java/org/stepik/android/view/profile_certificates/ui/fragment/ProfileCertificatesFragment.kt index c0969d80c8..c0cc6063fe 100644 --- a/app/src/main/java/org/stepik/android/view/profile_certificates/ui/fragment/ProfileCertificatesFragment.kt +++ b/app/src/main/java/org/stepik/android/view/profile_certificates/ui/fragment/ProfileCertificatesFragment.kt @@ -6,12 +6,12 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.error_no_connection_with_button_small.* -import kotlinx.android.synthetic.main.fragment_profile_certificates.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager import org.stepic.droid.model.CertificateListItem +import org.stepic.droid.databinding.FragmentProfileCertificatesBinding import org.stepik.android.presentation.profile_certificates.ProfileCertificatesPresenter import org.stepik.android.presentation.profile_certificates.ProfileCertificatesView import org.stepik.android.view.certificate.ui.adapter.delegate.CertificateProfileAdapterDelegate @@ -37,6 +37,7 @@ class ProfileCertificatesFragment : Fragment(R.layout.fragment_profile_certifica internal lateinit var screenManager: ScreenManager private var userId: Long by argument() + private val binding: FragmentProfileCertificatesBinding by viewBinding(FragmentProfileCertificatesBinding::bind) private var isCurrentUser: Boolean = false private val certificatesPresenter: ProfileCertificatesPresenter by viewModels { viewModelFactory } @@ -65,18 +66,18 @@ class ProfileCertificatesFragment : Fragment(R.layout.fragment_profile_certifica viewStateDelegate = ViewStateDelegate() viewStateDelegate.addState() viewStateDelegate.addState() - viewStateDelegate.addState(view, profileCertificatesRecycler) - viewStateDelegate.addState(view, certificatesLoadingError) - viewStateDelegate.addState(view, profileCertificatesRecycler) - viewStateDelegate.addState(view, profileCertificatesRecycler) + viewStateDelegate.addState(view, binding.profileCertificatesRecycler) + viewStateDelegate.addState(view, binding.certificatesLoadingError.root) + viewStateDelegate.addState(view, binding.profileCertificatesRecycler) + viewStateDelegate.addState(view, binding.profileCertificatesRecycler) viewStateDelegate.addState() - tryAgain.setOnClickListener { setDataToPresenter(forceUpdate = true) } - profileCertificatesTitle.setOnClickListener { screenManager.showCertificates(requireContext(), profileId, isCurrentUser) } + binding.certificatesLoadingError.tryAgain.setOnClickListener { setDataToPresenter(forceUpdate = true) } + binding.profileCertificatesTitle.setOnClickListener { screenManager.showCertificates(requireContext(), profileId, isCurrentUser) } - profileCertificatesRecycler.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) - profileCertificatesRecycler.isNestedScrollingEnabled = false - profileCertificatesRecycler.adapter = certificatesAdapter + binding.profileCertificatesRecycler.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) + binding.profileCertificatesRecycler.isNestedScrollingEnabled = false + binding.profileCertificatesRecycler.adapter = certificatesAdapter setDataToPresenter() } diff --git a/app/src/main/java/org/stepik/android/view/profile_courses/ui/fragment/ProfileCoursesFragment.kt b/app/src/main/java/org/stepik/android/view/profile_courses/ui/fragment/ProfileCoursesFragment.kt index f20e351e32..fbe1d67bc3 100644 --- a/app/src/main/java/org/stepik/android/view/profile_courses/ui/fragment/ProfileCoursesFragment.kt +++ b/app/src/main/java/org/stepik/android/view/profile_courses/ui/fragment/ProfileCoursesFragment.kt @@ -8,12 +8,12 @@ import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearSnapHelper import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.error_no_connection_with_button_small.* -import kotlinx.android.synthetic.main.fragment_profile_courses.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.FragmentProfileCoursesBinding import org.stepik.android.domain.course.analytic.CourseViewSource import org.stepik.android.domain.course_list.model.CourseListItem import org.stepik.android.domain.course_list.model.CourseListQuery @@ -62,6 +62,7 @@ class ProfileCoursesFragment : Fragment(R.layout.fragment_profile_courses), Prof internal lateinit var tableLayoutHorizontalSpanCountResolver: TableLayoutHorizontalSpanCountResolver private var userId by argument() + private val binding: FragmentProfileCoursesBinding by viewBinding(FragmentProfileCoursesBinding::bind) private val profileCoursesPresenter: ProfileCoursesPresenter by viewModels { viewModelFactory } private lateinit var courseContinueViewDelegate: CourseContinueViewDelegate @@ -112,19 +113,19 @@ class ProfileCoursesFragment : Fragment(R.layout.fragment_profile_courses), Prof override fun onViewCreated(view: View, savedInstanceState: Bundle?) { viewStateDelegate = ViewStateDelegate() viewStateDelegate.addState() - viewStateDelegate.addState(view, profileCoursesPlaceholder) + viewStateDelegate.addState(view, binding.profileCoursesPlaceholder) viewStateDelegate.addState() - viewStateDelegate.addState(view, profileCoursesLoadingError) - viewStateDelegate.addState(view, profileCoursesRecycler) + viewStateDelegate.addState(view, binding.profileCoursesLoadingError.root) + viewStateDelegate.addState(view, binding.profileCoursesRecycler) setDataToPresenter() - tryAgain.setOnClickListener { setDataToPresenter(forceUpdate = true) } + binding.profileCoursesLoadingError.tryAgain.setOnClickListener { setDataToPresenter(forceUpdate = true) } val rowCount = resources.getInteger(R.integer.course_list_rows) val columnsCount = resources.getInteger(R.integer.course_list_columns) tableLayoutManager = TableLayoutManager(requireContext(), columnsCount, rowCount, RecyclerView.HORIZONTAL, false) - with(profileCoursesRecycler) { + with(binding.profileCoursesRecycler) { layoutManager = tableLayoutManager adapter = coursesAdapter @@ -155,7 +156,7 @@ class ProfileCoursesFragment : Fragment(R.layout.fragment_profile_courses), Prof when (state) { is ProfileCoursesView.State.Content -> { coursesAdapter.items = state.courseListDataItems - (profileCoursesRecycler.layoutManager as? GridLayoutManager) + (binding.profileCoursesRecycler.layoutManager as? GridLayoutManager) ?.spanCount = min(resources.getInteger(R.integer.course_list_rows), state.courseListDataItems.size) } else -> Unit diff --git a/app/src/main/java/org/stepik/android/view/profile_detail/ui/fragment/ProfileDetailFragment.kt b/app/src/main/java/org/stepik/android/view/profile_detail/ui/fragment/ProfileDetailFragment.kt index 64f6061bdb..992258983a 100644 --- a/app/src/main/java/org/stepik/android/view/profile_detail/ui/fragment/ProfileDetailFragment.kt +++ b/app/src/main/java/org/stepik/android/view/profile_detail/ui/fragment/ProfileDetailFragment.kt @@ -7,9 +7,10 @@ import androidx.core.widget.TextViewCompat import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider -import kotlinx.android.synthetic.main.fragment_profile_detail.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.base.App +import org.stepic.droid.databinding.FragmentProfileDetailBinding import org.stepic.droid.ui.util.collapse import org.stepic.droid.ui.util.expand import org.stepik.android.domain.profile.model.ProfileData @@ -31,6 +32,7 @@ class ProfileDetailFragment : Fragment(R.layout.fragment_profile_detail), Profil internal lateinit var viewModelFactory: ViewModelProvider.Factory private var userId by argument() + private val binding: FragmentProfileDetailBinding by viewBinding(FragmentProfileDetailBinding::bind) private val profileDetailPresenter: ProfileDetailPresenter by viewModels { viewModelFactory } @@ -40,16 +42,16 @@ class ProfileDetailFragment : Fragment(R.layout.fragment_profile_detail), Profil } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - TextViewCompat.setLineHeight(profileDetails.textView, resources.getDimensionPixelOffset(R.dimen.comment_item_text_line)) + TextViewCompat.setLineHeight(binding.profileDetails.textView, resources.getDimensionPixelOffset(R.dimen.comment_item_text_line)) - profileDetailsTitleArrow.changeState() - profileDetailsTitle.setOnClickListener { - profileDetailsTitleArrow.changeState() - val isExpanded = profileDetailsTitleArrow.isExpanded() + binding.profileDetailsTitleArrow.changeState() + binding.profileDetailsTitle.setOnClickListener { + binding.profileDetailsTitleArrow.changeState() + val isExpanded = binding.profileDetailsTitleArrow.isExpanded() if (isExpanded) { - profileDetails.expand() + binding.profileDetails.expand() } else { - profileDetails.collapse() + binding.profileDetails.collapse() } } @@ -78,7 +80,7 @@ class ProfileDetailFragment : Fragment(R.layout.fragment_profile_detail), Profil if (details.isNullOrBlank()) { view?.isVisible = false } else { - profileDetails.setText(details) + binding.profileDetails.setText(details) view?.isVisible = true } } diff --git a/app/src/main/java/org/stepik/android/view/profile_edit/ui/activity/ProfileEditActivity.kt b/app/src/main/java/org/stepik/android/view/profile_edit/ui/activity/ProfileEditActivity.kt index 8893037228..866a9b9f9f 100644 --- a/app/src/main/java/org/stepik/android/view/profile_edit/ui/activity/ProfileEditActivity.kt +++ b/app/src/main/java/org/stepik/android/view/profile_edit/ui/activity/ProfileEditActivity.kt @@ -11,10 +11,11 @@ import androidx.core.content.ContextCompat import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.activity_profile_edit.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.ActivityProfileEditBinding import org.stepic.droid.ui.util.initCenteredToolbar import org.stepic.droid.ui.util.snackbar import ru.nobird.app.core.model.mutate @@ -34,6 +35,8 @@ class ProfileEditActivity : AppCompatActivity(R.layout.activity_profile_edit), P Intent(context, ProfileEditActivity::class.java) } + private val binding: ActivityProfileEditBinding by viewBinding(ActivityProfileEditBinding::bind) + private val profileEditPresenter: ProfileEditPresenter by viewModels { viewModelFactory } @Inject @@ -72,17 +75,17 @@ class ProfileEditActivity : AppCompatActivity(R.layout.activity_profile_edit), P profileEditAdapter += adapterDelegate(R.layout.item_profile_edit_navigation) - navigationRecycler.layoutManager = LinearLayoutManager(this) - navigationRecycler.adapter = profileEditAdapter + binding.navigationRecycler.layoutManager = LinearLayoutManager(this) + binding.navigationRecycler.adapter = profileEditAdapter - navigationRecycler.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL).apply { + binding.navigationRecycler.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL).apply { ContextCompat.getDrawable(this@ProfileEditActivity, R.drawable.bg_divider_vertical)?.let(::setDrawable) }) viewStateDelegate.addState() viewStateDelegate.addState() - viewStateDelegate.addState(profileEditEmptyLogin) - viewStateDelegate.addState(navigationRecycler) + viewStateDelegate.addState(binding.profileEditEmptyLogin.root) + viewStateDelegate.addState(binding.navigationRecycler) } private fun injectComponent() { @@ -114,12 +117,12 @@ class ProfileEditActivity : AppCompatActivity(R.layout.activity_profile_edit), P when (requestCode) { ProfileEditInfoActivity.REQUEST_CODE -> if (resultCode == Activity.RESULT_OK) { - root.snackbar(messageRes = R.string.profile_edit_change_success_info) + binding.root.snackbar(messageRes = R.string.profile_edit_change_success_info) } ProfileEditPasswordActivity.REQUEST_CODE -> if (resultCode == Activity.RESULT_OK) { - root.snackbar(messageRes = R.string.profile_edit_change_success_password) + binding.root.snackbar(messageRes = R.string.profile_edit_change_success_password) } else -> diff --git a/app/src/main/java/org/stepik/android/view/profile_edit/ui/activity/ProfileEditInfoActivity.kt b/app/src/main/java/org/stepik/android/view/profile_edit/ui/activity/ProfileEditInfoActivity.kt index 0f9bb3e5db..7f84c524dd 100644 --- a/app/src/main/java/org/stepik/android/view/profile_edit/ui/activity/ProfileEditInfoActivity.kt +++ b/app/src/main/java/org/stepik/android/view/profile_edit/ui/activity/ProfileEditInfoActivity.kt @@ -10,9 +10,10 @@ import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.DialogFragment import androidx.lifecycle.ViewModelProvider -import kotlinx.android.synthetic.main.activity_profile_edit_info.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.base.App +import org.stepic.droid.databinding.ActivityProfileEditInfoBinding import org.stepic.droid.ui.dialogs.LoadingProgressDialogFragment import org.stepic.droid.ui.util.initCenteredToolbar import org.stepic.droid.ui.util.snackbar @@ -33,6 +34,8 @@ class ProfileEditInfoActivity : AppCompatActivity(), ProfileEditInfoView { .putExtra(EXTRA_PROFILE, profile) } + private val binding: ActivityProfileEditInfoBinding by viewBinding(ActivityProfileEditInfoBinding::bind) + private val progressDialogFragment: DialogFragment = LoadingProgressDialogFragment.newInstance() @@ -52,11 +55,11 @@ class ProfileEditInfoActivity : AppCompatActivity(), ProfileEditInfoView { initCenteredToolbar(R.string.profile_edit_info_title, showHomeButton = true, homeIndicator = R.drawable.ic_close_dark) if (savedInstanceState == null) { - firstNameEditText.setText(profile.firstName ?: "") - lastNameEditText.setText(profile.lastName ?: "") + binding.firstNameEditText.setText(profile.firstName ?: "") + binding.lastNameEditText.setText(profile.lastName ?: "") - shortBioEditText.setText(profile.shortBio ?: "") - detailsEditText.setText(profile.details ?: "") + binding.shortBioEditText.setText(profile.shortBio ?: "") + binding.detailsEditText.setText(profile.details ?: "") } } @@ -96,10 +99,10 @@ class ProfileEditInfoActivity : AppCompatActivity(), ProfileEditInfoView { } private fun submit() { - val firstName = firstNameEditText.text.toString() - val lastName = lastNameEditText.text.toString() - val shortBio = shortBioEditText.text.toString() - val details = detailsEditText.text.toString() + val firstName = binding.firstNameEditText.text.toString() + val lastName = binding.lastNameEditText.text.toString() + val shortBio = binding.shortBioEditText.text.toString() + val details = binding.detailsEditText.text.toString() profileEditInfoPresenter.updateProfileInfo(profile, firstName, lastName, shortBio, details) } @@ -121,11 +124,11 @@ class ProfileEditInfoActivity : AppCompatActivity(), ProfileEditInfoView { } override fun showNetworkError() { - root.snackbar(messageRes = R.string.no_connection) + binding.root.snackbar(messageRes = R.string.no_connection) } override fun showInfoError() { - root.snackbar(messageRes = R.string.profile_edit_error_info) + binding.root.snackbar(messageRes = R.string.profile_edit_error_info) } override fun finish() { diff --git a/app/src/main/java/org/stepik/android/view/profile_edit/ui/activity/ProfileEditPasswordActivity.kt b/app/src/main/java/org/stepik/android/view/profile_edit/ui/activity/ProfileEditPasswordActivity.kt index f4137437b1..e2addc7a43 100644 --- a/app/src/main/java/org/stepik/android/view/profile_edit/ui/activity/ProfileEditPasswordActivity.kt +++ b/app/src/main/java/org/stepik/android/view/profile_edit/ui/activity/ProfileEditPasswordActivity.kt @@ -12,9 +12,10 @@ import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.DialogFragment import androidx.lifecycle.ViewModelProvider -import kotlinx.android.synthetic.main.activity_profile_edit_password.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.base.App +import org.stepic.droid.databinding.ActivityProfileEditPasswordBinding import org.stepic.droid.ui.dialogs.LoadingProgressDialogFragment import org.stepic.droid.ui.util.initCenteredToolbar import org.stepic.droid.ui.util.snackbar @@ -45,6 +46,8 @@ class ProfileEditPasswordActivity : AppCompatActivity(), ProfileEditPasswordView private val profileId by lazy { intent.getLongExtra(EXTRA_PROFILE_ID, -1) } + private val binding: ActivityProfileEditPasswordBinding by viewBinding(ActivityProfileEditPasswordBinding::bind) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_profile_edit_password) @@ -53,18 +56,18 @@ class ProfileEditPasswordActivity : AppCompatActivity(), ProfileEditPasswordView initCenteredToolbar(R.string.profile_edit_password_title, showHomeButton = true, homeIndicator = R.drawable.ic_close_dark) - currentPasswordEditText.addTextChangedListener(object : TextWatcher { + binding.currentPasswordEditText.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) { - currentPasswordInputLayout.isErrorEnabled = false + binding.currentPasswordInputLayout.isErrorEnabled = false } override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} }) - newPasswordEditText.addTextChangedListener(object : TextWatcher { + binding.newPasswordEditText.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) { - newPasswordInputLayout.isErrorEnabled = false + binding.newPasswordInputLayout.isErrorEnabled = false } override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} @@ -108,16 +111,16 @@ class ProfileEditPasswordActivity : AppCompatActivity(), ProfileEditPasswordView } private fun submit() { - val isCurrentPasswordFilled = ValidateUtil.validateRequiredField(currentPasswordInputLayout, currentPasswordEditText) - val isNewPasswordFilled = ValidateUtil.validateRequiredField(newPasswordInputLayout, newPasswordEditText) + val isCurrentPasswordFilled = ValidateUtil.validateRequiredField(binding.currentPasswordInputLayout, binding.currentPasswordEditText) + val isNewPasswordFilled = ValidateUtil.validateRequiredField(binding.newPasswordInputLayout, binding.newPasswordEditText) if (!isCurrentPasswordFilled || !isNewPasswordFilled) { return } - val currentPassword = currentPasswordEditText.text.toString() - val newPassword = newPasswordEditText.text.toString() + val currentPassword = binding.currentPasswordEditText.text.toString() + val newPassword = binding.newPasswordEditText.text.toString() profileEditPasswordPresenter .updateProfilePassword(profileId, currentPassword, newPassword) @@ -140,15 +143,15 @@ class ProfileEditPasswordActivity : AppCompatActivity(), ProfileEditPasswordView } override fun showNetworkError() { - root.snackbar(messageRes = R.string.no_connection) + binding.root.snackbar(messageRes = R.string.no_connection) } override fun showPasswordError() { - root.snackbar(messageRes = R.string.profile_edit_error_password) + binding.root.snackbar(messageRes = R.string.profile_edit_error_password) } override fun finish() { super.finish() overridePendingTransition(R.anim.no_transition, R.anim.push_down) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/profile_edit/ui/adapter/delegates/ProfileEditTextDelegate.kt b/app/src/main/java/org/stepik/android/view/profile_edit/ui/adapter/delegates/ProfileEditTextDelegate.kt index d1bbdd5d3a..8d5d1465c7 100644 --- a/app/src/main/java/org/stepik/android/view/profile_edit/ui/adapter/delegates/ProfileEditTextDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/profile_edit/ui/adapter/delegates/ProfileEditTextDelegate.kt @@ -2,8 +2,9 @@ package org.stepik.android.view.profile_edit.ui.adapter.delegates import android.view.View import android.view.ViewGroup -import kotlinx.android.synthetic.main.item_profile_edit_navigation.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemProfileEditNavigationBinding import org.stepik.android.view.profile_edit.model.ProfileEditItem import ru.nobird.android.ui.adapterdelegates.AdapterDelegate import ru.nobird.android.ui.adapterdelegates.DelegateViewHolder @@ -18,17 +19,16 @@ class ProfileEditTextDelegate( true inner class ViewHolder(root: View) : DelegateViewHolder(root) { - private val title = root.title - private val subtitle = root.subtitle + private val viewBinding: ItemProfileEditNavigationBinding by viewBinding { ItemProfileEditNavigationBinding.bind(root) } init { - root.setOnClickListener { itemData?.let(onItemClicked) } + itemView.setOnClickListener { itemData?.let(onItemClicked) } } override fun onBind(data: ProfileEditItem) { - title.text = data.title - subtitle.text = data.subtitle + viewBinding.title.text = data.title + viewBinding.subtitle.text = data.subtitle itemView.isEnabled = itemData?.type != ProfileEditItem.Type.EMAIL } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/profile_id/ui/fragment/ProfileIdFragment.kt b/app/src/main/java/org/stepik/android/view/profile_id/ui/fragment/ProfileIdFragment.kt index 00a6068638..d5582d6738 100644 --- a/app/src/main/java/org/stepik/android/view/profile_id/ui/fragment/ProfileIdFragment.kt +++ b/app/src/main/java/org/stepik/android/view/profile_id/ui/fragment/ProfileIdFragment.kt @@ -7,9 +7,10 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider -import kotlinx.android.synthetic.main.view_profile_user_id.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.base.App +import org.stepic.droid.databinding.ViewProfileUserIdBinding import org.stepic.droid.util.copyTextToClipboard import org.stepik.android.domain.profile.model.ProfileData import org.stepik.android.presentation.profile_id.ProfileIdPresenter @@ -33,6 +34,8 @@ class ProfileIdFragment : Fragment(R.layout.view_profile_user_id), ProfileIdView private val profileIdPresenter: ProfileIdPresenter by viewModels { viewModelFactory } + private val binding: ViewProfileUserIdBinding by viewBinding(ViewProfileUserIdBinding::bind) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) injectComponent() @@ -40,7 +43,7 @@ class ProfileIdFragment : Fragment(R.layout.view_profile_user_id), ProfileIdView override fun onViewCreated(view: View, savedInstanceState: Bundle?) { view.isVisible = false - profileUserId.setOnLongClickListener { + binding.profileUserId.setOnLongClickListener { val textToCopy = (it as AppCompatTextView).text.toString() requireContext().copyTextToClipboard(textToCopy = textToCopy, toastMessage = getString(R.string.copied_to_clipboard_toast)) true @@ -68,9 +71,9 @@ class ProfileIdFragment : Fragment(R.layout.view_profile_user_id), ProfileIdView if (profileData?.isCurrentUser == true && userId != null) { view?.isVisible = true - profileUserId.text = getString(R.string.profile_user_id, userId) + binding.profileUserId.text = getString(R.string.profile_user_id, userId) } else { view?.isVisible = false } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/profile_links/ui/fragment/ProfileLinksFragment.kt b/app/src/main/java/org/stepik/android/view/profile_links/ui/fragment/ProfileLinksFragment.kt index d1ec8cbe3a..32cf9ba1df 100644 --- a/app/src/main/java/org/stepik/android/view/profile_links/ui/fragment/ProfileLinksFragment.kt +++ b/app/src/main/java/org/stepik/android/view/profile_links/ui/fragment/ProfileLinksFragment.kt @@ -8,11 +8,11 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.error_no_connection_with_button_small.* -import kotlinx.android.synthetic.main.fragment_profile_links.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.FragmentProfileLinksBinding import org.stepik.android.model.SocialProfile import org.stepik.android.presentation.profile_links.ProfileLinksPresenter import org.stepik.android.presentation.profile_links.ProfileLinksView @@ -45,6 +45,8 @@ class ProfileLinksFragment : Fragment(), ProfileLinksView { private lateinit var viewStateDelegate: ViewStateDelegate + private val binding: FragmentProfileLinksBinding by viewBinding(FragmentProfileLinksBinding::bind) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -65,14 +67,14 @@ class ProfileLinksFragment : Fragment(), ProfileLinksView { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { viewStateDelegate = ViewStateDelegate() viewStateDelegate.addState() - viewStateDelegate.addState(view, profileExternalLinksLoading) - viewStateDelegate.addState(view, profileExternalLinksLoadingError) - viewStateDelegate.addState(view, profileExternalLinksRecycler) + viewStateDelegate.addState(view, binding.profileExternalLinksLoading) + viewStateDelegate.addState(view, binding.profileExternalLinksLoadingError.root) + viewStateDelegate.addState(view, binding.profileExternalLinksRecycler) viewStateDelegate.addState() - tryAgain.setOnClickListener { setDataToPresenter(forceUpdate = true) } + binding.profileExternalLinksLoadingError.tryAgain.setOnClickListener { setDataToPresenter(forceUpdate = true) } - with(profileExternalLinksRecycler) { + with(binding.profileExternalLinksRecycler) { adapter = profileLinksAdapter layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) isNestedScrollingEnabled = false @@ -106,4 +108,4 @@ class ProfileLinksFragment : Fragment(), ProfileLinksView { profileLinksPresenter.detachView(this) super.onStop() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/profile_notification/ui/fragment/ProfileNotificationFragment.kt b/app/src/main/java/org/stepik/android/view/profile_notification/ui/fragment/ProfileNotificationFragment.kt index 2ced26ac04..34e1f28145 100644 --- a/app/src/main/java/org/stepik/android/view/profile_notification/ui/fragment/ProfileNotificationFragment.kt +++ b/app/src/main/java/org/stepik/android/view/profile_notification/ui/fragment/ProfileNotificationFragment.kt @@ -14,11 +14,11 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider -import kotlinx.android.synthetic.main.fragment_profile_notification.* -import kotlinx.android.synthetic.main.view_notification_interval_chooser.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App +import org.stepic.droid.databinding.FragmentProfileNotificationBinding import org.stepic.droid.ui.dialogs.TimeIntervalPickerDialogFragment import org.stepic.droid.ui.util.collapse import org.stepic.droid.ui.util.expand @@ -37,6 +37,8 @@ import java.util.TimeZone import javax.inject.Inject class ProfileNotificationFragment : Fragment(R.layout.fragment_profile_notification), ProfileNotificationView, TimeIntervalPickerDialogFragment.Companion.Callback { + private val binding: FragmentProfileNotificationBinding by viewBinding(FragmentProfileNotificationBinding::bind) + companion object { fun newInstance(userId: Long): Fragment = ProfileNotificationFragment() @@ -65,7 +67,7 @@ class ProfileNotificationFragment : Fragment(R.layout.fragment_profile_notificat override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - notificationIntervalChooserContainer.setOnClickListener { + binding.notificationIntervalChooser.root.setOnClickListener { val supportFragmentManager = fragmentManager ?: return@setOnClickListener @@ -75,7 +77,7 @@ class ProfileNotificationFragment : Fragment(R.layout.fragment_profile_notificat dialog.showIfNotExists(supportFragmentManager, TimeIntervalPickerDialogFragment.TAG) } - marketingNotificationSwitch.setOnCheckedChangeListener { _, isChecked -> + binding.marketingNotificationSwitch.setOnCheckedChangeListener { _, isChecked -> if (!isMarketingNotificationSwitchUpdating) { profileNotificationPresenter.switchMarketingNotification(isChecked) } @@ -115,9 +117,9 @@ class ProfileNotificationFragment : Fragment(R.layout.fragment_profile_notificat override fun showNotificationEnabledState(notificationEnabled: Boolean, notificationTimeValue: String) { val notificationEnabledWithPermission = notificationEnabled && requireContext().isNotificationPermissionGranted() - notificationStreakSwitch.isChecked = notificationEnabledWithPermission - if (notificationStreakSwitch.visibility != View.VISIBLE) { - notificationStreakSwitch.visibility = View.VISIBLE + binding.notificationStreakSwitch.isChecked = notificationEnabledWithPermission + if (binding.notificationStreakSwitch.visibility != View.VISIBLE) { + binding.notificationStreakSwitch.visibility = View.VISIBLE } if (notificationEnabledWithPermission) { hideNotificationTime(false) @@ -125,7 +127,7 @@ class ProfileNotificationFragment : Fragment(R.layout.fragment_profile_notificat hideNotificationTime(true) } - notificationStreakSwitch.setOnCheckedChangeListener { _, isChecked -> + binding.notificationStreakSwitch.setOnCheckedChangeListener { _, isChecked -> if (!requireContext().isNotificationPermissionGranted()) { requestNotificationPermission() profileNotificationPresenter.tryShowNotificationSetting() @@ -136,25 +138,25 @@ class ProfileNotificationFragment : Fragment(R.layout.fragment_profile_notificat } // need to set for show default value, when user enable it - notificationIntervalTitle.text = + binding.notificationIntervalChooser.notificationIntervalTitle.text = resources.getString(R.string.notification_time, notificationTimeValue) } override fun setNewTimeInterval(timePresentationString: String) { - notificationIntervalTitle.text = resources.getString(R.string.notification_time, timePresentationString) + binding.notificationIntervalChooser.notificationIntervalTitle.text = resources.getString(R.string.notification_time, timePresentationString) } override fun showMarketingNotificationState(subscribedForMarketing: Boolean, isUpdating: Boolean) { - marketingNotificationSwitch.isVisible = true - marketingNotificationSwitch.isEnabled = !isUpdating + binding.marketingNotificationSwitch.isVisible = true + binding.marketingNotificationSwitch.isEnabled = !isUpdating isMarketingNotificationSwitchUpdating = true - marketingNotificationSwitch.isChecked = subscribedForMarketing + binding.marketingNotificationSwitch.isChecked = subscribedForMarketing isMarketingNotificationSwitchUpdating = false } override fun hideMarketingNotification() { - marketingNotificationSwitch.isVisible = false + binding.marketingNotificationSwitch.isVisible = false } override fun showMarketingNotificationUpdateFailed() { @@ -163,10 +165,10 @@ class ProfileNotificationFragment : Fragment(R.layout.fragment_profile_notificat override fun hideNotificationTime(needHide: Boolean) { if (needHide) { - notificationIntervalChooserContainer.collapse(object : Animation.AnimationListener { + binding.notificationIntervalChooser.root.collapse(object : Animation.AnimationListener { override fun onAnimationRepeat(animation: Animation?) {} override fun onAnimationEnd(animation: Animation?) { - (notificationStreakSwitch.layoutParams as LinearLayoutCompat.LayoutParams).apply { + (binding.notificationStreakSwitch.layoutParams as LinearLayoutCompat.LayoutParams).apply { setMargins( resources.getDimension(R.dimen.profile_block_margin).toInt(), 0, @@ -178,10 +180,10 @@ class ProfileNotificationFragment : Fragment(R.layout.fragment_profile_notificat override fun onAnimationStart(animation: Animation?) {} }) } else { - notificationIntervalChooserContainer.expand(object : Animation.AnimationListener { + binding.notificationIntervalChooser.root.expand(object : Animation.AnimationListener { override fun onAnimationRepeat(animation: Animation?) {} override fun onAnimationEnd(animation: Animation?) { - notificationTimeZoneInfo.setPadding( + binding.notificationIntervalChooser.notificationTimeZoneInfo.setPadding( resources.getDimension(R.dimen.profile_block_margin).toInt(), 0, resources.getDimension(R.dimen.profile_block_margin).toInt(), @@ -228,6 +230,6 @@ class ProfileNotificationFragment : Fragment(R.layout.fragment_profile_notificat val print = String.format("%s\n%s", DateTimeHelper.hourMinutesOfMidnightDiffWithUtc(timezone, nowDate), timezone.getDisplayName(isDaylight, TimeZone.LONG)) - notificationTimeZoneInfo.text = getString(R.string.streak_updated_timezone, print) + binding.notificationIntervalChooser.notificationTimeZoneInfo.text = getString(R.string.streak_updated_timezone, print) } } \ No newline at end of file diff --git a/app/src/main/java/org/stepik/android/view/settings/ui/activity/SettingsActivity.kt b/app/src/main/java/org/stepik/android/view/settings/ui/activity/SettingsActivity.kt index e7bb9925c3..e9294d82bc 100644 --- a/app/src/main/java/org/stepik/android/view/settings/ui/activity/SettingsActivity.kt +++ b/app/src/main/java/org/stepik/android/view/settings/ui/activity/SettingsActivity.kt @@ -36,9 +36,11 @@ open class SettingsActivity : SmartLockActivityBase(), SettingsFragment.SignOutL } protected open fun setUpToolbar() { - initCenteredToolbar(R.string.settings_title, + initCenteredToolbar( + R.string.settings_title, showHomeButton = true, - homeIndicator = closeIconDrawableRes) + homeIndicator = closeIconDrawableRes + ) } override fun onOptionsItemSelected(item: MenuItem): Boolean = diff --git a/app/src/main/java/org/stepik/android/view/settings/ui/fragment/SettingsFragment.kt b/app/src/main/java/org/stepik/android/view/settings/ui/fragment/SettingsFragment.kt index eedfc1dde2..219c9dfc2a 100644 --- a/app/src/main/java/org/stepik/android/view/settings/ui/fragment/SettingsFragment.kt +++ b/app/src/main/java/org/stepik/android/view/settings/ui/fragment/SettingsFragment.kt @@ -6,13 +6,14 @@ import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider +import by.kirich1409.viewbindingdelegate.viewBinding import com.vk.api.sdk.VK -import kotlinx.android.synthetic.main.fragment_settings.* import org.stepic.droid.R import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.FragmentSettingsBinding import org.stepic.droid.preferences.SharedPreferenceHelper import org.stepic.droid.preferences.UserPreferences import org.stepic.droid.ui.dialogs.AllowMobileDataDialogFragment @@ -43,6 +44,8 @@ class SettingsFragment : SettingsFragment() } + private val binding: FragmentSettingsBinding by viewBinding(FragmentSettingsBinding::bind) + private val presenter: SettingsPresenter by viewModels { viewModelFactory } private val progressDialogFragment: DialogFragment = @@ -74,41 +77,41 @@ class SettingsFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - notificationActionButton.setOnClickListener { + binding.notificationActionButton.setOnClickListener { screenManager.showNotificationSettings(activity) } - fragmentSettingsWifiEnableSwitch.isChecked = !sharedPreferenceHelper.isMobileInternetAlsoAllowed // if first time it is true + binding.fragmentSettingsWifiEnableSwitch.isChecked = !sharedPreferenceHelper.isMobileInternetAlsoAllowed // if first time it is true - fragmentSettingsExternalPlayerSwitch.isChecked = userPreferences.isOpenInExternal + binding.fragmentSettingsExternalPlayerSwitch.isChecked = userPreferences.isOpenInExternal - fragmentSettingsExternalPlayerSwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.isOpenInExternal = isChecked } + binding.fragmentSettingsExternalPlayerSwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.isOpenInExternal = isChecked } - fragmentSettingsCalendarWidgetSwitch.isChecked = userPreferences.isNeedToShowCalendarWidget + binding.fragmentSettingsCalendarWidgetSwitch.isChecked = userPreferences.isNeedToShowCalendarWidget - fragmentSettingsCalendarWidgetSwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.isNeedToShowCalendarWidget = isChecked } + binding.fragmentSettingsCalendarWidgetSwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.isNeedToShowCalendarWidget = isChecked } - fragmentSettingsKeepScreenOnSwitch.isChecked = userPreferences.isKeepScreenOnSteps - fragmentSettingsKeepScreenOnSwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.isKeepScreenOnSteps = isChecked } + binding.fragmentSettingsKeepScreenOnSwitch.isChecked = userPreferences.isKeepScreenOnSteps + binding.fragmentSettingsKeepScreenOnSwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.isKeepScreenOnSteps = isChecked } - fragmentSettingsAdaptiveMode.isChecked = userPreferences.isAdaptiveModeEnabled - fragmentSettingsAdaptiveMode.setOnCheckedChangeListener { _, isChecked -> userPreferences.isAdaptiveModeEnabled = isChecked } + binding.fragmentSettingsAdaptiveMode.isChecked = userPreferences.isAdaptiveModeEnabled + binding.fragmentSettingsAdaptiveMode.setOnCheckedChangeListener { _, isChecked -> userPreferences.isAdaptiveModeEnabled = isChecked } - fragmentSettingsDiscountingPolicySwitch.isChecked = userPreferences.isShowDiscountingPolicyWarning + binding.fragmentSettingsDiscountingPolicySwitch.isChecked = userPreferences.isShowDiscountingPolicyWarning - fragmentSettingsDiscountingPolicySwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.isShowDiscountingPolicyWarning = isChecked } + binding.fragmentSettingsDiscountingPolicySwitch.setOnCheckedChangeListener { _, isChecked -> userPreferences.isShowDiscountingPolicyWarning = isChecked } - fragmentSettingsAutoplay.isChecked = userPreferences.isAutoplayEnabled - fragmentSettingsAutoplay.setOnCheckedChangeListener { _, isChecked -> userPreferences.isAutoplayEnabled = isChecked } + binding.fragmentSettingsAutoplay.isChecked = userPreferences.isAutoplayEnabled + binding.fragmentSettingsAutoplay.setOnCheckedChangeListener { _, isChecked -> userPreferences.isAutoplayEnabled = isChecked } - fragmentSettingsWifiEnableSwitch.setOnCheckedChangeListener { _, newCheckedState -> - if (fragmentSettingsWifiEnableSwitch.isUserTriggered) { + binding.fragmentSettingsWifiEnableSwitch.setOnCheckedChangeListener { _, newCheckedState -> + if (binding.fragmentSettingsWifiEnableSwitch.isUserTriggered) { if (newCheckedState) { // wifi only onMobileDataStateChanged(false) } else { // wifi and mobile internet - fragmentSettingsWifiEnableSwitch.isChecked = true + binding.fragmentSettingsWifiEnableSwitch.isChecked = true val dialogFragment = AllowMobileDataDialogFragment.newInstance() dialogFragment.setTargetFragment(this@SettingsFragment, 0) dialogFragment.showIfNotExists(parentFragmentManager, AllowMobileDataDialogFragment.TAG) @@ -116,67 +119,67 @@ class SettingsFragment : } } - videoQualityView.setOnClickListener { + binding.videoQualityView.setOnClickListener { VideoQualityDialog .newInstance(forPlaying = false) .showIfNotExists(childFragmentManager, VideoQualityDialog.TAG) } - videoPlayingQualityView.setOnClickListener { + binding.videoPlayingQualityView.setOnClickListener { VideoQualityDialog .newInstance(forPlaying = true) .showIfNotExists(childFragmentManager, VideoQualityDialog.TAG) } - storageManagementButton.setOnClickListener { screenManager.showStorageManagement(activity) } + binding.storageManagementButton.setOnClickListener { screenManager.showStorageManagement(activity) } - langWidgetActionButton.setOnClickListener { + binding.langWidgetActionButton.setOnClickListener { CoursesLangDialogFragment .newInstance() .showIfNotExists(childFragmentManager, CoursesLangDialogFragment.TAG) } - nightModeSettingsButton.setOnClickListener { + binding.nightModeSettingsButton.setOnClickListener { NightModeSettingDialogFragment .newInstance() .showIfNotExists(childFragmentManager, NightModeSettingDialogFragment.TAG) } - fontSizeSettingsButton.setOnClickListener { + binding.fontSizeSettingsButton.setOnClickListener { ChooseFontSizeDialogFragment .newInstance() .showIfNotExists(childFragmentManager, ChooseFontSizeDialogFragment.TAG) } - downloadsSettingsButton.setOnClickListener { + binding.downloadsSettingsButton.setOnClickListener { analytic.reportEvent(Analytic.Screens.USER_OPEN_DOWNLOADS) screenManager.showDownloads(requireContext()) } - contactSupportButton.setOnClickListener { + binding.contactSupportButton.setOnClickListener { presenter.contactSupport( getString(R.string.feedback_subject), DeviceInfoUtil.getInfosAboutDevice(context, "\n") ) } - helpCenterButton.setOnClickListener { + binding.helpCenterButton.setOnClickListener { InAppWebViewDialogFragment .newInstance(getString(R.string.settings_help_center), getString(R.string.settings_help_center_url)) .showIfNotExists(childFragmentManager, InAppWebViewDialogFragment.TAG) } - feedbackSettingsButton.setOnClickListener { + binding.feedbackSettingsButton.setOnClickListener { analytic.reportEvent(Analytic.Screens.USER_OPEN_FEEDBACK) screenManager.openFeedbackActivity(requireActivity()) } - aboutSettingsButton.setOnClickListener { + binding.aboutSettingsButton.setOnClickListener { analytic.reportEvent(Analytic.Screens.USER_OPEN_ABOUT_APP) screenManager.openAboutActivity(requireActivity()) } - deleteAccountButton.setOnClickListener { + binding.deleteAccountButton.setOnClickListener { analytic.reportAmplitudeEvent(AmplitudeAnalytic.Settings.DELETE_ACCOUNT_CLICKED) InAppWebViewDialogFragment .newInstance( @@ -187,7 +190,7 @@ class SettingsFragment : .showIfNotExists(childFragmentManager, InAppWebViewDialogFragment.TAG) } - logoutSettingsButton.setOnClickListener { + binding.logoutSettingsButton.setOnClickListener { val supportFragmentManager = activity ?.supportFragmentManager ?: return@setOnClickListener @@ -217,13 +220,13 @@ class SettingsFragment : } override fun onDestroyView() { - fragmentSettingsKeepScreenOnSwitch.setOnCheckedChangeListener(null) - fragmentSettingsDiscountingPolicySwitch.setOnCheckedChangeListener(null) - fragmentSettingsCalendarWidgetSwitch.setOnCheckedChangeListener(null) - fragmentSettingsWifiEnableSwitch.setOnCheckedChangeListener(null) - fragmentSettingsExternalPlayerSwitch.setOnCheckedChangeListener(null) - storageManagementButton.setOnClickListener(null) - notificationActionButton.setOnClickListener(null) + binding.fragmentSettingsKeepScreenOnSwitch.setOnCheckedChangeListener(null) + binding.fragmentSettingsDiscountingPolicySwitch.setOnCheckedChangeListener(null) + binding.fragmentSettingsCalendarWidgetSwitch.setOnCheckedChangeListener(null) + binding.fragmentSettingsWifiEnableSwitch.setOnCheckedChangeListener(null) + binding.fragmentSettingsExternalPlayerSwitch.setOnCheckedChangeListener(null) + binding.storageManagementButton.setOnClickListener(null) + binding.notificationActionButton.setOnClickListener(null) super.onDestroyView() } @@ -232,7 +235,7 @@ class SettingsFragment : } override fun onMobileDataStateChanged(isMobileAllowed: Boolean) { - fragmentSettingsWifiEnableSwitch.isChecked = !isMobileAllowed + binding.fragmentSettingsWifiEnableSwitch.isChecked = !isMobileAllowed storeMobileState(isMobileAllowed) } @@ -273,4 +276,4 @@ class SettingsFragment : interface SignOutListener { fun onSignOut() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/solutions/ui/activity/SolutionsActivity.kt b/app/src/main/java/org/stepik/android/view/solutions/ui/activity/SolutionsActivity.kt index e8dcd9ad67..33a53968e9 100644 --- a/app/src/main/java/org/stepik/android/view/solutions/ui/activity/SolutionsActivity.kt +++ b/app/src/main/java/org/stepik/android/view/solutions/ui/activity/SolutionsActivity.kt @@ -13,15 +13,12 @@ import androidx.fragment.app.DialogFragment import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.activity_solutions.* -import kotlinx.android.synthetic.main.empty_default.* -import kotlinx.android.synthetic.main.error_no_connection_with_button.* -import kotlinx.android.synthetic.main.progress_bar_on_empty_screen.* -import kotlinx.android.synthetic.main.view_centered_toolbar.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.base.App import org.stepic.droid.base.FragmentActivityBase +import org.stepic.droid.databinding.ActivitySolutionsBinding import org.stepic.droid.ui.dialogs.LoadingProgressDialogFragment import org.stepic.droid.ui.util.initCenteredToolbar import org.stepic.droid.util.ProgressHelper @@ -73,6 +70,8 @@ class SolutionsActivity : FragmentActivityBase(), SolutionsView, RemoveSolutions private val progressDialogFragment: DialogFragment = LoadingProgressDialogFragment.newInstance() + private val binding: ActivitySolutionsBinding by viewBinding(ActivitySolutionsBinding::bind) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_solutions) @@ -91,7 +90,7 @@ class SolutionsActivity : FragmentActivityBase(), SolutionsView, RemoveSolutions onItemClick = ::handleSubmissionItemClick ) - with(solutionsRecycler) { + with(binding.solutionsRecycler) { itemAnimator = null adapter = solutionsAdapter layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) @@ -107,10 +106,10 @@ class SolutionsActivity : FragmentActivityBase(), SolutionsView, RemoveSolutions evaluationDrawable.addFrame(getDrawableCompat(R.drawable.ic_step_quiz_evaluation_frame_3), EVALUATION_FRAME_DURATION_MS) evaluationDrawable.isOneShot = false - solutionsSubmitFeedback.setCompoundDrawablesWithIntrinsicBounds(evaluationDrawable, null, null, null) + binding.solutionsSubmitFeedback.setCompoundDrawablesWithIntrinsicBounds(evaluationDrawable, null, null, null) evaluationDrawable.start() - solutionsSubmitButton.setOnClickListener { + binding.solutionsSubmitButton.setOnClickListener { if (fetchSelectedSubmissionItems().isEmpty()) { for (index in solutionsAdapter.items.indices) { selectionHelper.select(index) @@ -122,7 +121,7 @@ class SolutionsActivity : FragmentActivityBase(), SolutionsView, RemoveSolutions initViewStateDelegate() solutionsPresenter.fetchSolutionItems(localOnly = true) - tryAgain.setOnClickListener { solutionsPresenter.fetchSolutionItems(localOnly = false) } + binding.solutionsError.tryAgain.setOnClickListener { solutionsPresenter.fetchSolutionItems(localOnly = false) } if (savedInstanceState != null && savedInstanceState.containsKey(CHECKED_ITEMS_ARGUMENT)) { checkedIndices.addAll(savedInstanceState.getIntegerArrayList(CHECKED_ITEMS_ARGUMENT) as ArrayList) @@ -162,11 +161,11 @@ class SolutionsActivity : FragmentActivityBase(), SolutionsView, RemoveSolutions val selectedCount = fetchSelectedSubmissionItems().size menu.findItem(R.id.attempts_menu_item_delete).isVisible = selectedCount != 0 && isDeleteMenuItemVisible if (selectedCount == 0) { - centeredToolbarTitle.text = getString(R.string.solutions_toolbar_title) - solutionsSubmitButton.text = getString(R.string.solutions_submit_all) + binding.solutionsAppbar.centeredToolbarContainer.centeredToolbarTitle.text = getString(R.string.solutions_toolbar_title) + binding.solutionsSubmitButton.text = getString(R.string.solutions_submit_all) } else { - centeredToolbarTitle.text = getString(R.string.solutions_selected, selectedCount) - solutionsSubmitButton.text = resources.getQuantityString(R.plurals.submit_solutions, selectedCount, selectedCount) + binding.solutionsAppbar.centeredToolbarContainer.centeredToolbarTitle.text = getString(R.string.solutions_selected, selectedCount) + binding.solutionsSubmitButton.text = resources.getQuantityString(R.plurals.submit_solutions, selectedCount, selectedCount) } return super.onPrepareOptionsMenu(menu) } @@ -189,13 +188,13 @@ class SolutionsActivity : FragmentActivityBase(), SolutionsView, RemoveSolutions private fun initViewStateDelegate() { viewStateDelegate.addState() - viewStateDelegate.addState(loadProgressbarOnEmptyScreen) - viewStateDelegate.addState(report_empty) - viewStateDelegate.addState(error) + viewStateDelegate.addState(binding.solutionsLoadingProgress.root) + viewStateDelegate.addState(binding.solutionsEmpty.root) + viewStateDelegate.addState(binding.solutionsError.root) viewStateDelegate.addState( - solutionsRecycler, - solutionsSubmissionSeparator, - solutionsSubmitButton + binding.solutionsRecycler, + binding.solutionsSubmissionSeparator.root, + binding.solutionsSubmitButton ) } @@ -206,8 +205,8 @@ class SolutionsActivity : FragmentActivityBase(), SolutionsView, RemoveSolutions if (state is SolutionsView.State.SolutionsLoaded) { solutionsAdapter.items = listOf(SolutionItem.Disclaimer) + state.solutions - solutionsSubmitButton.isEnabled = !state.isSending && hasSubmissionItemsToSend() - solutionsSubmitFeedback.isVisible = state.isSending + binding.solutionsSubmitButton.isEnabled = !state.isSending && hasSubmissionItemsToSend() + binding.solutionsSubmitFeedback.isVisible = state.isSending if (checkedIndices.isNotEmpty()) { checkedIndices.forEach { selectionHelper.select(it) } } @@ -217,7 +216,7 @@ class SolutionsActivity : FragmentActivityBase(), SolutionsView, RemoveSolutions } override fun showNetworkError() { - root.snackbar(messageRes = R.string.no_connection) + binding.root.snackbar(messageRes = R.string.no_connection) } override fun setBlockingLoading(isLoading: Boolean) { @@ -404,4 +403,4 @@ class SolutionsActivity : FragmentActivityBase(), SolutionsView, RemoveSolutions .filterIndexed { index, _ -> selectionHelper.isSelected(index) } .filterIsInstance() .filter { it.submission.status == Submission.Status.LOCAL } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/solutions/ui/adapter/delegate/SolutionLessonAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/solutions/ui/adapter/delegate/SolutionLessonAdapterDelegate.kt index 6231c0f630..85fca59d5c 100644 --- a/app/src/main/java/org/stepik/android/view/solutions/ui/adapter/delegate/SolutionLessonAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/solutions/ui/adapter/delegate/SolutionLessonAdapterDelegate.kt @@ -2,8 +2,9 @@ package org.stepik.android.view.solutions.ui.adapter.delegate import android.view.View import android.view.ViewGroup -import kotlinx.android.synthetic.main.item_solution_lesson.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemSolutionLessonBinding import org.stepik.android.domain.solutions.model.SolutionItem import ru.nobird.android.ui.adapterdelegates.AdapterDelegate import ru.nobird.android.ui.adapterdelegates.DelegateViewHolder @@ -20,22 +21,20 @@ class SolutionLessonAdapterDelegate( ViewHolder(createView(parent, R.layout.item_solution_lesson)) private inner class ViewHolder(root: View) : DelegateViewHolder(root) { - - private val lessonTitle = root.lessonTitle - private val lessonCheckBox = root.lessonCheckBox + private val viewBinding: ItemSolutionLessonBinding by viewBinding { ItemSolutionLessonBinding.bind(root) } init { - lessonCheckBox.setOnClickListener { (itemData as? SolutionItem.LessonItem)?.let(onClick) } + viewBinding.lessonCheckBox.setOnClickListener { (itemData as? SolutionItem.LessonItem)?.let(onClick) } } override fun onBind(data: SolutionItem) { data as SolutionItem.LessonItem selectionHelper.isSelected(adapterPosition).let { isSelected -> itemView.isSelected = isSelected - lessonCheckBox.isChecked = isSelected + viewBinding.lessonCheckBox.isChecked = isSelected } - lessonCheckBox.isEnabled = data.isEnabled - lessonTitle.text = context.getString( + viewBinding.lessonCheckBox.isEnabled = data.isEnabled + viewBinding.lessonTitle.text = context.getString( R.string.solutions_lesson_placeholder, data.section.position, data.unit.position, @@ -43,4 +42,4 @@ class SolutionLessonAdapterDelegate( ) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/solutions/ui/adapter/delegate/SolutionSectionAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/solutions/ui/adapter/delegate/SolutionSectionAdapterDelegate.kt index 149c37b17e..847c85c518 100644 --- a/app/src/main/java/org/stepik/android/view/solutions/ui/adapter/delegate/SolutionSectionAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/solutions/ui/adapter/delegate/SolutionSectionAdapterDelegate.kt @@ -2,8 +2,9 @@ package org.stepik.android.view.solutions.ui.adapter.delegate import android.view.View import android.view.ViewGroup -import kotlinx.android.synthetic.main.item_solution_section.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemSolutionSectionBinding import org.stepik.android.domain.solutions.model.SolutionItem import ru.nobird.android.ui.adapterdelegates.AdapterDelegate import ru.nobird.android.ui.adapterdelegates.DelegateViewHolder @@ -20,26 +21,24 @@ class SolutionSectionAdapterDelegate( ViewHolder(createView(parent, R.layout.item_solution_section)) private inner class ViewHolder(root: View) : DelegateViewHolder(root) { - - private val sectionTitle = root.sectionTitle - private val sectionCheckBox = root.sectionCheckBox + private val viewBinding: ItemSolutionSectionBinding by viewBinding { ItemSolutionSectionBinding.bind(root) } init { - sectionCheckBox.setOnClickListener { (itemData as? SolutionItem.SectionItem)?.let(onClick) } + viewBinding.sectionCheckBox.setOnClickListener { (itemData as? SolutionItem.SectionItem)?.let(onClick) } } override fun onBind(data: SolutionItem) { data as SolutionItem.SectionItem selectionHelper.isSelected(adapterPosition).let { isSelected -> itemView.isSelected = isSelected - sectionCheckBox.isChecked = isSelected + viewBinding.sectionCheckBox.isChecked = isSelected } - sectionCheckBox.isEnabled = data.isEnabled - sectionTitle.text = context.resources.getString( + viewBinding.sectionCheckBox.isEnabled = data.isEnabled + viewBinding.sectionTitle.text = context.resources.getString( R.string.solutions_section_placeholder, data.section.position, data.section.title ) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/solutions/ui/adapter/delegate/SolutionSubmissionAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/solutions/ui/adapter/delegate/SolutionSubmissionAdapterDelegate.kt index 31bda6eade..52f79b6228 100644 --- a/app/src/main/java/org/stepik/android/view/solutions/ui/adapter/delegate/SolutionSubmissionAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/solutions/ui/adapter/delegate/SolutionSubmissionAdapterDelegate.kt @@ -4,8 +4,9 @@ import android.view.View import android.view.ViewGroup import androidx.core.content.ContextCompat import androidx.core.text.HtmlCompat -import kotlinx.android.synthetic.main.item_solution_submission.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemSolutionSubmissionBinding import org.stepic.droid.util.AppConstants import org.stepic.droid.util.DateTimeHelper import org.stepic.droid.util.resolveResourceIdAttribute @@ -28,19 +29,11 @@ class SolutionSubmissionAdapterDelegate( ViewHolder(createView(parent, R.layout.item_solution_submission)) private inner class ViewHolder(root: View) : DelegateViewHolder(root) { - - private val submissionRoot = root - private val submissionQuizIcon = root.submissionQuizIcon - private val submissionTitle = root.submissionTitle - private val submissionStep = root.submissionStep - private val submissionCheckBox = root.submissionCheckBox - private val submissionStatusIconWrong = root.submissionStatusIconWrong - private val submissionStatusIconCorrect = root.submissionStatusIconCorrect - private val submissionStatusText = root.submissionStatusText + private val viewBinding: ItemSolutionSubmissionBinding by viewBinding { ItemSolutionSubmissionBinding.bind(root) } init { root.setOnClickListener { (itemData as? SolutionItem.SubmissionItem)?.let(onItemClick) } - submissionCheckBox.setOnClickListener { + viewBinding.submissionCheckBox.setOnClickListener { (itemData as? SolutionItem.SubmissionItem)?.let(onCheckboxClick) } } @@ -50,11 +43,11 @@ class SolutionSubmissionAdapterDelegate( selectionHelper.isSelected(adapterPosition).let { isSelected -> itemView.isSelected = isSelected - submissionCheckBox.isChecked = isSelected + viewBinding.submissionCheckBox.isChecked = isSelected } - submissionRoot.isEnabled = data.isEnabled - submissionCheckBox.isEnabled = data.isEnabled + itemView.isEnabled = data.isEnabled + viewBinding.submissionCheckBox.isEnabled = data.isEnabled val resourceId = when (data.step.block?.name) { @@ -64,9 +57,9 @@ class SolutionSubmissionAdapterDelegate( R.drawable.ic_easy_quiz } - submissionQuizIcon.setImageResource(resourceId) - submissionTitle.text = HtmlCompat.fromHtml(data.step.block?.text ?: "", HtmlCompat.FROM_HTML_MODE_LEGACY).toString() - submissionStep.text = + viewBinding.submissionQuizIcon.setImageResource(resourceId) + viewBinding.submissionTitle.text = HtmlCompat.fromHtml(data.step.block?.text ?: "", HtmlCompat.FROM_HTML_MODE_LEGACY).toString() + viewBinding.submissionStep.text = context.resources.getString( R.string.solutions_submission_step_position, data.step.position, @@ -75,31 +68,31 @@ class SolutionSubmissionAdapterDelegate( when (data.submission.status) { Submission.Status.CORRECT -> { - submissionRoot.setBackgroundResource(R.drawable.bg_attempt_submission_correct_item) - submissionStatusText.text = context.getString(R.string.solutions_submission_correctly) - submissionStatusText.setTextColor(ContextCompat.getColor(context, R.color.submission_correct)) - submissionStatusText.visibility = View.VISIBLE - submissionStatusIconCorrect.visibility = View.VISIBLE - submissionStatusIconWrong.visibility = View.GONE - submissionCheckBox.visibility = View.INVISIBLE + itemView.setBackgroundResource(R.drawable.bg_attempt_submission_correct_item) + viewBinding.submissionStatusText.text = context.getString(R.string.solutions_submission_correctly) + viewBinding.submissionStatusText.setTextColor(ContextCompat.getColor(context, R.color.submission_correct)) + viewBinding.submissionStatusText.visibility = View.VISIBLE + viewBinding.submissionStatusIconCorrect.visibility = View.VISIBLE + viewBinding.submissionStatusIconWrong.visibility = View.GONE + viewBinding.submissionCheckBox.visibility = View.INVISIBLE } Submission.Status.WRONG -> { - submissionRoot.setBackgroundResource(R.drawable.bg_attempt_submission_incorrect_item) - submissionStatusText.text = context.getString(R.string.solutions_submission_incorrectly) - submissionStatusText.setTextColor(ContextCompat.getColor(context, R.color.submission_incorrect)) - submissionStatusText.visibility = View.VISIBLE - submissionStatusIconCorrect.visibility = View.GONE - submissionStatusIconWrong.visibility = View.VISIBLE - submissionCheckBox.visibility = View.INVISIBLE + itemView.setBackgroundResource(R.drawable.bg_attempt_submission_incorrect_item) + viewBinding.submissionStatusText.text = context.getString(R.string.solutions_submission_incorrectly) + viewBinding.submissionStatusText.setTextColor(ContextCompat.getColor(context, R.color.submission_incorrect)) + viewBinding.submissionStatusText.visibility = View.VISIBLE + viewBinding.submissionStatusIconCorrect.visibility = View.GONE + viewBinding.submissionStatusIconWrong.visibility = View.VISIBLE + viewBinding.submissionCheckBox.visibility = View.INVISIBLE } else -> { - submissionRoot.setBackgroundResource(context.resolveResourceIdAttribute(R.attr.selectableItemBackground)) - submissionStatusText.visibility = View.GONE - submissionStatusIconCorrect.visibility = View.GONE - submissionStatusIconWrong.visibility = View.GONE - submissionCheckBox.visibility = View.VISIBLE + itemView.setBackgroundResource(context.resolveResourceIdAttribute(R.attr.selectableItemBackground)) + viewBinding.submissionStatusText.visibility = View.GONE + viewBinding.submissionStatusIconCorrect.visibility = View.GONE + viewBinding.submissionStatusIconWrong.visibility = View.GONE + viewBinding.submissionCheckBox.visibility = View.VISIBLE } } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step/model/SectionUnavailableAction.kt b/app/src/main/java/org/stepik/android/view/step/model/SectionUnavailableAction.kt index 03ca29c059..abf9f38643 100644 --- a/app/src/main/java/org/stepik/android/view/step/model/SectionUnavailableAction.kt +++ b/app/src/main/java/org/stepik/android/view/step/model/SectionUnavailableAction.kt @@ -1,7 +1,7 @@ package org.stepik.android.view.step.model import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import org.stepik.android.model.Lesson import org.stepik.android.model.Section import org.stepik.android.view.course_content.model.RequiredSection diff --git a/app/src/main/java/org/stepik/android/view/step/model/StepNavigationAction.kt b/app/src/main/java/org/stepik/android/view/step/model/StepNavigationAction.kt index 9a995edcfa..0813eb3711 100644 --- a/app/src/main/java/org/stepik/android/view/step/model/StepNavigationAction.kt +++ b/app/src/main/java/org/stepik/android/view/step/model/StepNavigationAction.kt @@ -1,7 +1,7 @@ package org.stepik.android.view.step.model import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import org.stepik.android.domain.lesson.model.LessonData import org.stepik.android.domain.step.model.StepNavigationDirection import org.stepik.android.model.Course diff --git a/app/src/main/java/org/stepik/android/view/step/ui/delegate/StepDiscussionsDelegate.kt b/app/src/main/java/org/stepik/android/view/step/ui/delegate/StepDiscussionsDelegate.kt index bab1927a96..124e682f6b 100644 --- a/app/src/main/java/org/stepik/android/view/step/ui/delegate/StepDiscussionsDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step/ui/delegate/StepDiscussionsDelegate.kt @@ -1,20 +1,20 @@ package org.stepik.android.view.step.ui.delegate -import android.view.View import androidx.core.view.isVisible -import kotlinx.android.synthetic.main.fragment_step.view.* -import kotlinx.android.synthetic.main.view_step_discussion.view.* +import org.stepic.droid.databinding.ViewStepDiscussionBinding import org.stepik.android.model.comments.DiscussionThread import org.stepik.android.view.comment.model.DiscussionThreadContainer class StepDiscussionsDelegate( - containerView: View, + discussionBinding: ViewStepDiscussionBinding, + stepSolutionsBinding: ViewStepDiscussionBinding, onDiscussionThreadClicked: (discussionThread: DiscussionThread) -> Unit ) { + private val delegates = mapOf( - DiscussionThread.THREAD_DEFAULT to Delegate(containerView.stepDiscussions, onDiscussionThreadClicked), - DiscussionThread.THREAD_SOLUTIONS to Delegate(containerView.stepSolutions, onDiscussionThreadClicked) + DiscussionThread.THREAD_DEFAULT to Delegate(discussionBinding, onDiscussionThreadClicked), + DiscussionThread.THREAD_SOLUTIONS to Delegate(stepSolutionsBinding, onDiscussionThreadClicked) ) fun setDiscussionThreads(discussionThreads: List) { @@ -24,14 +24,14 @@ class StepDiscussionsDelegate( } private class Delegate( - private val containerView: View, + private val discussionBinding: ViewStepDiscussionBinding, onDiscussionThreadClicked: (discussionThread: DiscussionThread) -> Unit ) { - private val stepDiscussions = containerView.stepDiscussionsCount + private val stepDiscussions = discussionBinding.stepDiscussionsCount private var discussionThread: DiscussionThread? = null init { - containerView.isVisible = false + discussionBinding.root.isVisible = false stepDiscussions.setOnClickListener { discussionThread?.let(onDiscussionThreadClicked) } } @@ -49,7 +49,7 @@ class StepDiscussionsDelegate( setDiscussionThreadData(discussionProxy, discussionsCount, DiscussionThreadContainer.SOLUTIONS) else -> - containerView.isVisible = false + discussionBinding.root.isVisible = false } } @@ -57,17 +57,17 @@ class StepDiscussionsDelegate( stepDiscussions.text = when { discussionProxy == null -> - containerView.context.getString(discussionThreadContainer.disabledStringRes) + discussionBinding.root.context.getString(discussionThreadContainer.disabledStringRes) discussionsCount > 0 -> - containerView.context.getString(discussionThreadContainer.showStringRes, discussionsCount) + discussionBinding.root.context.getString(discussionThreadContainer.showStringRes, discussionsCount) else -> - containerView.context.getString(discussionThreadContainer.writeFirstStringRes) + discussionBinding.root.context.getString(discussionThreadContainer.writeFirstStringRes) } stepDiscussions.setIconResource(if (discussionProxy != null) discussionThreadContainer.containerDrawable else -1) stepDiscussions.isEnabled = discussionProxy != null - containerView.isVisible = true + discussionBinding.root.isVisible = true } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step/ui/delegate/StepNavigationDelegate.kt b/app/src/main/java/org/stepik/android/view/step/ui/delegate/StepNavigationDelegate.kt index bc88203f9e..480c7fe063 100644 --- a/app/src/main/java/org/stepik/android/view/step/ui/delegate/StepNavigationDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step/ui/delegate/StepNavigationDelegate.kt @@ -1,53 +1,52 @@ package org.stepik.android.view.step.ui.delegate import android.view.Gravity -import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible -import kotlinx.android.synthetic.main.view_step_navigation.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.ViewStepNavigationBinding import org.stepik.android.domain.step.model.StepNavigationDirection class StepNavigationDelegate( - private val containerView: View, + private val binding: ViewStepNavigationBinding, private val onDirectionClicked: (StepNavigationDirection) -> Unit ) { - private val nextButton = containerView.stepNavigationNext - private val prevButton = containerView.stepNavigationPrev - init { - containerView.isVisible = false + binding.root.isVisible = false - prevButton.setOnClickListener { onDirectionClicked(StepNavigationDirection.PREV) } - nextButton.setOnClickListener { onDirectionClicked(StepNavigationDirection.NEXT) } + binding.stepNavigationNext.setOnClickListener { onDirectionClicked(StepNavigationDirection.PREV) } + binding.stepNavigationNext.setOnClickListener { onDirectionClicked(StepNavigationDirection.NEXT) } } fun setState(directions: Set) { - containerView.isVisible = directions.isNotEmpty() + binding.root.isVisible = directions.isNotEmpty() val isPrevAvailable = StepNavigationDirection.PREV in directions val isNextAvailable = StepNavigationDirection.NEXT in directions - prevButton.isVisible = isPrevAvailable - nextButton.isVisible = isNextAvailable + binding.stepNavigationNext.isVisible = isPrevAvailable + binding.stepNavigationNext.isVisible = isNextAvailable when { !isPrevAvailable && isNextAvailable -> { - nextButton.gravity = Gravity.CENTER + binding.stepNavigationNext.gravity = Gravity.CENTER } isPrevAvailable && !isNextAvailable -> { - prevButton.setText(R.string.step_navigation_prev) - prevButton.layoutParams = prevButton.layoutParams.apply { width = 0 } - prevButton.compoundDrawablePadding = nextButton.compoundDrawablePadding + binding.stepNavigationNext.setText(R.string.step_navigation_prev) + binding.stepNavigationNext.layoutParams = binding.stepNavigationNext.layoutParams.apply { width = 0 } + binding.stepNavigationNext.compoundDrawablePadding = + binding.stepNavigationNext.compoundDrawablePadding } isPrevAvailable && isNextAvailable -> { - prevButton.text = null - prevButton.layoutParams = prevButton.layoutParams.apply { width = ViewGroup.LayoutParams.WRAP_CONTENT } - prevButton.compoundDrawablePadding = 0 - nextButton.gravity = Gravity.CENTER_VERTICAL or Gravity.START + binding.stepNavigationNext.text = null + binding.stepNavigationNext.layoutParams = binding.stepNavigationNext.layoutParams.apply { + width = ViewGroup.LayoutParams.WRAP_CONTENT + } + binding.stepNavigationNext.compoundDrawablePadding = 0 + binding.stepNavigationNext.gravity = Gravity.CENTER_VERTICAL or Gravity.START } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step/ui/delegate/StepSolutionStatsDelegate.kt b/app/src/main/java/org/stepik/android/view/step/ui/delegate/StepSolutionStatsDelegate.kt index b126b70e79..ff16812d6b 100644 --- a/app/src/main/java/org/stepik/android/view/step/ui/delegate/StepSolutionStatsDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step/ui/delegate/StepSolutionStatsDelegate.kt @@ -1,41 +1,38 @@ package org.stepik.android.view.step.ui.delegate -import android.view.View import androidx.core.text.bold import androidx.core.text.buildSpannedString import androidx.core.view.isVisible -import kotlinx.android.synthetic.main.view_step_solution_stats.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.ViewStepSolutionStatsBinding import org.stepik.android.model.Step class StepSolutionStatsDelegate( - containerView: View, + private val binding: ViewStepSolutionStatsBinding, step: Step, isHasQuiz: Boolean ) { - private val context = containerView.context - private val solvedAmount = containerView.stepAmountPassed - private val solvedPercentage = containerView.stepSolvedPercentage + private val context = binding.root.context init { val correctPercentage = step.correctRatio?.let { (it * 100).toInt() } ?: 0 if (isHasQuiz && correctPercentage > 0) { - containerView.isVisible = true + binding.root.isVisible = true - solvedAmount.text = buildSpannedString { + binding.stepAmountPassed.text = buildSpannedString { append(context.resources.getString(R.string.step_amount_passed)) bold { append(step.passedBy.toString()) } } - solvedPercentage.text = buildSpannedString { + binding.stepSolvedPercentage.text = buildSpannedString { append(context.resources.getString(R.string.step_correct_submissions_percentage)) bold { append(context.resources.getString(R.string.percent_symbol, correctPercentage)) } } } else { - containerView.isVisible = false + binding.root.isVisible = false } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step/ui/fragment/StepFragment.kt b/app/src/main/java/org/stepik/android/view/step/ui/fragment/StepFragment.kt index df21871a69..b188c0cdf9 100644 --- a/app/src/main/java/org/stepik/android/view/step/ui/fragment/StepFragment.kt +++ b/app/src/main/java/org/stepik/android/view/step/ui/fragment/StepFragment.kt @@ -19,17 +19,16 @@ import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.snackbar.Snackbar import com.google.firebase.remoteconfig.FirebaseRemoteConfig -import kotlinx.android.synthetic.main.fragment_step.* -import kotlinx.android.synthetic.main.view_step_disabled.view.* -import kotlinx.android.synthetic.main.view_step_quiz_error.* import org.stepic.droid.R import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.configuration.EndpointResolver import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.FragmentStepBinding import org.stepic.droid.persistence.model.StepPersistentWrapper import org.stepic.droid.ui.dialogs.LoadingProgressDialogFragment import org.stepic.droid.ui.dialogs.StepShareDialogFragment @@ -122,6 +121,8 @@ class StepFragment : Fragment(R.layout.fragment_step), StepView, private val progressDialogFragment: DialogFragment = LoadingProgressDialogFragment.newInstance() + private val stepBinding: FragmentStepBinding by viewBinding(FragmentStepBinding::bind) + override fun onCreate(savedInstanceState: Bundle?) { injectComponent() @@ -140,14 +141,19 @@ class StepFragment : Fragment(R.layout.fragment_step), StepView, override fun onViewCreated(view: View, savedInstanceState: Bundle?) { stepSolutionStatsDelegate = StepSolutionStatsDelegate( - stepSolutionStats, + stepBinding.stepSolutionStats, stepWrapper.step, stepWrapper.isStepCanHaveQuiz ) - stepNavigationDelegate = StepNavigationDelegate(stepNavigation) { stepPresenter.onStepDirectionClicked(it) } + stepNavigationDelegate = StepNavigationDelegate(stepBinding.stepNavigation) { + stepPresenter.onStepDirectionClicked(it) + } - stepDiscussionsDelegate = StepDiscussionsDelegate(view) { discussionThread -> + stepDiscussionsDelegate = StepDiscussionsDelegate( + stepBinding.stepDiscussions, + stepSolutionsBinding = stepBinding.stepSolutions + ) { discussionThread -> analytic.reportAmplitudeEvent( AmplitudeAnalytic.Discussions.SCREEN_OPENED, mapOf(AmplitudeAnalytic.Discussions.Params.SOURCE to AmplitudeAnalytic.Discussions.Values.DEFAULT) @@ -163,9 +169,9 @@ class StepFragment : Fragment(R.layout.fragment_step), StepView, ) } - stepContentNext.isVisible = isStepContentNextVisible(stepWrapper, lessonData) - stepContentNext.setOnClickListener { move() } - stepStatusTryAgain.setOnClickListener { stepPresenter.fetchStepUpdate(stepWrapper.step.id) } + stepBinding.stepContentNext.isVisible = isStepContentNextVisible(stepWrapper, lessonData) + stepBinding.stepContentNext.setOnClickListener { move() } + stepBinding.stepQuizError.stepStatusTryAgain.setOnClickListener { stepPresenter.fetchStepUpdate(stepWrapper.step.id) } initDisabledStep() initDisabledStepTeacher() @@ -187,7 +193,7 @@ class StepFragment : Fragment(R.layout.fragment_step), StepView, .copyTextToClipboard(textToCopy = stepUri, toastMessage = getString(R.string.link_copied_title)) } } - val placeholderMessage = stepDisabled.placeholderMessage + val placeholderMessage = stepBinding.stepDisabled.placeholderMessage placeholderMessage.text = buildSpannedString { append(getString(R.string.step_disabled_student_description_part_1)) @@ -246,7 +252,7 @@ class StepFragment : Fragment(R.layout.fragment_step), StepView, "" } - val placeholderMessage = stepDisabledTeacher.placeholderMessage + val placeholderMessage = stepBinding.stepDisabledTeacher.placeholderMessage placeholderMessage.text = buildSpannedString { append(planDescription1) @@ -261,7 +267,7 @@ class StepFragment : Fragment(R.layout.fragment_step), StepView, } private fun initStepContentFragment() { - stepContentContainer.layoutParams = (stepContentContainer.layoutParams as LinearLayoutCompat.LayoutParams) + stepBinding.stepContentContainer.layoutParams = (stepBinding.stepContentContainer.layoutParams as LinearLayoutCompat.LayoutParams) .apply { if (stepWrapper.isStepCanHaveQuiz) { height = LinearLayout.LayoutParams.WRAP_CONTENT @@ -285,9 +291,9 @@ class StepFragment : Fragment(R.layout.fragment_step), StepView, private fun setStepQuizFragment(isNeedReload: Boolean) { val isStepHasQuiz = stepWrapper.isStepCanHaveQuiz - stepContentSeparator.isVisible = isStepHasQuiz - stepQuizContainer.isVisible = isStepHasQuiz - stepQuizError.isVisible = false + stepBinding.stepContentSeparator.root.isVisible = isStepHasQuiz + stepBinding.stepQuizContainer.isVisible = isStepHasQuiz + stepBinding.stepQuizError.root.isVisible = false if (isStepHasQuiz) { val isQuizFragmentEmpty = childFragmentManager.findFragmentByTag(STEP_QUIZ_FRAGMENT_TAG) == null @@ -379,15 +385,15 @@ class StepFragment : Fragment(R.layout.fragment_step), StepView, val isStepUnavailable = isStepDisabled && !lessonData.lesson.isTeacher - stepContentContainer.isGone = isStepUnavailable - stepContentSeparator.isGone = isStepUnavailable - stepQuizError.isGone = isStepUnavailable - stepQuizContainer.isGone = isStepUnavailable - stepFooter.isGone = isStepUnavailable + stepBinding.stepContentContainer.isGone = isStepUnavailable + stepBinding.stepContentSeparator.root.isGone = isStepUnavailable + stepBinding.stepQuizError.root.isGone = isStepUnavailable + stepBinding.stepQuizContainer.isGone = isStepUnavailable + stepBinding.stepFooter.isGone = isStepUnavailable - stepDisabled.isVisible = isStepUnavailable - stepDisabledTeacher.isVisible = isStepDisabled && lessonData.lesson.isTeacher - stepContentNext.isVisible = isStepContentNextVisible(state.stepWrapper, lessonData) + stepBinding.stepDisabled.root.isVisible = isStepUnavailable + stepBinding.stepDisabledTeacher.root.isVisible = isStepDisabled && lessonData.lesson.isTeacher + stepBinding.stepContentNext.isVisible = isStepContentNextVisible(state.stepWrapper, lessonData) stepWrapper = state.stepWrapper @@ -398,9 +404,9 @@ class StepFragment : Fragment(R.layout.fragment_step), StepView, setStepQuizFragment(isNeedReloadQuiz) Step.Status.PREPARING, Step.Status.ERROR -> { - stepContentSeparator.isVisible = true - stepQuizContainer.isVisible = false - stepQuizError.isVisible = true + stepBinding.stepContentSeparator.root.isVisible = true + stepBinding.stepQuizContainer.isVisible = false + stepBinding.stepQuizError.root.isVisible = true } else -> Unit } @@ -426,15 +432,15 @@ class StepFragment : Fragment(R.layout.fragment_step), StepView, override fun setNavigation(directions: Set) { stepNavigationDelegate.setState(directions) - val actionBottomMargin = if (stepNavigation.visibility == View.VISIBLE) { + val actionBottomMargin = if (stepBinding.stepNavigation.root.visibility == View.VISIBLE) { 0 } else { resources.getDimensionPixelSize(R.dimen.step_quiz_container_bottom_margin) } - stepQuizContainer.updateLayoutParams { + stepBinding.stepQuizContainer.updateLayoutParams { bottomMargin = actionBottomMargin } - stepContentNext.updateLayoutParams { + stepBinding.stepContentNext.updateLayoutParams { bottomMargin = actionBottomMargin } } @@ -504,4 +510,4 @@ class StepFragment : Fragment(R.layout.fragment_step), StepView, override fun showErrorMessage() { view?.snackbar(messageRes = R.string.step_navigation_action_unknown, length = Snackbar.LENGTH_LONG) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_content_video/ui/fragment/VideoStepContentFragment.kt b/app/src/main/java/org/stepik/android/view/step_content_video/ui/fragment/VideoStepContentFragment.kt index b6948fc0e5..488dc860fd 100644 --- a/app/src/main/java/org/stepik/android/view/step_content_video/ui/fragment/VideoStepContentFragment.kt +++ b/app/src/main/java/org/stepik/android/view/step_content_video/ui/fragment/VideoStepContentFragment.kt @@ -8,14 +8,13 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider +import by.kirich1409.viewbindingdelegate.viewBinding import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.fragment_step_content_video.* -import kotlinx.android.synthetic.main.view_course_info_video.* -import kotlinx.android.synthetic.main.view_length_video_thumbnail.* import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.FragmentStepContentVideoBinding import org.stepic.droid.persistence.model.StepPersistentWrapper import org.stepic.droid.ui.util.snackbar import org.stepik.android.domain.lesson.model.LessonData @@ -53,6 +52,8 @@ class VideoStepContentFragment : Fragment(), VideoStepContentView, Playable { private val presenter: VideoStepContentPresenter by viewModels { viewModelFactory } private var stepId: Long by argument() + private val binding: FragmentStepContentVideoBinding by viewBinding(FragmentStepContentVideoBinding::bind) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) injectComponent() @@ -80,15 +81,15 @@ class VideoStepContentFragment : Fragment(), VideoStepContentView, Playable { Glide.with(this) .load(thumbnail) .placeholder(R.drawable.general_placeholder) - .into(videoThumbnail) + .into(binding.videoContainer.videoThumbnail) - videoContainer.setOnClickListener { openVideoPlayer() } + binding.videoContainer.root.setOnClickListener { openVideoPlayer() } } private fun openVideoPlayer() { if (stepWrapper.cachedVideo == null && stepWrapper.step.block?.video == null) { analytic.reportEventWithName(Analytic.Error.ILLEGAL_STATE_VIDEO_STEP_PLAY, stepWrapper.step.id.toString()) - videoStepContent.snackbar(messageRes = R.string.step_content_video_no_video) + binding.root.snackbar(messageRes = R.string.step_content_video_no_video) } else { val thumbnail = stepWrapper.cachedVideo?.thumbnail ?: stepWrapper.step.block?.video?.thumbnail @@ -116,12 +117,12 @@ class VideoStepContentFragment : Fragment(), VideoStepContentView, Playable { val videoLengthText = (state as? VideoStepContentView.State.Loaded) ?.videoLength - videoLength.isVisible = videoLengthText != null - videoLength.text = videoLengthText + binding.videoLengthContainer.videoLength.isVisible = videoLengthText != null + binding.videoLengthContainer.videoLength.text = videoLengthText } override fun play(): Boolean { openVideoPlayer() return true } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz/ui/delegate/StepQuizFeedbackBlocksDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz/ui/delegate/StepQuizFeedbackBlocksDelegate.kt index 4ce79a2b16..6091a5bdc4 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz/ui/delegate/StepQuizFeedbackBlocksDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz/ui/delegate/StepQuizFeedbackBlocksDelegate.kt @@ -9,8 +9,8 @@ import androidx.appcompat.content.res.AppCompatResources import androidx.core.content.ContextCompat import androidx.core.graphics.drawable.DrawableCompat import androidx.core.view.isVisible -import kotlinx.android.synthetic.main.layout_step_quiz_feedback_block.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.LayoutStepQuizFeedbackBlockBinding import org.stepic.droid.ui.util.setTextViewBackgroundWithoutResettingPadding import org.stepik.android.view.step_quiz.model.StepQuizFeedbackState import org.stepik.android.view.ui.delegate.ViewStateDelegate @@ -26,26 +26,27 @@ class StepQuizFeedbackBlocksDelegate( private const val EVALUATION_FRAME_DURATION_MS = 250 } - private val context = containerView.context - private val resources = containerView.resources + private val binding = LayoutStepQuizFeedbackBlockBinding.bind(containerView) + private val context = binding.root.context + private val resources = binding.root.resources - private val stepQuizFeedbackEvaluation = containerView.stepQuizFeedbackEvaluation - private val stepQuizFeedbackCorrect = containerView.stepQuizFeedbackCorrect - private val stepQuizFeedbackPartiallyCorrect = containerView.stepQuizFeedbackPartiallyCorrect - private val stepQuizFeedbackWrong = containerView.stepQuizFeedbackWrong - private val stepQuizFeedbackValidation = containerView.stepQuizFeedbackValidation + private val stepQuizFeedbackEvaluation = binding.stepQuizFeedbackEvaluation + private val stepQuizFeedbackCorrect = binding.stepQuizFeedbackCorrect + private val stepQuizFeedbackPartiallyCorrect = binding.stepQuizFeedbackPartiallyCorrect + private val stepQuizFeedbackWrong = binding.stepQuizFeedbackWrong + private val stepQuizFeedbackValidation = binding.stepQuizFeedbackValidation - private val stepQuizFeedbackHint = containerView.stepQuizFeedbackHint + private val stepQuizFeedbackHint = binding.stepQuizFeedbackHint private val viewStateDelegate = ViewStateDelegate() init { viewStateDelegate.addState() - viewStateDelegate.addState(containerView, stepQuizFeedbackEvaluation) - viewStateDelegate.addState(containerView, stepQuizFeedbackCorrect, stepQuizFeedbackHint) - viewStateDelegate.addState(containerView, stepQuizFeedbackPartiallyCorrect, stepQuizFeedbackHint) - viewStateDelegate.addState(containerView, stepQuizFeedbackWrong, stepQuizFeedbackHint) - viewStateDelegate.addState(containerView, stepQuizFeedbackValidation) + viewStateDelegate.addState(binding.root, stepQuizFeedbackEvaluation) + viewStateDelegate.addState(binding.root, stepQuizFeedbackCorrect, stepQuizFeedbackHint) + viewStateDelegate.addState(binding.root, stepQuizFeedbackPartiallyCorrect, stepQuizFeedbackHint) + viewStateDelegate.addState(binding.root, stepQuizFeedbackWrong, stepQuizFeedbackHint) + viewStateDelegate.addState(binding.root, stepQuizFeedbackValidation) val evaluationDrawable = AnimationDrawable() evaluationDrawable.addFrame(context.getDrawableCompat(R.drawable.ic_step_quiz_evaluation_frame_1), EVALUATION_FRAME_DURATION_MS) @@ -131,4 +132,4 @@ class StepQuizFeedbackBlocksDelegate( } targetView.setTextViewBackgroundWithoutResettingPadding(backgroundShape) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz/ui/factory/StepQuizFormFactory.kt b/app/src/main/java/org/stepik/android/view/step_quiz/ui/factory/StepQuizFormFactory.kt index 48c3509d38..5bd323c66b 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz/ui/factory/StepQuizFormFactory.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz/ui/factory/StepQuizFormFactory.kt @@ -2,11 +2,16 @@ package org.stepik.android.view.step_quiz.ui.factory import android.view.View import androidx.annotation.LayoutRes +import org.stepic.droid.databinding.FragmentStepQuizBinding import org.stepik.android.view.step_quiz.ui.delegate.StepQuizFormDelegate interface StepQuizFormFactory { @LayoutRes fun getLayoutResForStep(blockName: String?): Int - fun getDelegateForStep(blockName: String?, view: View): StepQuizFormDelegate? + fun getDelegateForStep( + blockName: String?, + stepQuizBinding: FragmentStepQuizBinding, + quizView: View + ): StepQuizFormDelegate? } \ No newline at end of file diff --git a/app/src/main/java/org/stepik/android/view/step_quiz/ui/factory/StepQuizViewStateDelegateFactory.kt b/app/src/main/java/org/stepik/android/view/step_quiz/ui/factory/StepQuizViewStateDelegateFactory.kt index d02842f6f4..f6d9c808c0 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz/ui/factory/StepQuizViewStateDelegateFactory.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz/ui/factory/StepQuizViewStateDelegateFactory.kt @@ -1,7 +1,7 @@ package org.stepik.android.view.step_quiz.ui.factory import android.view.View -import kotlinx.android.synthetic.main.fragment_step_quiz.view.* +import org.stepic.droid.databinding.FragmentStepQuizBinding import org.stepik.android.presentation.step_quiz.StepQuizFeature import org.stepik.android.view.ui.delegate.ViewStateDelegate import javax.inject.Inject @@ -9,20 +9,22 @@ import javax.inject.Inject class StepQuizViewStateDelegateFactory @Inject constructor() { - fun create(quizFragmentLayout: View, vararg quizViews: View): ViewStateDelegate = - ViewStateDelegate() + fun create(quizFragmentLayout: View, vararg quizViews: View): ViewStateDelegate { + val binding = FragmentStepQuizBinding.bind(quizFragmentLayout) + return ViewStateDelegate() .apply { addState() - addState(quizFragmentLayout.stepQuizProgress) - addState(quizFragmentLayout.stepQuizProgress) + addState(binding.stepQuizProgress) + addState(binding.stepQuizProgress) addState( - quizFragmentLayout.stepQuizReviewTeacherMessage, - quizFragmentLayout.stepQuizDiscountingPolicy, - quizFragmentLayout.stepQuizFeedbackBlocks, - quizFragmentLayout.stepQuizDescription, - quizFragmentLayout.stepQuizActionContainer, + binding.stepQuizReviewTeacherMessage, + binding.stepQuizDiscountingPolicy, + binding.stepQuizFeedbackBlocks.root, + binding.stepQuizDescription, + binding.stepQuizActionContainer.root, *quizViews ) - addState(quizFragmentLayout.stepQuizNetworkError) + addState(binding.stepQuizNetworkError.root) } + } } diff --git a/app/src/main/java/org/stepik/android/view/step_quiz/ui/fragment/DefaultStepQuizFragment.kt b/app/src/main/java/org/stepik/android/view/step_quiz/ui/fragment/DefaultStepQuizFragment.kt index a2923893ac..44438a92cb 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz/ui/fragment/DefaultStepQuizFragment.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz/ui/fragment/DefaultStepQuizFragment.kt @@ -8,13 +8,12 @@ import androidx.annotation.LayoutRes import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider +import by.kirich1409.viewbindingdelegate.viewBinding import com.jakewharton.rxrelay2.BehaviorRelay -import kotlinx.android.synthetic.main.error_no_connection_with_button_small.view.* -import kotlinx.android.synthetic.main.fragment_step_quiz.* -import kotlinx.android.synthetic.main.view_step_quiz_submit_button.* import org.stepic.droid.R import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.FragmentStepQuizBinding import org.stepic.droid.persistence.model.StepPersistentWrapper import org.stepic.droid.ui.util.snackbar import org.stepik.android.domain.lesson.model.LessonData @@ -37,6 +36,8 @@ import ru.nobird.android.view.base.ui.extension.showIfNotExists import javax.inject.Inject abstract class DefaultStepQuizFragment : Fragment(), ReduxView { + protected val stepQuizBinding: FragmentStepQuizBinding by viewBinding(FragmentStepQuizBinding::bind) + @Inject internal lateinit var viewModelFactory: ViewModelProvider.Factory @@ -63,9 +64,6 @@ abstract class DefaultStepQuizFragment : Fragment(), ReduxView private lateinit var stepQuizDelegate: StepQuizDelegate - protected abstract val quizLayoutRes: Int - @LayoutRes get - protected abstract val quizViews: Array override fun onCreate(savedInstanceState: Bundle?) { @@ -86,7 +84,7 @@ abstract class DefaultStepQuizFragment : Fragment(), ReduxView(root) { - private val itemChoiceContainer = root.itemChoiceContainer - private val itemChoiceCheckmark = root.itemChoiceCheckmark - private val itemChoiceLatex = root.itemChoiceLatex - private val itemChoiceLatexProgress = root.itemChoiceLatexProgress - private val itemChoiceFeedback = root.itemChoiceFeedback + private val viewBinding: ItemStepQuizChoiceBinding by viewBinding { ItemStepQuizChoiceBinding.bind(root) } + private val layerListDrawableDelegate: LayerListDrawableDelegate init { - root.itemChoiceContainer.setOnClickListener { + viewBinding.itemChoiceContainer.setOnClickListener { if (it.isEnabled) { onClick(itemData as Choice) } @@ -54,11 +52,11 @@ class ChoicesAdapterDelegate( R.id.incorrect_layer, R.id.incorrect_layer_with_hint ), - itemChoiceContainer.background.mutate() as LayerDrawable + viewBinding.itemChoiceContainer.background.mutate() as LayerDrawable ) - itemChoiceLatex.webViewClient = ProgressableWebViewClient(itemChoiceLatexProgress, itemChoiceLatex.webView) + viewBinding.itemChoiceLatex.webViewClient = ProgressableWebViewClient(viewBinding.itemChoiceLatexProgress, viewBinding.itemChoiceLatex.webView) - itemChoiceFeedback.background = AppCompatResources + viewBinding.itemChoiceFeedback.background = AppCompatResources .getDrawable(context, R.drawable.bg_shape_rounded_bottom) ?.mutate() ?.let { DrawableCompat.wrap(it) } @@ -69,17 +67,17 @@ class ChoicesAdapterDelegate( } override fun onBind(data: Choice) { - itemView.itemChoiceContainer.isEnabled = data.isEnabled + viewBinding.itemChoiceContainer.isEnabled = data.isEnabled itemView.isSelected = selectionHelper.isSelected(adapterPosition) - itemChoiceCheckmark.isInvisible = data.correct != true - itemChoiceLatex.setText(data.option) + viewBinding.itemChoiceCheckmark.isInvisible = data.correct != true + viewBinding.itemChoiceLatex.setText(data.option) layerListDrawableDelegate.showLayer(getItemBackgroundLayer(data)) bindHint(data) } private fun bindHint(data: Choice) { - itemChoiceFeedback.isVisible = !data.feedback.isNullOrEmpty() - itemChoiceFeedback.setText(data.feedback) + viewBinding.itemChoiceFeedback.isVisible = !data.feedback.isNullOrEmpty() + viewBinding.itemChoiceFeedback.setText(data.feedback) } private fun getItemBackgroundLayer(data: Choice): Int = diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_choice/ui/delegate/ChoiceStepQuizFormDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_choice/ui/delegate/ChoiceStepQuizFormDelegate.kt index cc13cc07cb..bb04290751 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_choice/ui/delegate/ChoiceStepQuizFormDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_choice/ui/delegate/ChoiceStepQuizFormDelegate.kt @@ -1,11 +1,13 @@ package org.stepik.android.view.step_quiz_choice.ui.delegate import android.view.View +import android.widget.TextView import androidx.annotation.StringRes import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.fragment_step_quiz.view.* -import kotlinx.android.synthetic.main.layout_step_quiz_choice.view.* +import androidx.recyclerview.widget.RecyclerView import org.stepic.droid.R +import org.stepic.droid.databinding.FragmentStepQuizBinding +import org.stepic.droid.databinding.LayoutStepQuizChoiceBinding import org.stepik.android.model.Reply import org.stepik.android.presentation.step_quiz.StepQuizFeature import org.stepik.android.presentation.step_quiz.model.ReplyResult @@ -20,18 +22,36 @@ import ru.nobird.android.ui.adapters.selection.SelectionHelper import ru.nobird.android.ui.adapters.selection.SingleChoiceSelectionHelper class ChoiceStepQuizFormDelegate( - containerView: View, + private val quizDescription: TextView, + private val choicesRecycler: RecyclerView, private val onQuizChanged: (ReplyResult) -> Unit ) : StepQuizFormDelegate { - private val context = containerView.context + constructor( + stepQuizBinding: FragmentStepQuizBinding, + choiceStepQuizBinding: LayoutStepQuizChoiceBinding, + onQuizChanged: (ReplyResult) -> Unit + ) : this( + stepQuizBinding.stepQuizDescription, + choiceStepQuizBinding.root, + onQuizChanged + ) - private val quizDescription = containerView.stepQuizDescription + constructor( + containerView: View, + onQuizChanged: (ReplyResult) -> Unit + ) : this( + containerView.findViewById(R.id.stepQuizDescription), + containerView.findViewById(R.id.choicesRecycler), + onQuizChanged + ) + + private val context = choicesRecycler.context private val choiceStepQuizOptionsMapper = ChoiceStepQuizOptionsMapper() private var choicesAdapter: DefaultDelegateAdapter = DefaultDelegateAdapter() private lateinit var selectionHelper: SelectionHelper init { - containerView.choicesRecycler.apply { + choicesRecycler.apply { itemAnimator = null adapter = choicesAdapter layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) @@ -102,4 +122,4 @@ class ChoiceStepQuizFormDelegate( } onQuizChanged(createReply()) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_choice/ui/fragment/ChoiceStepQuizFragment.kt b/app/src/main/java/org/stepik/android/view/step_quiz_choice/ui/fragment/ChoiceStepQuizFragment.kt index f6e61ae688..ffdd8a6fbe 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_choice/ui/fragment/ChoiceStepQuizFragment.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_choice/ui/fragment/ChoiceStepQuizFragment.kt @@ -1,9 +1,10 @@ package org.stepik.android.view.step_quiz_choice.ui.fragment +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import androidx.fragment.app.Fragment -import kotlinx.android.synthetic.main.layout_step_quiz_choice.* -import org.stepic.droid.R +import org.stepic.droid.databinding.LayoutStepQuizChoiceBinding import org.stepik.android.presentation.step_quiz.StepQuizFeature import org.stepik.android.view.step_quiz.ui.delegate.StepQuizFormDelegate import org.stepik.android.view.step_quiz.ui.fragment.DefaultStepQuizFragment @@ -21,12 +22,22 @@ class ChoiceStepQuizFragment : } } - override val quizLayoutRes: Int = - R.layout.layout_step_quiz_choice + private var _binding: LayoutStepQuizChoiceBinding? = null + private val binding get() = _binding!! override val quizViews: Array - get() = arrayOf(choicesRecycler) + get() = arrayOf(binding.root) - override fun createStepQuizFormDelegate(view: View): StepQuizFormDelegate = - ChoiceStepQuizFormDelegate(view, onQuizChanged = ::syncReplyState) -} \ No newline at end of file + override fun createStepView(layoutInflater: LayoutInflater, parent: ViewGroup): View { + return LayoutStepQuizChoiceBinding.inflate(layoutInflater, parent, false).also { + _binding = it + }.root + } + + override fun createStepQuizFormDelegate(): StepQuizFormDelegate = + ChoiceStepQuizFormDelegate( + stepQuizBinding = stepQuizBinding, + choiceStepQuizBinding = binding, + onQuizChanged = ::syncReplyState + ) +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/adapter/delegate/CodeDetailLimitAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/adapter/delegate/CodeDetailLimitAdapterDelegate.kt index 499c03e38e..1758d72261 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/adapter/delegate/CodeDetailLimitAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/adapter/delegate/CodeDetailLimitAdapterDelegate.kt @@ -2,7 +2,7 @@ package org.stepik.android.view.step_quiz_code.ui.adapter.delegate import android.view.View import android.view.ViewGroup -import kotlinx.android.synthetic.main.item_step_quiz_code_detail_limit.view.* +import androidx.appcompat.widget.AppCompatTextView import org.stepic.droid.R import org.stepik.android.view.step_quiz_code.model.CodeDetail import ru.nobird.android.ui.adapterdelegates.AdapterDelegate @@ -16,8 +16,8 @@ class CodeDetailLimitAdapterDelegate : AdapterDelegate(root) { - private val title = root.stepQuizCodeDetailLimitTitle - private val value = root.stepQuizCodeDetailLimitValue + private val title = root.findViewById(R.id.stepQuizCodeDetailLimitTitle) + private val value = root.findViewById(R.id.stepQuizCodeDetailLimitValue) override fun onBind(data: CodeDetail) { data as CodeDetail.Limit diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/adapter/delegate/CodeDetailSampleAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/adapter/delegate/CodeDetailSampleAdapterDelegate.kt index 8f8f5e307a..2a8675349d 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/adapter/delegate/CodeDetailSampleAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/adapter/delegate/CodeDetailSampleAdapterDelegate.kt @@ -4,9 +4,9 @@ import android.view.Gravity import android.view.View import android.view.ViewGroup import androidx.appcompat.content.res.AppCompatResources +import androidx.appcompat.widget.AppCompatTextView import androidx.core.graphics.drawable.DrawableCompat import androidx.core.widget.TextViewCompat -import kotlinx.android.synthetic.main.item_step_quiz_code_detail_sample.view.* import org.stepic.droid.R import org.stepik.android.view.base.ui.drawable.GravityDrawable import org.stepik.android.view.step_quiz_code.model.CodeDetail @@ -21,9 +21,9 @@ class CodeDetailSampleAdapterDelegate : AdapterDelegate(root) { - private val title = root.stepQuizCodeDetailSampleTitle - private val input = root.stepQuizCodeDetailSampleInput - private val output = root.stepQuizCodeDetailSampleOutput + private val title = root.findViewById(R.id.stepQuizCodeDetailSampleTitle) + private val input = root.findViewById(R.id.stepQuizCodeDetailSampleInput) + private val output = root.findViewById(R.id.stepQuizCodeDetailSampleOutput) init { val sampleHeight = context.resources.getDimensionPixelOffset(R.dimen.step_quiz_code_sample_min_height) diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/delegate/CodeLayoutDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/delegate/CodeLayoutDelegate.kt index b8ac767693..08b56b81ed 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/delegate/CodeLayoutDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/delegate/CodeLayoutDelegate.kt @@ -1,23 +1,56 @@ package org.stepik.android.view.step_quiz_code.ui.delegate import android.view.View -import kotlinx.android.synthetic.main.layout_step_quiz_code_fullscreen_playground.view.* +import com.google.android.material.button.MaterialButton import org.stepic.droid.R +import org.stepic.droid.code.ui.CodeEditorLayout +import org.stepic.droid.databinding.LayoutStepQuizCodeBinding import org.stepic.droid.model.code.extensionForLanguage import org.stepic.droid.ui.adapters.CodeToolbarAdapter import org.stepik.android.model.Step -class CodeLayoutDelegate( - codeContainerView: View, +class CodeLayoutDelegate private constructor( + private val codeLayout: CodeEditorLayout, + private val stepQuizActionChangeLang: MaterialButton, private val step: Step, private val codeTemplates: Map, private val codeQuizInstructionDelegate: CodeQuizInstructionDelegate, private var codeToolbarAdapter: CodeToolbarAdapter?, private val onChangeLanguageClicked: () -> Unit ) { + constructor( + codeLayoutBinding: LayoutStepQuizCodeBinding, + step: Step, + codeTemplates: Map, + codeQuizInstructionDelegate: CodeQuizInstructionDelegate, + codeToolbarAdapter: CodeToolbarAdapter?, + onChangeLanguageClicked: () -> Unit + ) : this( + codeLayoutBinding.codeStepLayout, + codeLayoutBinding.stepQuizActionChangeLang, + step, + codeTemplates, + codeQuizInstructionDelegate, + codeToolbarAdapter, + onChangeLanguageClicked + ) - private val codeLayout = codeContainerView.codeStepLayout - private val stepQuizActionChangeLang = codeContainerView.stepQuizActionChangeLang + constructor( + codeContainerView: View, + step: Step, + codeTemplates: Map, + codeQuizInstructionDelegate: CodeQuizInstructionDelegate, + codeToolbarAdapter: CodeToolbarAdapter?, + onChangeLanguageClicked: () -> Unit + ) : this( + codeContainerView.findViewById(R.id.codeStepLayout), + codeContainerView.findViewById(R.id.stepQuizActionChangeLang), + step, + codeTemplates, + codeQuizInstructionDelegate, + codeToolbarAdapter, + onChangeLanguageClicked + ) init { /** @@ -50,4 +83,4 @@ class CodeLayoutDelegate( codeLayout.isEnabled = isEnabled stepQuizActionChangeLang.isEnabled = isEnabled } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/delegate/CodeQuizInstructionDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/delegate/CodeQuizInstructionDelegate.kt index 3ffebd986f..f96919fb31 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/delegate/CodeQuizInstructionDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/delegate/CodeQuizInstructionDelegate.kt @@ -5,9 +5,11 @@ import androidx.appcompat.content.res.AppCompatResources import androidx.core.view.isVisible import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.layout_step_quiz_code.view.* +import androidx.recyclerview.widget.RecyclerView import org.stepic.droid.R +import org.stepic.droid.databinding.LayoutStepQuizCodeBinding import org.stepic.droid.model.code.ProgrammingLanguage +import org.stepic.droid.ui.custom.ArrowImageView import org.stepic.droid.ui.util.collapse import org.stepic.droid.ui.util.expand import org.stepik.android.model.Step @@ -17,14 +19,31 @@ import org.stepik.android.view.step_quiz_code.ui.adapter.delegate.CodeDetailLimi import org.stepik.android.view.step_quiz_code.ui.adapter.delegate.CodeDetailSampleAdapterDelegate import ru.nobird.android.ui.adapters.DefaultDelegateAdapter -class CodeQuizInstructionDelegate( - detailsContainerView: View, +class CodeQuizInstructionDelegate private constructor( + private val stepQuizCodeDetails: View, + private val stepQuizCodeDetailsContent: RecyclerView, + private val stepQuizCodeDetailsArrow: ArrowImageView?, isCollapseable: Boolean ) { + constructor( + codeLayoutBinding: LayoutStepQuizCodeBinding, + isCollapseable: Boolean + ) : this( + codeLayoutBinding.stepQuizCodeDetails, + codeLayoutBinding.stepQuizCodeDetailsContent, + codeLayoutBinding.stepQuizCodeDetailsArrow, + isCollapseable + ) - private val stepQuizCodeDetails = detailsContainerView.stepQuizCodeDetails - private val stepQuizCodeDetailsArrow = detailsContainerView.stepQuizCodeDetailsArrow - private val stepQuizCodeDetailsContent = detailsContainerView.stepQuizCodeDetailsContent + constructor( + detailsContainerView: View, + isCollapseable: Boolean + ) : this( + detailsContainerView.findViewById(R.id.stepQuizCodeDetails), + detailsContainerView.findViewById(R.id.stepQuizCodeDetailsContent), + detailsContainerView.findViewById(R.id.stepQuizCodeDetailsArrow), + isCollapseable + ) private val stepQuizCodeDetailsAdapter = DefaultDelegateAdapter() private val codeStepQuizDetailsMapper = CodeStepQuizDetailsMapper() @@ -45,6 +64,7 @@ class CodeQuizInstructionDelegate( if (isCollapseable) { stepQuizCodeDetails.setOnClickListener { + val stepQuizCodeDetailsArrow = stepQuizCodeDetailsArrow ?: return@setOnClickListener stepQuizCodeDetailsArrow.changeState() if (stepQuizCodeDetailsArrow.isExpanded()) { stepQuizCodeDetailsContent.expand() @@ -65,4 +85,4 @@ class CodeQuizInstructionDelegate( stepQuizCodeDetails.isVisible = stepQuizCodeDetailsAdapter.items.isNotEmpty() } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/delegate/CodeStepQuizFormDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/delegate/CodeStepQuizFormDelegate.kt index d1d4b26e7e..9818fbbb75 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/delegate/CodeStepQuizFormDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/delegate/CodeStepQuizFormDelegate.kt @@ -3,10 +3,8 @@ package org.stepik.android.view.step_quiz_code.ui.delegate import android.view.View import androidx.core.widget.doAfterTextChanged import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.layout_step_quiz_code.view.* -import kotlinx.android.synthetic.main.layout_step_quiz_code_fullscreen_playground.view.codeStepLayout -import kotlinx.android.synthetic.main.layout_step_quiz_code_fullscreen_playground.view.stepQuizActions import org.stepic.droid.R +import org.stepic.droid.databinding.LayoutStepQuizCodeBinding import org.stepic.droid.ui.util.setCompoundDrawables import org.stepik.android.model.Reply import org.stepik.android.model.code.CodeOptions @@ -21,13 +19,31 @@ import org.stepik.android.view.ui.delegate.ViewStateDelegate import ru.nobird.android.ui.adapters.DefaultDelegateAdapter class CodeStepQuizFormDelegate( - containerView: View, + codeStepQuizBinding: LayoutStepQuizCodeBinding, private val codeOptions: CodeOptions, private val codeLayoutDelegate: CodeLayoutDelegate, private val onFullscreenClicked: (lang: String, code: String) -> Unit, private val syncCodePreference: (String) -> Unit, private val onQuizChanged: (ReplyResult) -> Unit ) : StepQuizFormDelegate { + constructor( + containerView: View, + codeOptions: CodeOptions, + codeLayoutDelegate: CodeLayoutDelegate, + onFullscreenClicked: (lang: String, code: String) -> Unit, + syncCodePreference: (String) -> Unit, + onQuizChanged: (ReplyResult) -> Unit + ) : this( + LayoutStepQuizCodeBinding.bind(containerView.findViewById(R.id.stepQuizCodeContainer)), + codeOptions, + codeLayoutDelegate, + onFullscreenClicked, + syncCodePreference, + onQuizChanged + ) + + private val binding = codeStepQuizBinding + private var state: CodeStepQuizFormState = CodeStepQuizFormState.Idle set(value) { field = value @@ -44,11 +60,11 @@ class CodeStepQuizFormDelegate( private val viewStateDelegate = ViewStateDelegate() - private val codeLayout = containerView.codeStepLayout - private val stepQuizActions = containerView.stepQuizActions + private val codeLayout = binding.codeStepLayout + private val stepQuizActions = binding.stepQuizActions - private val stepQuizCodeLangChooserTitle = containerView.stepQuizCodeLangChooserTitle - private val stepQuizCodeLangChooser = containerView.stepQuizCodeLangChooser + private val stepQuizCodeLangChooserTitle = binding.stepQuizCodeLangChooserTitle + private val stepQuizCodeLangChooser = binding.stepQuizCodeLangChooser private val stepQuizCodeLangChooserAdapter = DefaultDelegateAdapter() private val codeStepQuizFormStateMapper = CodeStepQuizFormStateMapper() @@ -56,7 +72,7 @@ class CodeStepQuizFormDelegate( init { viewStateDelegate.addState() viewStateDelegate.addState(stepQuizCodeLangChooserTitle, stepQuizCodeLangChooser, - containerView.stepQuizCodeLangChooserDividerTop, containerView.stepQuizCodeLangChooserDividerBottom) + binding.stepQuizCodeLangChooserDividerTop.root, binding.stepQuizCodeLangChooserDividerBottom.root) viewStateDelegate.addState(codeLayout, stepQuizActions) /** @@ -114,4 +130,4 @@ class CodeStepQuizFormDelegate( fun updateCodeLayoutFromDialog(lang: String, code: String) { state = CodeStepQuizFormState.Lang(lang, code) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/delegate/CodeStepRunCodeDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/delegate/CodeStepRunCodeDelegate.kt index efee122f9c..e166b37e5d 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/delegate/CodeStepRunCodeDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/delegate/CodeStepRunCodeDelegate.kt @@ -11,8 +11,8 @@ import androidx.appcompat.widget.ListPopupWindow import androidx.core.graphics.drawable.DrawableCompat import androidx.core.view.isGone import com.google.android.material.tabs.TabLayout -import kotlinx.android.synthetic.main.layout_step_quiz_code_fullscreen_run_code.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.LayoutStepQuizCodeFullscreenRunCodeBinding import org.stepic.droid.code.ui.CodeEditorLayout import org.stepic.droid.model.code.ProgrammingLanguage import org.stepic.droid.persistence.model.StepPersistentWrapper @@ -39,16 +39,18 @@ class CodeStepRunCodeDelegate( private const val RUN_CODE_TAB = 2 } - private val runCodeScrollView = runCodeLayout.dataScrollView - private val runCodeInputDataTitle = runCodeLayout.inputDataTitle - private val runCodeInputSamplePicker = runCodeLayout.inputDataSamplePicker - private val runCodeInputDataSample = runCodeLayout.inputDataSample - private val runCodeOutputDataSeparator = runCodeLayout.outputSeparator - private val runCodeOutputDataTitle = runCodeLayout.outputDataTitle - private val runCodeOutputDataSample = runCodeLayout.outputDataSample - private val runCodeFeedback = runCodeLayout.runCodeFeedback - private val runCodeFab = runCodeLayout.runCodeFab - private val runCodeAction = runCodeLayout.runCodeAction + private val binding = LayoutStepQuizCodeFullscreenRunCodeBinding.bind(runCodeLayout) + + private val runCodeScrollView = binding.dataScrollView + private val runCodeInputDataTitle = binding.inputDataTitle + private val runCodeInputSamplePicker = binding.inputDataSamplePicker + private val runCodeInputDataSample = binding.inputDataSample + private val runCodeOutputDataSeparator = binding.outputSeparator.root + private val runCodeOutputDataTitle = binding.outputDataTitle + private val runCodeOutputDataSample = binding.outputDataSample + private val runCodeFeedback = binding.runCodeFeedback + private val runCodeFab = binding.runCodeFab + private val runCodeAction = binding.runCodeAction var lang: String = "" set(value) { @@ -235,4 +237,4 @@ class CodeStepRunCodeDelegate( stepId = stepWrapper.step.id ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/fragment/CodeStepQuizFragment.kt b/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/fragment/CodeStepQuizFragment.kt index 28feedcda0..75a9ce6c3d 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/fragment/CodeStepQuizFragment.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_code/ui/fragment/CodeStepQuizFragment.kt @@ -1,9 +1,10 @@ package org.stepik.android.view.step_quiz_code.ui.fragment +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import androidx.fragment.app.Fragment -import kotlinx.android.synthetic.main.layout_step_quiz_code.* -import org.stepic.droid.R +import org.stepic.droid.databinding.LayoutStepQuizCodeBinding import org.stepic.droid.ui.dialogs.ChangeCodeLanguageDialog import org.stepic.droid.ui.dialogs.ProgrammingLanguageChooserDialogFragment import org.stepik.android.domain.code_preference.model.InitCodePreference @@ -36,23 +37,29 @@ class CodeStepQuizFragment : private lateinit var codeStepQuizFormDelegate: CodeStepQuizFormDelegate - override val quizLayoutRes: Int = - R.layout.layout_step_quiz_code + private var _binding: LayoutStepQuizCodeBinding? = null + private val binding get() = _binding!! override val quizViews: Array - get() = arrayOf(stepQuizCodeContainer) + get() = arrayOf(binding.root) - override fun createStepQuizFormDelegate(view: View): StepQuizFormDelegate { + override fun createStepView(layoutInflater: LayoutInflater, parent: ViewGroup): View { + return LayoutStepQuizCodeBinding.inflate(layoutInflater, parent, false).also { + _binding = it + }.root + } + + override fun createStepQuizFormDelegate(): StepQuizFormDelegate { codeOptions = stepWrapper.step.block?.options ?: throw IllegalArgumentException("Code options shouldn't be null") codeStepQuizFormDelegate = CodeStepQuizFormDelegate( - containerView = view, + codeStepQuizBinding = binding, codeOptions = codeOptions, codeLayoutDelegate = CodeLayoutDelegate( - codeContainerView = view, + codeLayoutBinding = binding, step = stepWrapper.step, codeTemplates = codeOptions.codeTemplates, - codeQuizInstructionDelegate = CodeQuizInstructionDelegate(view, true), + codeQuizInstructionDelegate = CodeQuizInstructionDelegate(binding, true), codeToolbarAdapter = null, onChangeLanguageClicked = ::onChangeLanguageClicked ), @@ -108,4 +115,4 @@ class CodeStepQuizFragment : .newInstance(lang, code, codeOptions.codeTemplates, stepWrapper, lessonData.lesson.title.orEmpty()) .showIfNotExists(childFragmentManager, CodeStepQuizFullScreenDialogFragment.TAG) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/adapter/delegate/FillBlanksItemInputAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/adapter/delegate/FillBlanksItemInputAdapterDelegate.kt index 67f4fb9d6d..aa65588ebf 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/adapter/delegate/FillBlanksItemInputAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/adapter/delegate/FillBlanksItemInputAdapterDelegate.kt @@ -5,8 +5,9 @@ import android.view.View import android.view.ViewGroup import androidx.annotation.DrawableRes import androidx.annotation.IdRes -import kotlinx.android.synthetic.main.item_step_quiz_fill_blanks_input.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemStepQuizFillBlanksInputBinding import org.stepic.droid.ui.util.setCompoundDrawables import org.stepik.android.view.step_quiz_choice.ui.delegate.LayerListDrawableDelegate import org.stepik.android.view.step_quiz_fill_blanks.ui.model.FillBlanksItem @@ -23,25 +24,25 @@ class FillBlanksItemInputAdapterDelegate( ViewHolder(createView(parent, R.layout.item_step_quiz_fill_blanks_input)) private inner class ViewHolder(root: View) : DelegateViewHolder(root) { - private val stepQuizFillBlanksText = root.stepQuizFillBlanksText + private val viewBinding: ItemStepQuizFillBlanksInputBinding by viewBinding { ItemStepQuizFillBlanksInputBinding.bind(root) } private val layerListDrawableDelegate: LayerListDrawableDelegate init { - stepQuizFillBlanksText.setOnClickListener { onItemClicked(adapterPosition, (itemData as FillBlanksItem.Input).text) } + viewBinding.stepQuizFillBlanksText.setOnClickListener { onItemClicked(adapterPosition, (itemData as FillBlanksItem.Input).text) } layerListDrawableDelegate = LayerListDrawableDelegate( listOf( R.id.checked_layer, R.id.correct_layer, R.id.incorrect_layer ), - stepQuizFillBlanksText.background.mutate() as LayerDrawable + viewBinding.stepQuizFillBlanksText.background.mutate() as LayerDrawable ) } override fun onBind(data: FillBlanksItem) { data as FillBlanksItem.Input itemView.isEnabled = data.isEnabled - stepQuizFillBlanksText.text = data.text + viewBinding.stepQuizFillBlanksText.text = data.text val (@IdRes layer, @DrawableRes icon) = when (data.correct) { true -> R.id.correct_layer to R.drawable.ic_step_quiz_correct @@ -53,7 +54,7 @@ class FillBlanksItemInputAdapterDelegate( R.id.checked_layer to -1 } layerListDrawableDelegate.showLayer(layer) - stepQuizFillBlanksText.setCompoundDrawables(start = icon) + viewBinding.stepQuizFillBlanksText.setCompoundDrawables(start = icon) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/adapter/delegate/FillBlanksItemSelectAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/adapter/delegate/FillBlanksItemSelectAdapterDelegate.kt index c4d4ae3862..11668d1bc6 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/adapter/delegate/FillBlanksItemSelectAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/adapter/delegate/FillBlanksItemSelectAdapterDelegate.kt @@ -14,8 +14,9 @@ import androidx.annotation.IdRes import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.widget.ListPopupWindow import androidx.core.graphics.drawable.DrawableCompat -import kotlinx.android.synthetic.main.item_step_quiz_fill_blanks_select.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemStepQuizFillBlanksSelectBinding import org.stepik.android.view.step_quiz_choice.ui.delegate.LayerListDrawableDelegate import org.stepik.android.view.step_quiz_fill_blanks.ui.model.FillBlanksItem import ru.nobird.android.ui.adapterdelegates.AdapterDelegate @@ -31,7 +32,8 @@ class FillBlanksItemSelectAdapterDelegate( ViewHolder(createView(parent, R.layout.item_step_quiz_fill_blanks_select)) private inner class ViewHolder(root: View) : DelegateViewHolder(root) { - private val stepQuizFillBlanksText = root.stepQuizFillBlanksText + private val viewBinding: ItemStepQuizFillBlanksSelectBinding by viewBinding { ItemStepQuizFillBlanksSelectBinding.bind(root) } + private val stepQuizFillBlanksText = viewBinding.stepQuizFillBlanksText private val layerListDrawableDelegate: LayerListDrawableDelegate init { diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/adapter/delegate/FillBlanksItemTextAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/adapter/delegate/FillBlanksItemTextAdapterDelegate.kt index 1b7057bcfd..5580d02335 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/adapter/delegate/FillBlanksItemTextAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/adapter/delegate/FillBlanksItemTextAdapterDelegate.kt @@ -3,9 +3,10 @@ package org.stepik.android.view.step_quiz_fill_blanks.ui.adapter.delegate import android.view.View import android.view.ViewGroup import androidx.core.view.updateLayoutParams +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.flexbox.FlexboxLayoutManager -import kotlinx.android.synthetic.main.item_step_quiz_fill_blanks_text.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.ItemStepQuizFillBlanksTextBinding import org.stepik.android.view.step_quiz_fill_blanks.ui.model.FillBlanksItem import ru.nobird.android.ui.adapterdelegates.AdapterDelegate import ru.nobird.android.ui.adapterdelegates.DelegateViewHolder @@ -18,7 +19,8 @@ class FillBlanksItemTextAdapterDelegate : AdapterDelegate(root) { - private val stepQuizFillBlanksText = root.stepQuizFillBlanksText + private val viewBinding: ItemStepQuizFillBlanksTextBinding by viewBinding { ItemStepQuizFillBlanksTextBinding.bind(root) } + private val stepQuizFillBlanksText = viewBinding.stepQuizFillBlanksText override fun onBind(data: FillBlanksItem) { data as FillBlanksItem.Text diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/delegate/FillBlanksStepQuizFormDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/delegate/FillBlanksStepQuizFormDelegate.kt index 277f62fb33..bacca63bc9 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/delegate/FillBlanksStepQuizFormDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/delegate/FillBlanksStepQuizFormDelegate.kt @@ -1,12 +1,13 @@ package org.stepik.android.view.step_quiz_fill_blanks.ui.delegate import android.view.View +import android.widget.TextView import androidx.fragment.app.FragmentManager +import androidx.recyclerview.widget.RecyclerView import com.google.android.flexbox.FlexboxLayoutManager -import kotlinx.android.synthetic.main.fragment_step_quiz.view.* -import kotlinx.android.synthetic.main.layout_step_quiz_fill_blanks.view.* import org.stepic.droid.R -import ru.nobird.app.core.model.mutate +import org.stepic.droid.databinding.FragmentStepQuizBinding +import org.stepic.droid.databinding.LayoutStepQuizFillBlanksBinding import org.stepik.android.model.Reply import org.stepik.android.presentation.step_quiz.StepQuizFeature import org.stepik.android.presentation.step_quiz.model.ReplyResult @@ -20,13 +21,40 @@ import org.stepik.android.view.step_quiz_fill_blanks.ui.mapper.FillBlanksItemMap import org.stepik.android.view.step_quiz_fill_blanks.ui.model.FillBlanksItem import ru.nobird.android.ui.adapters.DefaultDelegateAdapter import ru.nobird.android.view.base.ui.extension.showIfNotExists +import ru.nobird.app.core.model.mutate class FillBlanksStepQuizFormDelegate( - private val containerView: View, + private val rootView: View, + private val quizDescription: TextView, + private val fillBlanksRecycler: RecyclerView, private val fragmentManager: FragmentManager, private val onQuizChanged: (ReplyResult) -> Unit ) : StepQuizFormDelegate { - private val quizDescription = containerView.stepQuizDescription + constructor( + stepQuizBinding: FragmentStepQuizBinding, + fillBlanksBinding: LayoutStepQuizFillBlanksBinding, + fragmentManager: FragmentManager, + onQuizChanged: (ReplyResult) -> Unit + ) : this( + stepQuizBinding.root, + stepQuizBinding.stepQuizDescription, + fillBlanksBinding.root, + fragmentManager, + onQuizChanged + ) + + constructor( + containerView: View, + fragmentManager: FragmentManager, + onQuizChanged: (ReplyResult) -> Unit + ) : this( + containerView, + containerView.findViewById(R.id.stepQuizDescription), + containerView.findViewById(R.id.fillBlanksRecycler), + fragmentManager, + onQuizChanged + ) + private val itemsAdapter = DefaultDelegateAdapter() private val fillBlanksItemMapper = FillBlanksItemMapper() @@ -37,7 +65,7 @@ class FillBlanksStepQuizFormDelegate( itemsAdapter += FillBlanksItemInputAdapterDelegate(onItemClicked = ::inputItemAction) itemsAdapter += FillBlanksItemSelectAdapterDelegate(onItemClicked = ::selectItemAction) - with(containerView.fillBlanksRecycler) { + with(fillBlanksRecycler) { itemAnimator = null adapter = itemsAdapter isNestedScrollingEnabled = false @@ -73,7 +101,7 @@ class FillBlanksStepQuizFormDelegate( ?.submission itemsAdapter.items = fillBlanksItemMapper.mapToFillBlanksItems(state.attempt, submission, StepQuizFormResolver.isQuizEnabled(state)) - containerView.post { containerView.fillBlanksRecycler.requestLayout() } + rootView.post { fillBlanksRecycler.requestLayout() } } override fun createReply(): ReplyResult = @@ -96,4 +124,4 @@ class FillBlanksStepQuizFormDelegate( ), ReplyResult.Validation.Success ) -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/fragment/FillBlanksInputBottomSheetDialogFragment.kt b/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/fragment/FillBlanksInputBottomSheetDialogFragment.kt index 1638a95602..526a7e32f9 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/fragment/FillBlanksInputBottomSheetDialogFragment.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/fragment/FillBlanksInputBottomSheetDialogFragment.kt @@ -8,12 +8,15 @@ import android.view.ViewGroup import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputMethodManager import androidx.fragment.app.DialogFragment +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import kotlinx.android.synthetic.main.bottom_sheet_dialog_fill_blanks_input.* import org.stepic.droid.R +import org.stepic.droid.databinding.BottomSheetDialogFillBlanksInputBinding import ru.nobird.android.view.base.ui.extension.argument class FillBlanksInputBottomSheetDialogFragment : BottomSheetDialogFragment() { + private val binding: BottomSheetDialogFillBlanksInputBinding by viewBinding(BottomSheetDialogFillBlanksInputBinding::bind) + companion object { const val TAG = "FillBlanksInputBottomSheetDialogFragment" @@ -46,22 +49,22 @@ class FillBlanksInputBottomSheetDialogFragment : BottomSheetDialogFragment() { index = savedInstanceState.getInt(ARG_INDEX) text = savedInstanceState.getString(ARG_TEXT) ?: return } - fillBlanksInputField.append(text) - fillBlanksInputField.setOnEditorActionListener { _, actionId, _ -> + binding.fillBlanksInputField.append(text) + binding.fillBlanksInputField.setOnEditorActionListener { _, actionId, _ -> if (actionId == EditorInfo.IME_ACTION_DONE) { super.dismiss() } false } - fillBlanksInputField.post { - fillBlanksInputField.requestFocus() - (requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).showSoftInput(fillBlanksInputField, InputMethodManager.SHOW_IMPLICIT) + binding.fillBlanksInputField.post { + binding.fillBlanksInputField.requestFocus() + (requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).showSoftInput(binding.fillBlanksInputField, InputMethodManager.SHOW_IMPLICIT) } } override fun onPause() { (parentFragment as? Callback) - ?.onSyncInputItemWithParent(index, fillBlanksInputField.text.toString()) + ?.onSyncInputItemWithParent(index, binding.fillBlanksInputField.text.toString()) super.onPause() } diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/fragment/FillBlanksStepQuizFragment.kt b/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/fragment/FillBlanksStepQuizFragment.kt index 349abca96f..8d38ad4359 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/fragment/FillBlanksStepQuizFragment.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_fill_blanks/ui/fragment/FillBlanksStepQuizFragment.kt @@ -1,9 +1,10 @@ package org.stepik.android.view.step_quiz_fill_blanks.ui.fragment +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import androidx.fragment.app.Fragment -import kotlinx.android.synthetic.main.layout_step_quiz_fill_blanks.* -import org.stepic.droid.R +import org.stepic.droid.databinding.LayoutStepQuizFillBlanksBinding import org.stepik.android.presentation.step_quiz.StepQuizFeature import org.stepik.android.view.step_quiz.ui.delegate.StepQuizFormDelegate import org.stepik.android.view.step_quiz.ui.fragment.DefaultStepQuizFragment @@ -24,18 +25,30 @@ class FillBlanksStepQuizFragment : private lateinit var fillBlanksStepQuizFormDelegate: FillBlanksStepQuizFormDelegate - override val quizLayoutRes: Int = - R.layout.layout_step_quiz_fill_blanks + private var _binding: LayoutStepQuizFillBlanksBinding? = null + private val binding: LayoutStepQuizFillBlanksBinding + get() = requireNotNull(_binding) override val quizViews: Array - get() = arrayOf(fillBlanksRecycler) + get() = arrayOf(binding.root) - override fun createStepQuizFormDelegate(view: View): StepQuizFormDelegate { - fillBlanksStepQuizFormDelegate = FillBlanksStepQuizFormDelegate(view, childFragmentManager, onQuizChanged = ::syncReplyState) + override fun createStepView(layoutInflater: LayoutInflater, parent: ViewGroup): View { + return LayoutStepQuizFillBlanksBinding.inflate(layoutInflater, parent, false).also { + _binding = it + }.root + } + + override fun createStepQuizFormDelegate(): StepQuizFormDelegate { + fillBlanksStepQuizFormDelegate = FillBlanksStepQuizFormDelegate( + stepQuizBinding = stepQuizBinding, + fillBlanksBinding = binding, + fragmentManager = childFragmentManager, + onQuizChanged = ::syncReplyState, + ) return fillBlanksStepQuizFormDelegate } override fun onSyncInputItemWithParent(index: Int, text: String) { fillBlanksStepQuizFormDelegate.updateInputItem(index, text) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_fullscreen_code/ui/dialog/CodeStepQuizFullScreenDialogFragment.kt b/app/src/main/java/org/stepik/android/view/step_quiz_fullscreen_code/ui/dialog/CodeStepQuizFullScreenDialogFragment.kt index cff4d05bfd..45974a8a31 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_fullscreen_code/ui/dialog/CodeStepQuizFullScreenDialogFragment.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_fullscreen_code/ui/dialog/CodeStepQuizFullScreenDialogFragment.kt @@ -14,19 +14,15 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import androidx.viewpager.widget.ViewPager +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.button.MaterialButton import com.google.android.material.floatingactionbutton.FloatingActionButton -import kotlinx.android.synthetic.main.dialog_step_quiz_code_fullscreen.* -import kotlinx.android.synthetic.main.layout_step_quiz_code_fullscreen_instruction.view.* -import kotlinx.android.synthetic.main.layout_step_quiz_code_fullscreen_playground.view.* -import kotlinx.android.synthetic.main.layout_step_quiz_code_fullscreen_run_code.view.* -import kotlinx.android.synthetic.main.layout_step_quiz_code_keyboard_extension.* -import kotlinx.android.synthetic.main.view_centered_toolbar.* -import kotlinx.android.synthetic.main.view_step_quiz_submit_button.view.* +import com.google.android.material.tabs.TabLayout import org.stepic.droid.R import org.stepic.droid.base.App import org.stepic.droid.code.ui.CodeEditorLayout import org.stepic.droid.code.util.CodeToolbarUtil +import org.stepic.droid.databinding.DialogStepQuizCodeFullscreenBinding import org.stepic.droid.model.code.ProgrammingLanguage import org.stepic.droid.persistence.model.StepPersistentWrapper import org.stepic.droid.ui.adapters.CodeToolbarAdapter @@ -69,6 +65,8 @@ class CodeStepQuizFullScreenDialogFragment : DialogFragment(), } } + private val binding: DialogStepQuizCodeFullscreenBinding by viewBinding(DialogStepQuizCodeFullscreenBinding::bind) + private lateinit var codeLayoutDelegate: CodeLayoutDelegate private var runCodeDelegate: CodeStepRunCodeDelegate? = null @@ -137,6 +135,9 @@ class CodeStepQuizFullScreenDialogFragment : DialogFragment(), override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + val centeredToolbar = view.findViewById(R.id.centeredToolbar) + val centeredToolbarTitle = view.findViewById(R.id.centeredToolbarTitle) + centeredToolbarTitle.text = lessonTitle centeredToolbar.inflateMenu(R.menu.code_playground_menu) centeredToolbar.setNavigationOnClickListener { dismiss() } @@ -171,22 +172,22 @@ class CodeStepQuizFullScreenDialogFragment : DialogFragment(), ?.text ?.takeIf(String::isNotEmpty) - instructionsLayout.stepQuizCodeTextContent.setText(text) + instructionsLayout.findViewById(R.id.stepQuizCodeTextContent).setText(text) /** * Code play ground view binding */ - submitButtonSeparator = playgroundLayout.submitButtonSeparator - codeSubmitFab = playgroundLayout.codeSubmitFab - codeSubmitButton = playgroundLayout.stepQuizAction - retryButton = playgroundLayout.stepQuizRetry - codeLayout = playgroundLayout.codeStepLayout + submitButtonSeparator = playgroundLayout.findViewById(R.id.submitButtonSeparator) + codeSubmitFab = playgroundLayout.findViewById(R.id.codeSubmitFab) + codeSubmitButton = playgroundLayout.findViewById(R.id.stepQuizAction) + retryButton = playgroundLayout.findViewById(R.id.stepQuizRetry) + codeLayout = playgroundLayout.findViewById(R.id.codeStepLayout) runCodeDelegate = runCodeLayout?.let { layout -> CodeStepRunCodeDelegate( runCodeLayout = layout, codeRunPresenter = codeRunPresenter, - fullScreenCodeTabs = fullScreenCodeTabs, + fullScreenCodeTabs = binding.fullScreenCodeTabs, codeLayout = codeLayout, context = requireContext(), stepWrapper = stepWrapper @@ -198,13 +199,13 @@ class CodeStepQuizFullScreenDialogFragment : DialogFragment(), /** * Run code view binding */ - runCodeActionSeparator = runCodeLayout?.runCodeActionSeparator - runCodeFab = runCodeLayout?.runCodeFab - runCodeAction = runCodeLayout?.runCodeAction + runCodeActionSeparator = runCodeLayout?.findViewById(R.id.runCodeActionSeparator) + runCodeFab = runCodeLayout?.findViewById(R.id.runCodeFab) + runCodeAction = runCodeLayout?.findViewById(R.id.runCodeAction) retryButton.isVisible = false setupCodeToolAdapter() - setupKeyboardExtension() + setupKeyboardExtension(view, centeredToolbar) codeLayoutDelegate = CodeLayoutDelegate( codeContainerView = playgroundLayout, @@ -217,7 +218,7 @@ class CodeStepQuizFullScreenDialogFragment : DialogFragment(), codeLayoutDelegate.setLanguage(lang, code) codeLayoutDelegate.setDetailsContentData(lang) - fullScreenCodeViewPager.setCurrentItem(CODE_TAB, false) + binding.fullScreenCodeViewPager.setCurrentItem(CODE_TAB, false) codeSubmitButton.setIconResource(R.drawable.ic_submit_code) codeSubmitButton.iconPadding = requireContext().resources.getDimensionPixelSize(R.dimen.step_quiz_full_screen_code_layout_action_button_icon_padding) @@ -234,9 +235,9 @@ class CodeStepQuizFullScreenDialogFragment : DialogFragment(), private fun initViewPager(isShowRunCode: Boolean) { val pagerAdapter = CodeStepQuizFullScreenPagerAdapter(requireContext(), isShowRunCode = isShowRunCode) - fullScreenCodeViewPager.adapter = pagerAdapter - fullScreenCodeTabs.setupWithViewPager(fullScreenCodeViewPager) - fullScreenCodeViewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { + binding.fullScreenCodeViewPager.adapter = pagerAdapter + binding.fullScreenCodeTabs.setupWithViewPager(binding.fullScreenCodeViewPager) + binding.fullScreenCodeViewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { override fun onPageScrollStateChanged(p0: Int) {} override fun onPageScrolled(p0: Int, p1: Float, p2: Int) {} override fun onPageSelected(p0: Int) { @@ -318,16 +319,17 @@ class CodeStepQuizFullScreenDialogFragment : DialogFragment(), /** * Keyboard extension */ - private fun setupKeyboardExtension() { + private fun setupKeyboardExtension(view: View, centeredToolbar: com.google.android.material.appbar.MaterialToolbar) { + val stepQuizCodeKeyboardExtension = view.findViewById(R.id.stepQuizCodeKeyboardExtension) stepQuizCodeKeyboardExtension.adapter = codeToolbarAdapter stepQuizCodeKeyboardExtension.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false) codeLayout.codeToolbarAdapter = codeToolbarAdapter setOnKeyboardOpenListener( - coordinator, + binding.coordinator, onKeyboardHidden = { if (keyboardShown) { - if (fullScreenCodeViewPager.currentItem == CODE_TAB && runCodeDelegate != null) { + if (binding.fullScreenCodeViewPager.currentItem == CODE_TAB && runCodeDelegate != null) { codeRunPresenter.resolveRunCodePopup() } stepQuizCodeKeyboardExtension.visibility = View.GONE @@ -343,14 +345,14 @@ class CodeStepQuizFullScreenDialogFragment : DialogFragment(), 0, requireContext().resources.getDimensionPixelSize(R.dimen.step_quiz_fullscreen_code_layout_bottom_padding) ) - setViewsVisibility(needShow = true) + setViewsVisibility(needShow = true, centeredToolbar = centeredToolbar) keyboardShown = false } }, onKeyboardShown = { if (!keyboardShown) { // We show the keyboard extension only when "Code" tab is opened - if (fullScreenCodeViewPager.currentItem == CODE_TAB) { + if (binding.fullScreenCodeViewPager.currentItem == CODE_TAB) { stepQuizCodeKeyboardExtension.visibility = View.VISIBLE } codeLayout.isNestedScrollingEnabled = false @@ -360,7 +362,7 @@ class CodeStepQuizFullScreenDialogFragment : DialogFragment(), bottomMargin = stepQuizCodeKeyboardExtension.height } codeLayout.setPadding(0, 0, 0, 0) - setViewsVisibility(needShow = false) + setViewsVisibility(needShow = false, centeredToolbar = centeredToolbar) keyboardShown = true } } @@ -370,12 +372,12 @@ class CodeStepQuizFullScreenDialogFragment : DialogFragment(), /** * Hiding views upon opening keyboard */ - private fun setViewsVisibility(needShow: Boolean) { + private fun setViewsVisibility(needShow: Boolean, centeredToolbar: com.google.android.material.appbar.MaterialToolbar = view!!.findViewById(R.id.centeredToolbar)) { submitButtonSeparator.isVisible = needShow codeSubmitFab.isVisible = !needShow codeSubmitButton.isVisible = needShow centeredToolbar.isVisible = needShow - fullScreenCodeTabs.isVisible = needShow + binding.fullScreenCodeTabs.isVisible = needShow runCodeActionSeparator?.isVisible = needShow runCodeFab?.isVisible = !needShow runCodeAction?.isVisible = needShow @@ -402,4 +404,4 @@ class CodeStepQuizFullScreenDialogFragment : DialogFragment(), fun onSyncCodeStateWithParent(lang: String, code: String, onSubmitClicked: Boolean = false) fun onSyncCodePreference(lang: String) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_matching/ui/adapter/delegate/MatchingItemOptionAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_matching/ui/adapter/delegate/MatchingItemOptionAdapterDelegate.kt index 425bbd7aac..83991f6018 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_matching/ui/adapter/delegate/MatchingItemOptionAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_matching/ui/adapter/delegate/MatchingItemOptionAdapterDelegate.kt @@ -3,8 +3,9 @@ package org.stepik.android.view.step_quiz_matching.ui.adapter.delegate import android.view.View import android.view.ViewGroup import androidx.core.view.ViewCompat -import kotlinx.android.synthetic.main.item_step_quiz_sorting.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemStepQuizSortingBinding import org.stepik.android.view.latex.ui.widget.ProgressableWebViewClient import org.stepik.android.view.step_quiz_matching.ui.model.MatchingItem import ru.nobird.android.ui.adapterdelegates.AdapterDelegate @@ -22,35 +23,31 @@ class MatchingItemOptionAdapterDelegate( ViewHolder(createView(parent, R.layout.item_step_quiz_sorting)) private inner class ViewHolder(root: View) : DelegateViewHolder(root) { - private val stepQuizSortingOption = root.stepQuizSortingOption - private val stepQuizSortingOptionProgress = root.stepQuizSortingOptionProgress - - private val stepQuizSortingOptionUp = root.stepQuizSortingOptionUp - private val stepQuizSortingOptionDown = root.stepQuizSortingOptionDown + private val viewBinding: ItemStepQuizSortingBinding by viewBinding { ItemStepQuizSortingBinding.bind(root) } init { - stepQuizSortingOptionUp.setOnClickListener { onMoveItemClicked(adapterPosition, SortingDirection.UP) } - stepQuizSortingOptionDown.setOnClickListener { onMoveItemClicked(adapterPosition, SortingDirection.DOWN) } + viewBinding.stepQuizSortingOptionUp.setOnClickListener { onMoveItemClicked(adapterPosition, SortingDirection.UP) } + viewBinding.stepQuizSortingOptionDown.setOnClickListener { onMoveItemClicked(adapterPosition, SortingDirection.DOWN) } root.layoutParams = (root.layoutParams as ViewGroup.MarginLayoutParams).apply { leftMargin = context.resources.getDimensionPixelOffset(R.dimen.step_quiz_matching_item_margin) } - stepQuizSortingOption.webViewClient = ProgressableWebViewClient(stepQuizSortingOptionProgress, stepQuizSortingOption.webView) + viewBinding.stepQuizSortingOption.webViewClient = ProgressableWebViewClient(viewBinding.stepQuizSortingOptionProgress, viewBinding.stepQuizSortingOption.webView) } override fun onBind(data: MatchingItem) { data as MatchingItem.Option itemView.isEnabled = data.isEnabled - stepQuizSortingOption.setText(data.text) + viewBinding.stepQuizSortingOption.setText(data.text) - stepQuizSortingOptionUp.isEnabled = data.isEnabled && adapterPosition != 1 - stepQuizSortingOptionUp.alpha = if (stepQuizSortingOptionUp.isEnabled) 1f else 0.2f + viewBinding.stepQuizSortingOptionUp.isEnabled = data.isEnabled && adapterPosition != 1 + viewBinding.stepQuizSortingOptionUp.alpha = if (viewBinding.stepQuizSortingOptionUp.isEnabled) 1f else 0.2f - stepQuizSortingOptionDown.isEnabled = data.isEnabled && adapterPosition + 1 != adapter.items.size - stepQuizSortingOptionDown.alpha = if (stepQuizSortingOptionDown.isEnabled) 1f else 0.2f + viewBinding.stepQuizSortingOptionDown.isEnabled = data.isEnabled && adapterPosition + 1 != adapter.items.size + viewBinding.stepQuizSortingOptionDown.alpha = if (viewBinding.stepQuizSortingOptionDown.isEnabled) 1f else 0.2f val elevation = if (data.isEnabled) context.resources.getDimension(R.dimen.step_quiz_sorting_item_elevation) else 0f ViewCompat.setElevation(itemView, elevation) @@ -60,4 +57,4 @@ class MatchingItemOptionAdapterDelegate( enum class SortingDirection { UP, DOWN } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_matching/ui/adapter/delegate/MatchingItemTitleAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_matching/ui/adapter/delegate/MatchingItemTitleAdapterDelegate.kt index 6648cc085a..7a2c594c48 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_matching/ui/adapter/delegate/MatchingItemTitleAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_matching/ui/adapter/delegate/MatchingItemTitleAdapterDelegate.kt @@ -4,8 +4,9 @@ import android.view.View import android.view.ViewGroup import androidx.core.view.ViewCompat import androidx.core.view.isVisible -import kotlinx.android.synthetic.main.item_step_quiz_sorting.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemStepQuizSortingBinding import org.stepik.android.view.latex.ui.widget.ProgressableWebViewClient import org.stepik.android.view.step_quiz_matching.ui.model.MatchingItem import ru.nobird.android.ui.adapterdelegates.AdapterDelegate @@ -19,33 +20,29 @@ class MatchingItemTitleAdapterDelegate : AdapterDelegate(root) { - private val stepQuizSortingOption = root.stepQuizSortingOption - private val stepQuizSortingOptionProgress = root.stepQuizSortingOptionProgress - - private val stepQuizSortingOptionUp = root.stepQuizSortingOptionUp - private val stepQuizSortingOptionDown = root.stepQuizSortingOptionDown + private val viewBinding: ItemStepQuizSortingBinding by viewBinding { ItemStepQuizSortingBinding.bind(root) } init { - stepQuizSortingOptionUp.isVisible = false - stepQuizSortingOptionDown.isVisible = false + viewBinding.stepQuizSortingOptionUp.isVisible = false + viewBinding.stepQuizSortingOptionDown.isVisible = false root.layoutParams = (root.layoutParams as ViewGroup.MarginLayoutParams).apply { rightMargin = context.resources.getDimensionPixelOffset(R.dimen.step_quiz_matching_item_margin) } - stepQuizSortingOption.webViewClient = ProgressableWebViewClient(stepQuizSortingOptionProgress, stepQuizSortingOption.webView) + viewBinding.stepQuizSortingOption.webViewClient = ProgressableWebViewClient(viewBinding.stepQuizSortingOptionProgress, viewBinding.stepQuizSortingOption.webView) } override fun onBind(data: MatchingItem) { data as MatchingItem.Title itemView.isEnabled = data.isEnabled - stepQuizSortingOption.setText(data.text) + viewBinding.stepQuizSortingOption.setText(data.text) val elevation = if (data.isEnabled) context.resources.getDimension(R.dimen.step_quiz_sorting_item_elevation) else 0f ViewCompat.setElevation(itemView, elevation) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_matching/ui/delegate/MatchingStepQuizFormDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_matching/ui/delegate/MatchingStepQuizFormDelegate.kt index 86bbca4e25..8a2334927c 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_matching/ui/delegate/MatchingStepQuizFormDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_matching/ui/delegate/MatchingStepQuizFormDelegate.kt @@ -1,11 +1,13 @@ package org.stepik.android.view.step_quiz_matching.ui.delegate import android.view.View +import android.widget.TextView import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.SimpleItemAnimator -import kotlinx.android.synthetic.main.fragment_step_quiz.view.* -import kotlinx.android.synthetic.main.layout_step_quiz_sorting.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.FragmentStepQuizBinding +import org.stepic.droid.databinding.LayoutStepQuizSortingBinding import org.stepik.android.model.Reply import org.stepik.android.presentation.step_quiz.StepQuizFeature import org.stepik.android.presentation.step_quiz.model.ReplyResult @@ -15,14 +17,33 @@ import org.stepik.android.view.step_quiz_matching.ui.adapter.delegate.MatchingIt import org.stepik.android.view.step_quiz_matching.ui.adapter.delegate.MatchingItemTitleAdapterDelegate import org.stepik.android.view.step_quiz_matching.ui.mapper.MatchingItemMapper import org.stepik.android.view.step_quiz_matching.ui.model.MatchingItem -import ru.nobird.app.core.model.swap import ru.nobird.android.ui.adapters.DefaultDelegateAdapter +import ru.nobird.app.core.model.swap class MatchingStepQuizFormDelegate( - containerView: View, + private val quizDescription: TextView, + private val sortingRecycler: RecyclerView, private val onQuizChanged: (ReplyResult) -> Unit ) : StepQuizFormDelegate { - private val quizDescription = containerView.stepQuizDescription + constructor( + stepQuizBinding: FragmentStepQuizBinding, + matchingStepQuizBinding: LayoutStepQuizSortingBinding, + onQuizChanged: (ReplyResult) -> Unit + ) : this( + stepQuizBinding.stepQuizDescription, + matchingStepQuizBinding.root, + onQuizChanged + ) + + constructor( + containerView: View, + onQuizChanged: (ReplyResult) -> Unit + ) : this( + containerView.findViewById(R.id.stepQuizDescription), + containerView.findViewById(R.id.sortingRecycler), + onQuizChanged + ) + private val optionsAdapter = DefaultDelegateAdapter() private val matchingItemMapper = MatchingItemMapper() @@ -32,7 +53,7 @@ class MatchingStepQuizFormDelegate( optionsAdapter += MatchingItemTitleAdapterDelegate() optionsAdapter += MatchingItemOptionAdapterDelegate(optionsAdapter, ::moveOption) - with(containerView.sortingRecycler) { + with(sortingRecycler) { adapter = optionsAdapter isNestedScrollingEnabled = false layoutManager = LinearLayoutManager(context) @@ -89,4 +110,4 @@ class MatchingStepQuizFormDelegate( ), ReplyResult.Validation.Success ) -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_matching/ui/fragment/MatchingStepQuizFragment.kt b/app/src/main/java/org/stepik/android/view/step_quiz_matching/ui/fragment/MatchingStepQuizFragment.kt index 4a4db1a076..f2467a6e67 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_matching/ui/fragment/MatchingStepQuizFragment.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_matching/ui/fragment/MatchingStepQuizFragment.kt @@ -1,9 +1,10 @@ package org.stepik.android.view.step_quiz_matching.ui.fragment +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import androidx.fragment.app.Fragment -import kotlinx.android.synthetic.main.layout_step_quiz_sorting.* -import org.stepic.droid.R +import org.stepic.droid.databinding.LayoutStepQuizSortingBinding import org.stepik.android.view.step_quiz.ui.delegate.StepQuizFormDelegate import org.stepik.android.view.step_quiz.ui.fragment.DefaultStepQuizFragment import org.stepik.android.view.step_quiz_matching.ui.delegate.MatchingStepQuizFormDelegate @@ -17,12 +18,23 @@ class MatchingStepQuizFragment : DefaultStepQuizFragment() { } } - override val quizLayoutRes: Int = - R.layout.layout_step_quiz_sorting + private var _binding: LayoutStepQuizSortingBinding? = null + private val binding + get() = requireNotNull(_binding) override val quizViews: Array - get() = arrayOf(sortingRecycler) + get() = arrayOf(binding.root) - override fun createStepQuizFormDelegate(view: View): StepQuizFormDelegate = - MatchingStepQuizFormDelegate(view, onQuizChanged = ::syncReplyState) -} \ No newline at end of file + override fun createStepView(layoutInflater: LayoutInflater, parent: ViewGroup): View { + return LayoutStepQuizSortingBinding.inflate(layoutInflater, parent, false).also { + _binding = it + }.root + } + + override fun createStepQuizFormDelegate(): StepQuizFormDelegate = + MatchingStepQuizFormDelegate( + stepQuizBinding = stepQuizBinding, + matchingStepQuizBinding = binding, + onQuizChanged = ::syncReplyState + ) +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_pycharm/ui/fragment/PyCharmStepQuizFragment.kt b/app/src/main/java/org/stepik/android/view/step_quiz_pycharm/ui/fragment/PyCharmStepQuizFragment.kt index 146e930a1a..f32b947b3b 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_pycharm/ui/fragment/PyCharmStepQuizFragment.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_pycharm/ui/fragment/PyCharmStepQuizFragment.kt @@ -6,8 +6,9 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment -import kotlinx.android.synthetic.main.fragment_step_quiz_pycharm.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.FragmentStepQuizPycharmBinding class PyCharmStepQuizFragment : Fragment() { companion object { @@ -15,12 +16,14 @@ class PyCharmStepQuizFragment : Fragment() { PyCharmStepQuizFragment() } + private val binding: FragmentStepQuizPycharmBinding by viewBinding(FragmentStepQuizPycharmBinding::bind) + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater.inflate(R.layout.fragment_step_quiz_pycharm, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - stepQuizFeedback.movementMethod = LinkMovementMethod.getInstance() + binding.stepQuizFeedback.movementMethod = LinkMovementMethod.getInstance() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_review/ui/delegate/StepQuizReviewDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_review/ui/delegate/StepQuizReviewDelegate.kt index a49f9182ca..cd6b9319d8 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_review/ui/delegate/StepQuizReviewDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_review/ui/delegate/StepQuizReviewDelegate.kt @@ -5,12 +5,10 @@ import android.view.ViewGroup import androidx.annotation.PluralsRes import androidx.annotation.StringRes import androidx.core.view.isVisible -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.error_no_connection_with_button_small.view.* -import kotlinx.android.synthetic.main.fragment_step_quiz_review_peer.* -import kotlinx.android.synthetic.main.layout_step_quiz_review_footer.* -import kotlinx.android.synthetic.main.layout_step_quiz_review_header.* import org.stepic.droid.R +import org.stepic.droid.databinding.FragmentStepQuizReviewPeerBinding +import org.stepic.droid.databinding.LayoutStepQuizReviewFooterBinding +import org.stepic.droid.databinding.LayoutStepQuizReviewHeaderBinding import org.stepik.android.model.ReviewStrategyType import org.stepik.android.model.Submission import org.stepik.android.presentation.step_quiz.StepQuizFeature @@ -24,7 +22,10 @@ import org.stepik.android.view.ui.delegate.ViewStateDelegate import ru.nobird.app.core.model.safeCast class StepQuizReviewDelegate( - override val containerView: View, + private val headerBinding: LayoutStepQuizReviewHeaderBinding, + private val footerBinding: LayoutStepQuizReviewFooterBinding, + private val reviewStep5Link: View, + private val peerBinding: FragmentStepQuizReviewPeerBinding?, private val instructionType: ReviewStrategyType, private val actionListener: ActionListener, @@ -32,45 +33,64 @@ class StepQuizReviewDelegate( private val quizView: View, private val quizDelegate: StepQuizDelegate, private val quizFeedbackBlocksDelegate: StepQuizFeedbackBlocksDelegate -) : LayoutContainer { +) { private val stepQuizFeedbackMapper = StepQuizFeedbackMapper() - private val resources = containerView.resources private val step1viewStateDelegate = ViewStateDelegate() .apply { addState( - reviewStep1DividerBottom, reviewStep1Container, reviewStep1Discounting, - reviewStep1ActionButton, reviewStep1ActionRetry + headerBinding.reviewStep1DividerBottom.root, + headerBinding.reviewStep1Container, + headerBinding.reviewStep1Discounting, + headerBinding.reviewStep1ActionButton, + headerBinding.reviewStep1ActionRetry ) } private val step1QuizViewStateDelegate = ViewStateDelegate() .apply { - addState(stepQuizProgress) - addState(stepQuizProgress) - addState(reviewStep1Discounting, reviewStep1QuizContainer, reviewStep1ActionButton, reviewStep1ActionRetry) - addState(stepQuizNetworkError) + addState(headerBinding.stepQuizProgress) + addState(headerBinding.stepQuizProgress) + addState( + headerBinding.reviewStep1Discounting, + headerBinding.reviewStep1QuizContainer, + headerBinding.reviewStep1ActionButton, + headerBinding.reviewStep1ActionRetry + ) + addState(headerBinding.stepQuizNetworkError.root) } private val step2viewStateDelegate = ViewStateDelegate() .apply { addState( - reviewStep2DividerBottom, reviewStep2Container, reviewStep2Loading, - reviewStep2CreateSession, reviewStep2SelectSubmission, reviewStep2Retry + headerBinding.reviewStep2DividerBottom.root, + headerBinding.reviewStep2Container, + headerBinding.reviewStep2Loading, + headerBinding.reviewStep2CreateSession, + headerBinding.reviewStep2SelectSubmission, + headerBinding.reviewStep2Retry + ) + addState( + headerBinding.reviewStep2DividerBottom.root, + headerBinding.reviewStep2Container + ) + addState( + headerBinding.reviewStep2DividerBottom.root, + headerBinding.reviewStep2Container ) - addState(reviewStep2DividerBottom, reviewStep2Container) - addState(reviewStep2DividerBottom, reviewStep2Container) } init { - stepQuizNetworkError.tryAgain.setOnClickListener { actionListener.onQuizTryAgainClicked() } + headerBinding.stepQuizNetworkError.tryAgain.setOnClickListener { actionListener.onQuizTryAgainClicked() } - reviewStep2SelectSubmission.setOnClickListener { actionListener.onSelectDifferentSubmissionClicked() } - reviewStep2CreateSession.setOnClickListener { actionListener.onCreateSessionClicked() } - reviewStep2Retry.setOnClickListener { actionListener.onSolveAgainClicked() } + headerBinding.reviewStep2SelectSubmission.setOnClickListener { actionListener.onSelectDifferentSubmissionClicked() } + headerBinding.reviewStep2CreateSession.setOnClickListener { actionListener.onCreateSessionClicked() } + headerBinding.reviewStep2Retry.setOnClickListener { actionListener.onSolveAgainClicked() } if (instructionType == ReviewStrategyType.PEER) { - reviewStep3Container.setOnClickListener { actionListener.onStartReviewClicked() } + requireNotNull(peerBinding) { "Peer review binding is required for peer review steps" } + .reviewStep3Container + .setOnClickListener { actionListener.onStartReviewClicked() } } } @@ -100,27 +120,27 @@ class StepQuizReviewDelegate( ?.submission ?.status - reviewStep1Status.status = + headerBinding.reviewStep1Status.status = if (submissionStatus == Submission.Status.WRONG) { ReviewStatusView.Status.ERROR } else { ReviewStatusView.Status.IN_PROGRESS } - stepQuizDescription.isEnabled = true + headerBinding.stepQuizDescription.isEnabled = true step1QuizViewStateDelegate.switchState(state.quizState) if (state.quizState is StepQuizFeature.State.AttemptLoaded) { quizDelegate.setState(state.quizState) } - setQuizViewParent(quizView, reviewStep1QuizContainer) - setQuizViewParent(quizFeedbackView, reviewStep1QuizContainer) + setQuizViewParent(quizView, headerBinding.reviewStep1QuizContainer) + setQuizViewParent(headerBinding.quizFeedbackView.root, headerBinding.reviewStep1QuizContainer) } else -> { - stepQuizDescription.isEnabled = false - reviewStep1Status.status = ReviewStatusView.Status.COMPLETED + headerBinding.stepQuizDescription.isEnabled = false + headerBinding.reviewStep1Status.status = ReviewStatusView.Status.COMPLETED } } } @@ -129,35 +149,50 @@ class StepQuizReviewDelegate( step2viewStateDelegate.switchState(state) when (state) { is StepQuizReviewFeature.State.SubmissionNotMade -> { - reviewStep2Title.setText(R.string.step_quiz_review_send_pending) - setStepStatus(reviewStep2Title, reviewStep2Link, reviewStep2Status, ReviewStatusView.Status.PENDING) + headerBinding.reviewStep2Title.setText(R.string.step_quiz_review_send_pending) + setStepStatus( + headerBinding.reviewStep2Title, + headerBinding.reviewStep2Link, + headerBinding.reviewStep2Status, + ReviewStatusView.Status.PENDING + ) } is StepQuizReviewFeature.State.SubmissionNotSelected -> { - reviewStep2Title.setText(R.string.step_quiz_review_send_in_progress) - setStepStatus(reviewStep2Title, reviewStep2Link, reviewStep2Status, ReviewStatusView.Status.IN_PROGRESS) + headerBinding.reviewStep2Title.setText(R.string.step_quiz_review_send_in_progress) + setStepStatus( + headerBinding.reviewStep2Title, + headerBinding.reviewStep2Link, + headerBinding.reviewStep2Status, + ReviewStatusView.Status.IN_PROGRESS + ) quizDelegate.setState(state.quizState) - reviewStep2Loading.isVisible = state.isSessionCreationInProgress - reviewStep2CreateSession.isVisible = !state.isSessionCreationInProgress - reviewStep2SelectSubmission.isVisible = !state.isSessionCreationInProgress - reviewStep2Retry.isVisible = !state.isSessionCreationInProgress + headerBinding.reviewStep2Loading.isVisible = state.isSessionCreationInProgress + headerBinding.reviewStep2CreateSession.isVisible = !state.isSessionCreationInProgress + headerBinding.reviewStep2SelectSubmission.isVisible = !state.isSessionCreationInProgress + headerBinding.reviewStep2Retry.isVisible = !state.isSessionCreationInProgress - setQuizViewParent(quizView, reviewStep2Container) - setQuizViewParent(quizFeedbackView, reviewStep2Container) + setQuizViewParent(quizView, headerBinding.reviewStep2Container) + setQuizViewParent(headerBinding.quizFeedbackView.root, headerBinding.reviewStep2Container) } else -> { - reviewStep2Title.setText(R.string.step_quiz_review_send_completed) - setStepStatus(reviewStep2Title, reviewStep2Link, reviewStep2Status, ReviewStatusView.Status.COMPLETED) + headerBinding.reviewStep2Title.setText(R.string.step_quiz_review_send_completed) + setStepStatus( + headerBinding.reviewStep2Title, + headerBinding.reviewStep2Link, + headerBinding.reviewStep2Status, + ReviewStatusView.Status.COMPLETED + ) state.safeCast() ?.quizState ?.safeCast() ?.let(quizDelegate::setState) - setQuizViewParent(quizView, reviewStep2Container) - setQuizViewParent(quizFeedbackView, reviewStep2Container) - quizFeedbackView.isVisible = false + setQuizViewParent(quizView, headerBinding.reviewStep2Container) + setQuizViewParent(headerBinding.quizFeedbackView.root, headerBinding.reviewStep2Container) + headerBinding.quizFeedbackView.root.isVisible = false } } } @@ -171,15 +206,21 @@ class StepQuizReviewDelegate( } private fun renderStep3(state: StepQuizReviewFeature.State) { + val requiredPeerBinding = requireNotNull(peerBinding) { "Peer review binding is required for peer review steps" } val reviewCount = state.safeCast()?.instruction?.minReviews ?: 0 when (state) { is StepQuizReviewFeature.State.SubmissionNotMade, is StepQuizReviewFeature.State.SubmissionNotSelected -> { - reviewStep3Title.setText(R.string.step_quiz_review_given_pending_zero) - setStepStatus(reviewStep3Title, reviewStep3Link, reviewStep3Status, ReviewStatusView.Status.PENDING) - reviewStep3Container.isVisible = false - reviewStep3Loading.isVisible = false + requiredPeerBinding.reviewStep3Title.setText(R.string.step_quiz_review_given_pending_zero) + setStepStatus( + requiredPeerBinding.reviewStep3Title, + requiredPeerBinding.reviewStep3Link, + requiredPeerBinding.reviewStep3Status, + ReviewStatusView.Status.PENDING + ) + requiredPeerBinding.reviewStep3Container.isVisible = false + requiredPeerBinding.reviewStep3Loading.isVisible = false } is StepQuizReviewFeature.State.SubmissionSelected -> { val givenReviewCount = state.session.givenReviews.size @@ -195,51 +236,74 @@ class StepQuizReviewDelegate( } else { R.plurals.step_quiz_review_given_pending } - append(resources.getQuantityString(pluralRes, remainingReviewCount, remainingReviewCount)) + append(headerBinding.root.resources.getQuantityString(pluralRes, remainingReviewCount, remainingReviewCount)) } if (givenReviewCount > 0) { if (isNotEmpty()) { append(" ") } - append(resources.getQuantityString(R.plurals.step_quiz_review_given_completed, givenReviewCount, givenReviewCount)) + append(headerBinding.root.resources.getQuantityString(R.plurals.step_quiz_review_given_completed, givenReviewCount, givenReviewCount)) } } - reviewStep3Title.text = text + requiredPeerBinding.reviewStep3Title.text = text - reviewStep3Container.isVisible = remainingReviewCount > 0 && !state.isReviewCreationInProgress + requiredPeerBinding.reviewStep3Container.isVisible = remainingReviewCount > 0 && !state.isReviewCreationInProgress - if (reviewStep3Container.isVisible) { - reviewStep3Container.isEnabled = remainingReviewCount <= 0 || state.session.isReviewAvailable - reviewStep3Container.setText(if (reviewStep3Container.isEnabled) R.string.step_quiz_review_given_start_review else R.string.step_quiz_review_given_no_review) + if (requiredPeerBinding.reviewStep3Container.isVisible) { + requiredPeerBinding.reviewStep3Container.isEnabled = remainingReviewCount <= 0 || state.session.isReviewAvailable + requiredPeerBinding.reviewStep3Container.setText( + if (requiredPeerBinding.reviewStep3Container.isEnabled) { + R.string.step_quiz_review_given_start_review + } else { + R.string.step_quiz_review_given_no_review + } + ) } - reviewStep3Loading.isVisible = state.isReviewCreationInProgress - setStepStatus(reviewStep3Title, reviewStep3Link, reviewStep3Status, ReviewStatusView.Status.IN_PROGRESS) + requiredPeerBinding.reviewStep3Loading.isVisible = state.isReviewCreationInProgress + setStepStatus( + requiredPeerBinding.reviewStep3Title, + requiredPeerBinding.reviewStep3Link, + requiredPeerBinding.reviewStep3Status, + ReviewStatusView.Status.IN_PROGRESS + ) } is StepQuizReviewFeature.State.Completed -> { val givenReviewCount = state.session.givenReviews.size - reviewStep3Title.text = resources.getQuantityString(R.plurals.step_quiz_review_given_completed, givenReviewCount, givenReviewCount) - reviewStep3Container.isVisible = false - reviewStep3Loading.isVisible = false - setStepStatus(reviewStep3Title, reviewStep3Link, reviewStep3Status, ReviewStatusView.Status.COMPLETED) + requiredPeerBinding.reviewStep3Title.text = + headerBinding.root.resources.getQuantityString(R.plurals.step_quiz_review_given_completed, givenReviewCount, givenReviewCount) + requiredPeerBinding.reviewStep3Container.isVisible = false + requiredPeerBinding.reviewStep3Loading.isVisible = false + setStepStatus( + requiredPeerBinding.reviewStep3Title, + requiredPeerBinding.reviewStep3Link, + requiredPeerBinding.reviewStep3Status, + ReviewStatusView.Status.COMPLETED + ) } else -> Unit } } private fun renderStep4(state: StepQuizReviewFeature.State) { + val requiredPeerBinding = requireNotNull(peerBinding) { "Peer review binding is required for peer review steps" } val reviewCount = state.safeCast()?.instruction?.minReviews ?: 0 when (state) { is StepQuizReviewFeature.State.SubmissionNotMade, is StepQuizReviewFeature.State.SubmissionNotSelected -> { - reviewStep4Title.setText(R.string.step_quiz_review_taken_pending_zero) - setStepStatus(reviewStep4Title, reviewStep4Link, reviewStep4Status, ReviewStatusView.Status.PENDING) - reviewStep4Container.isVisible = false - reviewStep4Hint.isVisible = false + requiredPeerBinding.reviewStep4Title.setText(R.string.step_quiz_review_taken_pending_zero) + setStepStatus( + requiredPeerBinding.reviewStep4Title, + requiredPeerBinding.reviewStep4Link, + requiredPeerBinding.reviewStep4Status, + ReviewStatusView.Status.PENDING + ) + requiredPeerBinding.reviewStep4Container.isVisible = false + requiredPeerBinding.reviewStep4Hint.isVisible = false } is StepQuizReviewFeature.State.SubmissionSelected -> { val takenReviewCount = state.session.takenReviews.size @@ -255,14 +319,14 @@ class StepQuizReviewDelegate( } else { R.plurals.step_quiz_review_taken_pending } - append(resources.getQuantityString(pluralRes, remainingReviewCount, remainingReviewCount)) + append(headerBinding.root.resources.getQuantityString(pluralRes, remainingReviewCount, remainingReviewCount)) } if (takenReviewCount > 0) { if (isNotEmpty()) { append(" ") } - append(resources.getQuantityString(R.plurals.step_quiz_review_taken_completed, takenReviewCount, takenReviewCount)) + append(headerBinding.root.resources.getQuantityString(R.plurals.step_quiz_review_taken_completed, takenReviewCount, takenReviewCount)) } } @@ -273,27 +337,38 @@ class StepQuizReviewDelegate( ReviewStatusView.Status.COMPLETED } - reviewStep4Title.text = text - setStepStatus(reviewStep4Title, reviewStep4Link, reviewStep4Status, status) - - reviewStep4Container.isVisible = takenReviewCount > 0 - reviewStep4Container.setOnClickListener { actionListener.onTakenReviewClicked(state.session.id) } - reviewStep4Hint.isVisible = takenReviewCount == 0 + requiredPeerBinding.reviewStep4Title.text = text + setStepStatus( + requiredPeerBinding.reviewStep4Title, + requiredPeerBinding.reviewStep4Link, + requiredPeerBinding.reviewStep4Status, + status + ) + + requiredPeerBinding.reviewStep4Container.isVisible = takenReviewCount > 0 + requiredPeerBinding.reviewStep4Container.setOnClickListener { actionListener.onTakenReviewClicked(state.session.id) } + requiredPeerBinding.reviewStep4Hint.isVisible = takenReviewCount == 0 } is StepQuizReviewFeature.State.Completed -> { val takenReviewCount = state.session.takenReviews.size - reviewStep4Title.text = resources.getQuantityString(R.plurals.step_quiz_review_taken_completed, takenReviewCount, takenReviewCount) - setStepStatus(reviewStep4Title, reviewStep4Link, reviewStep4Status, ReviewStatusView.Status.COMPLETED) - reviewStep4Container.isVisible = takenReviewCount > 0 - reviewStep4Container.setOnClickListener { actionListener.onTakenReviewClicked(state.session.id) } - reviewStep4Hint.isVisible = false + requiredPeerBinding.reviewStep4Title.text = + headerBinding.root.resources.getQuantityString(R.plurals.step_quiz_review_taken_completed, takenReviewCount, takenReviewCount) + setStepStatus( + requiredPeerBinding.reviewStep4Title, + requiredPeerBinding.reviewStep4Link, + requiredPeerBinding.reviewStep4Status, + ReviewStatusView.Status.COMPLETED + ) + requiredPeerBinding.reviewStep4Container.isVisible = takenReviewCount > 0 + requiredPeerBinding.reviewStep4Container.setOnClickListener { actionListener.onTakenReviewClicked(state.session.id) } + requiredPeerBinding.reviewStep4Hint.isVisible = false } else -> Unit } } private fun renderStep5(state: StepQuizReviewFeature.State) { - reviewStep5Status.position = + footerBinding.reviewStep5Status.position = when (instructionType) { ReviewStrategyType.PEER -> 5 ReviewStrategyType.INSTRUCTOR -> 3 @@ -303,9 +378,9 @@ class StepQuizReviewDelegate( is StepQuizReviewFeature.State.Completed -> { val receivedPoints = state.progress?.score?.toFloatOrNull() ?: 0f - reviewStep5Title.text = ProgressTextMapper + footerBinding.reviewStep5Title.text = ProgressTextMapper .mapProgressToText( - containerView.context, + footerBinding.reviewStep5Title.context, receivedPoints, state.progress?.cost ?: 0, R.string.step_quiz_review_peer_completed, @@ -315,16 +390,21 @@ class StepQuizReviewDelegate( when (instructionType) { ReviewStrategyType.PEER -> - reviewStep5Container.isVisible = false + footerBinding.reviewStep5Container.isVisible = false ReviewStrategyType.INSTRUCTOR -> { - reviewStep5Container.setOnClickListener { actionListener.onTakenReviewClicked(state.session.id) } - reviewStep5Container.isVisible = true + footerBinding.reviewStep5Container.setOnClickListener { actionListener.onTakenReviewClicked(state.session.id) } + footerBinding.reviewStep5Container.isVisible = true } } - setStepStatus(reviewStep5Title, reviewStep5Link, reviewStep5Status, ReviewStatusView.Status.IN_PROGRESS) - reviewStep5Status.status = ReviewStatusView.Status.COMPLETED - reviewStep5Hint.isVisible = false + setStepStatus( + footerBinding.reviewStep5Title, + reviewStep5Link, + footerBinding.reviewStep5Status, + ReviewStatusView.Status.IN_PROGRESS + ) + footerBinding.reviewStep5Status.status = ReviewStatusView.Status.COMPLETED + footerBinding.reviewStep5Hint.isVisible = false } else -> { val cost = state.safeCast()?.progress?.cost ?: 0L @@ -339,8 +419,12 @@ class StepQuizReviewDelegate( R.string.step_quiz_review_instructor_pending } - reviewStep5Title.text = resources.getString(stringRes, resources.getQuantityString(R.plurals.points, cost.toInt(), cost)) - reviewStep5Container.isVisible = false + footerBinding.reviewStep5Title.text = + headerBinding.root.resources.getString( + stringRes, + headerBinding.root.resources.getQuantityString(R.plurals.points, cost.toInt(), cost) + ) + footerBinding.reviewStep5Container.isVisible = false val status = if (state is StepQuizReviewFeature.State.SubmissionSelected && instructionType == ReviewStrategyType.INSTRUCTOR) { ReviewStatusView.Status.IN_PROGRESS @@ -348,9 +432,15 @@ class StepQuizReviewDelegate( ReviewStatusView.Status.PENDING } - reviewStep5Hint.isVisible = instructionType == ReviewStrategyType.INSTRUCTOR && status == ReviewStatusView.Status.IN_PROGRESS + footerBinding.reviewStep5Hint.isVisible = + instructionType == ReviewStrategyType.INSTRUCTOR && status == ReviewStatusView.Status.IN_PROGRESS - setStepStatus(reviewStep5Title, reviewStep5Link, reviewStep5Status, status) + setStepStatus( + footerBinding.reviewStep5Title, + reviewStep5Link, + footerBinding.reviewStep5Status, + status + ) } } } @@ -371,4 +461,4 @@ class StepQuizReviewDelegate( fun onStartReviewClicked() fun onTakenReviewClicked(sessionId: Long) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_review/ui/factory/StepQuizFormReviewFactory.kt b/app/src/main/java/org/stepik/android/view/step_quiz_review/ui/factory/StepQuizFormReviewFactory.kt index 7ae40fab9e..e0c7da4d02 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_review/ui/factory/StepQuizFormReviewFactory.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_review/ui/factory/StepQuizFormReviewFactory.kt @@ -1,8 +1,16 @@ package org.stepik.android.view.step_quiz_review.ui.factory import android.view.View +import android.widget.TextView import androidx.fragment.app.FragmentManager import org.stepic.droid.R +import org.stepic.droid.databinding.FragmentStepQuizBinding +import org.stepic.droid.databinding.LayoutStepQuizReviewHeaderBinding +import org.stepic.droid.databinding.LayoutStepQuizChoiceBinding +import org.stepic.droid.databinding.LayoutStepQuizFillBlanksBinding +import org.stepic.droid.databinding.LayoutStepQuizSortingBinding +import org.stepic.droid.databinding.LayoutStepQuizTableBinding +import org.stepic.droid.databinding.LayoutStepQuizTextBinding import org.stepic.droid.util.AppConstants import org.stepik.android.presentation.step_quiz.model.ReplyResult import org.stepik.android.view.step_quiz.ui.delegate.StepQuizFormDelegate @@ -43,28 +51,85 @@ class StepQuizFormReviewFactory( R.layout.fragment_step_quiz_unsupported } - override fun getDelegateForStep(blockName: String?, view: View): StepQuizFormDelegate? = + override fun getDelegateForStep( + blockName: String?, + stepQuizBinding: FragmentStepQuizBinding, + quizView: View + ): StepQuizFormDelegate? = + createDelegateForStep( + blockName = blockName, + rootView = stepQuizBinding.root, + stepQuizDescription = stepQuizBinding.stepQuizDescription, + quizView = quizView + ) + + fun getDelegateForStep( + blockName: String?, + reviewHeaderBinding: LayoutStepQuizReviewHeaderBinding, + quizView: View + ): StepQuizFormDelegate? = + createDelegateForStep( + blockName = blockName, + rootView = reviewHeaderBinding.root, + stepQuizDescription = reviewHeaderBinding.stepQuizDescription, + quizView = quizView + ) + + private fun createDelegateForStep( + blockName: String?, + rootView: View, + stepQuizDescription: TextView, + quizView: View + ): StepQuizFormDelegate? = when (blockName) { AppConstants.TYPE_STRING, AppConstants.TYPE_NUMBER, AppConstants.TYPE_MATH, AppConstants.TYPE_FREE_ANSWER -> - TextStepQuizFormDelegate(view, blockName, onQuizChanged) + TextStepQuizFormDelegate( + LayoutStepQuizTextBinding.bind(quizView).stringStepQuizField, + stepQuizDescription, + blockName, + onQuizChanged + ) AppConstants.TYPE_CHOICE -> - ChoiceStepQuizFormDelegate(view, onQuizChanged) + ChoiceStepQuizFormDelegate( + stepQuizDescription, + LayoutStepQuizChoiceBinding.bind(quizView).choicesRecycler, + onQuizChanged + ) AppConstants.TYPE_SORTING -> - SortingStepQuizFormDelegate(view, onQuizChanged) + SortingStepQuizFormDelegate( + stepQuizDescription, + LayoutStepQuizSortingBinding.bind(quizView).sortingRecycler, + onQuizChanged + ) AppConstants.TYPE_MATCHING -> - MatchingStepQuizFormDelegate(view, onQuizChanged) + MatchingStepQuizFormDelegate( + stepQuizDescription, + LayoutStepQuizSortingBinding.bind(quizView).sortingRecycler, + onQuizChanged + ) AppConstants.TYPE_FILL_BLANKS -> - FillBlanksStepQuizFormDelegate(view, fragmentManager, onQuizChanged) + FillBlanksStepQuizFormDelegate( + rootView, + stepQuizDescription, + LayoutStepQuizFillBlanksBinding.bind(quizView).fillBlanksRecycler, + fragmentManager, + onQuizChanged + ) AppConstants.TYPE_TABLE -> - TableStepQuizFormDelegate(view, fragmentManager, onQuizChanged) + TableStepQuizFormDelegate( + stepQuizDescription, + LayoutStepQuizTableBinding.bind(quizView).tableRecycler, + fragmentManager, + onQuizChanged + ) else -> null diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_review/ui/fragment/StepQuizReviewFragment.kt b/app/src/main/java/org/stepik/android/view/step_quiz_review/ui/fragment/StepQuizReviewFragment.kt index 86487f2512..645673ea40 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_review/ui/fragment/StepQuizReviewFragment.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_review/ui/fragment/StepQuizReviewFragment.kt @@ -8,16 +8,17 @@ import androidx.annotation.LayoutRes import androidx.annotation.StringRes import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider +import by.kirich1409.viewbindingdelegate.viewBinding import com.jakewharton.rxrelay2.BehaviorRelay -import kotlinx.android.synthetic.main.error_no_connection_with_button_small.view.* -import kotlinx.android.synthetic.main.fragment_step_quiz_review.* -import kotlinx.android.synthetic.main.fragment_step_quiz_review_peer.* -import kotlinx.android.synthetic.main.layout_step_quiz_review_header.* -import kotlinx.android.synthetic.main.layout_step_quiz_review_header.view.* import org.stepic.droid.R import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App +import org.stepic.droid.databinding.FragmentStepQuizReviewBinding +import org.stepic.droid.databinding.FragmentStepQuizReviewInstructorBinding +import org.stepic.droid.databinding.FragmentStepQuizReviewPeerBinding +import org.stepic.droid.databinding.LayoutStepQuizReviewFooterBinding +import org.stepic.droid.databinding.LayoutStepQuizReviewHeaderBinding import org.stepic.droid.persistence.model.StepPersistentWrapper import org.stepic.droid.ui.util.snackbar import org.stepic.droid.util.AppConstants @@ -34,7 +35,6 @@ import org.stepik.android.presentation.step_quiz_review.StepQuizReviewViewModel import org.stepik.android.view.in_app_web_view.ui.dialog.InAppWebViewDialogFragment import org.stepik.android.view.step_quiz.ui.delegate.StepQuizDelegate import org.stepik.android.view.step_quiz.ui.delegate.StepQuizFeedbackBlocksDelegate -import org.stepik.android.view.step_quiz.ui.factory.StepQuizFormFactory import org.stepik.android.view.step_quiz_review.routing.StepQuizReviewDeepLinkBuilder import org.stepik.android.view.step_quiz_review.ui.delegate.StepQuizReviewDelegate import org.stepik.android.view.step_quiz_review.ui.factory.StepQuizFormReviewFactory @@ -98,8 +98,14 @@ class StepQuizReviewFragment : private lateinit var viewStateDelegate: ViewStateDelegate private lateinit var quizView: View + private lateinit var reviewContainerView: View + private lateinit var reviewHeaderBinding: LayoutStepQuizReviewHeaderBinding + private lateinit var reviewFooterBinding: LayoutStepQuizReviewFooterBinding + private lateinit var reviewStep5Link: View + private var reviewPeerBinding: FragmentStepQuizReviewPeerBinding? = null - private lateinit var stepQuizFormFactory: StepQuizFormFactory + private lateinit var stepQuizFormFactory: StepQuizFormReviewFactory + private val binding: FragmentStepQuizReviewBinding by viewBinding(FragmentStepQuizReviewBinding::bind) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -131,26 +137,44 @@ class StepQuizReviewFragment : // we don't pass [root] in order to clear margins quizView = inflater.inflate(stepQuizFormFactory.getLayoutResForStep(stepWrapper.step.block?.name), null) - inflater.inflate(layoutId, view) - .also { - it.reviewStep1Container.addView(quizView) + reviewContainerView = inflater.inflate(layoutId, view, false) + view.addView(reviewContainerView) + + reviewHeaderBinding = LayoutStepQuizReviewHeaderBinding.bind(reviewContainerView) + reviewFooterBinding = LayoutStepQuizReviewFooterBinding.bind(reviewContainerView) + reviewPeerBinding = null + reviewStep5Link = + when (instructionType) { + ReviewStrategyType.PEER -> + FragmentStepQuizReviewPeerBinding + .bind(reviewContainerView) + .also { reviewPeerBinding = it } + .reviewStep5Link + + ReviewStrategyType.INSTRUCTOR -> + FragmentStepQuizReviewInstructorBinding + .bind(reviewContainerView) + .reviewStep5Link } + reviewHeaderBinding.reviewStep1Container.addView(quizView) + return view } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { viewStateDelegate = ViewStateDelegate() - viewStateDelegate.addState(stepQuizReviewLoading) - viewStateDelegate.addState(stepQuizReviewLoading) - viewStateDelegate.addState(stepQuizReviewNetworkError) - viewStateDelegate.addState(stepQuizReviewContainer) - viewStateDelegate.addState(stepQuizReviewContainer) - viewStateDelegate.addState(stepQuizReviewContainer) - viewStateDelegate.addState(stepQuizReviewContainer) - - stepQuizReviewNetworkError.tryAgain - .setOnClickListener { stepQuizReviewViewModel.onNewMessage(StepQuizReviewFeature.Message.InitWithStep(stepWrapper, lessonData, forceUpdate = true)) } + viewStateDelegate.addState(binding.stepQuizReviewLoading) + viewStateDelegate.addState(binding.stepQuizReviewLoading) + viewStateDelegate.addState(binding.stepQuizReviewNetworkError.root) + viewStateDelegate.addState(reviewContainerView) + viewStateDelegate.addState(reviewContainerView) + viewStateDelegate.addState(reviewContainerView) + viewStateDelegate.addState(reviewContainerView) + + binding.stepQuizReviewNetworkError.tryAgain.setOnClickListener { + stepQuizReviewViewModel.onNewMessage(StepQuizReviewFeature.Message.InitWithStep(stepWrapper, lessonData, forceUpdate = true)) + } val actionListener = object : StepQuizReviewDelegate.ActionListener { override fun onSelectDifferentSubmissionClicked() { @@ -190,25 +214,36 @@ class StepQuizReviewFragment : } val blockName = stepWrapper.step.block?.name - val stepQuizBlockDelegate = StepQuizFeedbackBlocksDelegate(quizFeedbackView, isTeacher = false, hasReview = false) {} + val stepQuizBlockDelegate = StepQuizFeedbackBlocksDelegate(reviewHeaderBinding.quizFeedbackView.root, isTeacher = false, hasReview = false) {} + val quizDelegate = StepQuizDelegate( step = stepWrapper.step, stepQuizLessonData = StepQuizLessonData(lessonData), - stepQuizFormDelegate = stepQuizFormFactory.getDelegateForStep(blockName, view) ?: throw IllegalStateException("Unsupported quiz"), + stepQuizFormDelegate = + stepQuizFormFactory.getDelegateForStep( + blockName, + reviewHeaderBinding, + quizView + ) ?: throw IllegalStateException("Unsupported quiz"), stepQuizFeedbackBlocksDelegate = stepQuizBlockDelegate, - stepQuizActionButton = reviewStep1ActionButton, - stepRetryButton = reviewStep1ActionRetry, + stepQuizActionButton = reviewHeaderBinding.reviewStep1ActionButton, + stepRetryButton = reviewHeaderBinding.reviewStep1ActionRetry, - stepQuizDiscountingPolicy = reviewStep1Discounting, + stepQuizDiscountingPolicy = reviewHeaderBinding.reviewStep1Discounting, stepQuizReviewTeacherMessage = null, onNewMessage = { stepQuizReviewViewModel.onNewMessage(StepQuizReviewFeature.Message.StepQuizMessage(it)) } ) delegate = StepQuizReviewDelegate( - view, instructionType, actionListener, + reviewHeaderBinding, + reviewFooterBinding, + reviewStep5Link, + reviewPeerBinding, + instructionType, + actionListener, blockName, quizView, quizDelegate, @@ -253,4 +288,4 @@ class StepQuizReviewFragment : private fun syncReplyState(replyResult: ReplyResult) { stepQuizReviewViewModel.onNewMessage(StepQuizReviewFeature.Message.StepQuizMessage(StepQuizFeature.Message.SyncReply(replyResult.reply))) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_review/ui/fragment/StepQuizReviewTeacherFragment.kt b/app/src/main/java/org/stepik/android/view/step_quiz_review/ui/fragment/StepQuizReviewTeacherFragment.kt index 35c191be67..3c15125d37 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_review/ui/fragment/StepQuizReviewTeacherFragment.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_review/ui/fragment/StepQuizReviewTeacherFragment.kt @@ -10,16 +10,13 @@ import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider +import by.kirich1409.viewbindingdelegate.viewBinding import com.jakewharton.rxrelay2.BehaviorRelay -import kotlinx.android.synthetic.main.error_no_connection_with_button_small.view.* -import kotlinx.android.synthetic.main.fragment_step_quiz.* -import kotlinx.android.synthetic.main.fragment_step_quiz.view.* -import kotlinx.android.synthetic.main.fragment_step_quiz_review_teacher.* -import kotlinx.android.synthetic.main.fragment_step_quiz_review_teacher.view.* -import kotlinx.android.synthetic.main.view_step_quiz_submit_button.* import org.stepic.droid.R import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App +import org.stepic.droid.databinding.FragmentStepQuizBinding +import org.stepic.droid.databinding.FragmentStepQuizReviewTeacherBinding import org.stepic.droid.persistence.model.StepPersistentWrapper import org.stepic.droid.ui.util.collapse import org.stepic.droid.ui.util.expand @@ -90,6 +87,8 @@ class StepQuizReviewTeacherFragment : private lateinit var viewStateDelegate: ViewStateDelegate private lateinit var quizViewStateDelegate: ViewStateDelegate + private val binding: FragmentStepQuizReviewTeacherBinding by viewBinding(FragmentStepQuizReviewTeacherBinding::bind) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -105,31 +104,30 @@ class StepQuizReviewTeacherFragment : } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - val view = inflater.inflate(R.layout.fragment_step_quiz_review_teacher, container, false) - val quizContainer = view.stepQuizReviewTeacherQuiz as ConstraintLayout + val viewBinding = FragmentStepQuizReviewTeacherBinding.inflate(inflater, container, false) + val stepQuizBinding = viewBinding.stepQuizReviewTeacherQuiz + val quizContainer = stepQuizBinding.root val quizLayoutRes = stepQuizFormFactory.getLayoutResForStep(stepWrapper.step.block?.name) quizLayout = inflater.inflate(quizLayoutRes, quizContainer, false) quizContainer.addView(quizLayout) - realignQuizLayout(quizContainer, quizLayout) - return view + realignQuizLayout(stepQuizBinding, quizLayout) + return viewBinding.root } /** * Align quiz container as vertical linear layout for smooth collapsing animation */ - private fun realignQuizLayout(quizContainer: ConstraintLayout, quizLayout: View) { - val feedbackBlocks = quizContainer.stepQuizFeedbackBlocks - + private fun realignQuizLayout(stepQuizBinding: FragmentStepQuizBinding, quizLayout: View) { quizLayout.updateLayoutParams { bottomToTop = ConstraintLayout.LayoutParams.UNSET } - feedbackBlocks.updateLayoutParams { + stepQuizBinding.stepQuizFeedbackBlocks.root.updateLayoutParams { bottomToTop = ConstraintLayout.LayoutParams.UNSET topToBottom = quizLayout.id topMargin = 16.toPx() } - quizContainer.stepQuizActionContainer.updateLayoutParams { - topToBottom = feedbackBlocks.id + stepQuizBinding.stepQuizActionContainer.root.updateLayoutParams { + topToBottom = stepQuizBinding.stepQuizFeedbackBlocks.root.id topMargin = 16.toPx() bottomMargin = 16.toPx() } @@ -138,53 +136,59 @@ class StepQuizReviewTeacherFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { viewStateDelegate = ViewStateDelegate() viewStateDelegate.addState( - stepQuizReviewTeacherQuizSkeleton, - stepQuizReviewTeacherButtonSkeleton + binding.stepQuizReviewTeacherQuizSkeleton, + binding.stepQuizReviewTeacherButtonSkeleton ) viewStateDelegate.addState( - stepQuizReviewTeacherQuizSkeleton, - stepQuizReviewTeacherButtonSkeleton + binding.stepQuizReviewTeacherQuizSkeleton, + binding.stepQuizReviewTeacherButtonSkeleton ) viewStateDelegate.addState( - stepQuizReviewTeacherNetworkError + binding.stepQuizReviewTeacherNetworkError.root ) viewStateDelegate.addState( - stepQuizReviewTeacherSpoiler, - stepQuizReviewTeacherContainer, - stepQuizReviewTeacherDescription, - stepQuizReviewTeacherSubmissions + binding.stepQuizReviewTeacherSpoiler, + binding.stepQuizReviewTeacherContainer, + binding.stepQuizReviewTeacherDescription, + binding.stepQuizReviewTeacherSubmissions ) + val stepQuizBinding = binding.stepQuizReviewTeacherQuiz quizViewStateDelegate = stepQuizViewStateDelegateFactory - .create(stepQuizReviewTeacherQuiz, quizLayout) + .create(stepQuizBinding.root, quizLayout) val blockName = stepWrapper.step.block?.name - stepQuizReviewTeacherSpoiler.setOnClickListener { - stepQuizReviewTeacherArrow.changeState() - if (stepQuizReviewTeacherArrow.isExpanded()) { - stepQuizReviewTeacherContainer.expand() + binding.stepQuizReviewTeacherSpoiler.setOnClickListener { + binding.stepQuizReviewTeacherArrow.changeState() + if (binding.stepQuizReviewTeacherArrow.isExpanded()) { + binding.stepQuizReviewTeacherContainer.expand() } else { - stepQuizReviewTeacherContainer.collapse() + binding.stepQuizReviewTeacherContainer.collapse() } } - stepQuizReviewTeacherMessage.isVisible = false + stepQuizBinding.stepQuizReviewTeacherMessage.isVisible = false val stepQuizBlockDelegate = - StepQuizFeedbackBlocksDelegate(stepQuizFeedbackBlocks, isTeacher = false, hasReview = false) {} + StepQuizFeedbackBlocksDelegate(stepQuizBinding.stepQuizFeedbackBlocks.root, isTeacher = false, hasReview = false) {} quizDelegate = StepQuizDelegate( step = stepWrapper.step, stepQuizLessonData = StepQuizLessonData(lessonData), - stepQuizFormDelegate = stepQuizFormFactory.getDelegateForStep(blockName, view) ?: throw IllegalStateException("Unsupported quiz"), + stepQuizFormDelegate = + stepQuizFormFactory.getDelegateForStep( + blockName, + stepQuizBinding, + quizLayout + ) ?: throw IllegalStateException("Unsupported quiz"), stepQuizFeedbackBlocksDelegate = stepQuizBlockDelegate, - stepQuizActionButton = stepQuizAction, - stepRetryButton = stepQuizRetry, + stepQuizActionButton = stepQuizBinding.stepQuizActionContainer.stepQuizAction, + stepRetryButton = stepQuizBinding.stepQuizActionContainer.stepQuizRetry, - stepQuizDiscountingPolicy = stepQuizDiscountingPolicy, + stepQuizDiscountingPolicy = stepQuizBinding.stepQuizDiscountingPolicy, stepQuizReviewTeacherMessage = null, onNewMessage = { stepQuizReviewTeacherViewModel.onNewMessage(StepQuizReviewTeacherFeature.Message.StepQuizMessage(it)) @@ -194,18 +198,18 @@ class StepQuizReviewTeacherFragment : } ) - stepQuizReviewTeacherNetworkError.tryAgain.setOnClickListener { + binding.stepQuizReviewTeacherNetworkError.tryAgain.setOnClickListener { stepQuizReviewTeacherViewModel .onNewMessage(StepQuizReviewTeacherFeature.Message.InitWithStep(stepWrapper, lessonData, instructionType, forceUpdate = true)) } - stepQuizNetworkError.tryAgain.setOnClickListener { + stepQuizBinding.stepQuizNetworkError.tryAgain.setOnClickListener { val quizMessage = StepQuizFeature.Message.InitWithStep(stepWrapper, lessonData, forceUpdate = true) stepQuizReviewTeacherViewModel .onNewMessage(StepQuizReviewTeacherFeature.Message.StepQuizMessage(quizMessage)) } - stepQuizReviewTeacherSubmissions.setOnClickListener { + binding.stepQuizReviewTeacherSubmissions.setOnClickListener { parentFragment.safeCast() ?.showSubmissions() } @@ -214,16 +218,17 @@ class StepQuizReviewTeacherFragment : override fun render(state: StepQuizReviewTeacherFeature.State) { viewStateDelegate.switchState(state) if (state is StepQuizReviewTeacherFeature.State.Data) { - stepQuizReviewTeacherContainer.isVisible = - stepQuizReviewTeacherArrow.isExpanded() + binding.stepQuizReviewTeacherContainer.isVisible = + binding.stepQuizReviewTeacherArrow.isExpanded() quizViewStateDelegate.switchState(state.quizState) + val stepQuizReviewTeacherMessage = binding.stepQuizReviewTeacherQuiz.stepQuizReviewTeacherMessage stepQuizReviewTeacherMessage.isVisible = false if (state.quizState is StepQuizFeature.State.AttemptLoaded) { quizDelegate.setState(state.quizState) } - stepQuizReviewTeacherDescription.text = + binding.stepQuizReviewTeacherDescription.text = when (state.instructionType) { ReviewStrategyType.INSTRUCTOR -> if (state.availableReviewCount > 0) { @@ -252,4 +257,4 @@ class StepQuizReviewTeacherFragment : StepQuizReviewTeacherFeature.Message.StepQuizMessage( StepQuizFeature.Message.SyncReply(replyResult.reply))) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_review/ui/widget/ReviewStatusView.kt b/app/src/main/java/org/stepik/android/view/step_quiz_review/ui/widget/ReviewStatusView.kt index 2cd9e2e7e5..2e5b22d35d 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_review/ui/widget/ReviewStatusView.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_review/ui/widget/ReviewStatusView.kt @@ -3,15 +3,15 @@ package org.stepik.android.view.step_quiz_review.ui.widget import android.content.Context import android.content.res.ColorStateList import android.util.AttributeSet +import android.view.LayoutInflater import android.view.View import android.widget.FrameLayout import android.widget.TextView import androidx.appcompat.content.res.AppCompatResources import androidx.core.view.isVisible -import kotlinx.android.synthetic.main.view_review_status.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.ViewReviewStatusBinding import org.stepic.droid.util.resolveColorAttribute -import ru.nobird.android.view.base.ui.extension.inflate class ReviewStatusView @JvmOverloads @@ -55,10 +55,10 @@ constructor( } init { - val view = inflate(R.layout.view_review_status, true) + val binding = ViewReviewStatusBinding.inflate(LayoutInflater.from(context), this) - textView = view.peerReviewStatusText - imageView = view.peerReviewStatusImage + textView = binding.peerReviewStatusText + imageView = binding.peerReviewStatusImage val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ReviewStatusView) try { @@ -71,4 +71,4 @@ constructor( enum class Status { ERROR, PENDING, IN_PROGRESS, COMPLETED } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_sorting/ui/adapter/delegate/SortingOptionAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_sorting/ui/adapter/delegate/SortingOptionAdapterDelegate.kt index 2956db3637..e06da0c02b 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_sorting/ui/adapter/delegate/SortingOptionAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_sorting/ui/adapter/delegate/SortingOptionAdapterDelegate.kt @@ -3,8 +3,9 @@ package org.stepik.android.view.step_quiz_sorting.ui.adapter.delegate import android.view.View import android.view.ViewGroup import androidx.core.view.ViewCompat -import kotlinx.android.synthetic.main.item_step_quiz_sorting.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemStepQuizSortingBinding import org.stepik.android.view.latex.ui.widget.ProgressableWebViewClient import org.stepik.android.view.step_quiz_sorting.ui.model.SortingOption import ru.nobird.android.ui.adapterdelegates.AdapterDelegate @@ -22,27 +23,24 @@ class SortingOptionAdapterDelegate( ViewHolder(createView(parent, R.layout.item_step_quiz_sorting)) private inner class ViewHolder(root: View) : DelegateViewHolder(root) { - private val stepQuizSortingOption = root.stepQuizSortingOption - private val stepQuizSortingOptionProgress = root.stepQuizSortingOptionProgress - private val stepQuizSortingOptionUp = root.stepQuizSortingOptionUp - private val stepQuizSortingOptionDown = root.stepQuizSortingOptionDown + private val viewBinding: ItemStepQuizSortingBinding by viewBinding { ItemStepQuizSortingBinding.bind(root) } init { - stepQuizSortingOptionUp.setOnClickListener { onMoveItemClicked(adapterPosition, SortingDirection.UP) } - stepQuizSortingOptionDown.setOnClickListener { onMoveItemClicked(adapterPosition, SortingDirection.DOWN) } + viewBinding.stepQuizSortingOptionUp.setOnClickListener { onMoveItemClicked(adapterPosition, SortingDirection.UP) } + viewBinding.stepQuizSortingOptionDown.setOnClickListener { onMoveItemClicked(adapterPosition, SortingDirection.DOWN) } - stepQuizSortingOption.webViewClient = ProgressableWebViewClient(stepQuizSortingOptionProgress, stepQuizSortingOption.webView) + viewBinding.stepQuizSortingOption.webViewClient = ProgressableWebViewClient(viewBinding.stepQuizSortingOptionProgress, viewBinding.stepQuizSortingOption.webView) } override fun onBind(data: SortingOption) { itemView.isEnabled = data.isEnabled - stepQuizSortingOption.setText(data.option) + viewBinding.stepQuizSortingOption.setText(data.option) - stepQuizSortingOptionUp.isEnabled = data.isEnabled && adapterPosition != 0 - stepQuizSortingOptionUp.alpha = if (stepQuizSortingOptionUp.isEnabled) 1f else 0.2f + viewBinding.stepQuizSortingOptionUp.isEnabled = data.isEnabled && adapterPosition != 0 + viewBinding.stepQuizSortingOptionUp.alpha = if (viewBinding.stepQuizSortingOptionUp.isEnabled) 1f else 0.2f - stepQuizSortingOptionDown.isEnabled = data.isEnabled && adapterPosition + 1 != adapter.items.size - stepQuizSortingOptionDown.alpha = if (stepQuizSortingOptionDown.isEnabled) 1f else 0.2f + viewBinding.stepQuizSortingOptionDown.isEnabled = data.isEnabled && adapterPosition + 1 != adapter.items.size + viewBinding.stepQuizSortingOptionDown.alpha = if (viewBinding.stepQuizSortingOptionDown.isEnabled) 1f else 0.2f val elevation = if (data.isEnabled) context.resources.getDimension(R.dimen.step_quiz_sorting_item_elevation) else 0f ViewCompat.setElevation(itemView, elevation) @@ -52,4 +50,4 @@ class SortingOptionAdapterDelegate( enum class SortingDirection { UP, DOWN } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_sorting/ui/delegate/SortingStepQuizFormDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_sorting/ui/delegate/SortingStepQuizFormDelegate.kt index 9d65dc17b8..c5c970ae7c 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_sorting/ui/delegate/SortingStepQuizFormDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_sorting/ui/delegate/SortingStepQuizFormDelegate.kt @@ -1,11 +1,13 @@ package org.stepik.android.view.step_quiz_sorting.ui.delegate import android.view.View +import android.widget.TextView import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.SimpleItemAnimator -import kotlinx.android.synthetic.main.fragment_step_quiz.view.* -import kotlinx.android.synthetic.main.layout_step_quiz_sorting.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.FragmentStepQuizBinding +import org.stepic.droid.databinding.LayoutStepQuizSortingBinding import org.stepik.android.model.Reply import org.stepik.android.presentation.step_quiz.StepQuizFeature import org.stepik.android.presentation.step_quiz.model.ReplyResult @@ -18,10 +20,28 @@ import ru.nobird.app.core.model.swap import ru.nobird.android.ui.adapters.DefaultDelegateAdapter class SortingStepQuizFormDelegate( - containerView: View, + private val quizDescription: TextView, + private val sortingRecycler: RecyclerView, private val onQuizChanged: (ReplyResult) -> Unit ) : StepQuizFormDelegate { - private val quizDescription = containerView.stepQuizDescription + constructor( + stepQuizBinding: FragmentStepQuizBinding, + sortingStepQuizBinding: LayoutStepQuizSortingBinding, + onQuizChanged: (ReplyResult) -> Unit + ) : this( + stepQuizBinding.stepQuizDescription, + sortingStepQuizBinding.root, + onQuizChanged + ) + + constructor( + containerView: View, + onQuizChanged: (ReplyResult) -> Unit + ) : this( + containerView.findViewById(R.id.stepQuizDescription), + containerView.findViewById(R.id.sortingRecycler), + onQuizChanged + ) private val optionsAdapter = DefaultDelegateAdapter() @@ -32,7 +52,7 @@ class SortingStepQuizFormDelegate( optionsAdapter += SortingOptionAdapterDelegate(optionsAdapter, ::moveSortingOption) - with(containerView.sortingRecycler) { + with(sortingRecycler) { adapter = optionsAdapter isNestedScrollingEnabled = false layoutManager = LinearLayoutManager(context) @@ -73,4 +93,4 @@ class SortingStepQuizFormDelegate( override fun createReply(): ReplyResult = ReplyResult(Reply(ordering = optionsAdapter.items.map(SortingOption::id)), ReplyResult.Validation.Success) -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_sorting/ui/fragment/SortingStepQuizFragment.kt b/app/src/main/java/org/stepik/android/view/step_quiz_sorting/ui/fragment/SortingStepQuizFragment.kt index 65fcf75ef8..4bc4b2e1f5 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_sorting/ui/fragment/SortingStepQuizFragment.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_sorting/ui/fragment/SortingStepQuizFragment.kt @@ -1,9 +1,10 @@ package org.stepik.android.view.step_quiz_sorting.ui.fragment +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import androidx.fragment.app.Fragment -import kotlinx.android.synthetic.main.layout_step_quiz_sorting.* -import org.stepic.droid.R +import org.stepic.droid.databinding.LayoutStepQuizSortingBinding import org.stepik.android.presentation.step_quiz.StepQuizFeature import org.stepik.android.view.step_quiz.ui.delegate.StepQuizFormDelegate import org.stepik.android.view.step_quiz.ui.fragment.DefaultStepQuizFragment @@ -21,12 +22,23 @@ class SortingStepQuizFragment : } } - override val quizLayoutRes: Int = - R.layout.layout_step_quiz_sorting + private var _binding: LayoutStepQuizSortingBinding? = null + private val binding: LayoutStepQuizSortingBinding + get() = requireNotNull(_binding) override val quizViews: Array - get() = arrayOf(sortingRecycler) + get() = arrayOf(binding.root) - override fun createStepQuizFormDelegate(view: View): StepQuizFormDelegate = - SortingStepQuizFormDelegate(view, onQuizChanged = ::syncReplyState) -} \ No newline at end of file + override fun createStepView(layoutInflater: LayoutInflater, parent: ViewGroup): View { + return LayoutStepQuizSortingBinding.inflate(layoutInflater, parent, false).also { + _binding = it + }.root + } + + override fun createStepQuizFormDelegate(): StepQuizFormDelegate = + SortingStepQuizFormDelegate( + stepQuizBinding = stepQuizBinding, + sortingStepQuizBinding = binding, + onQuizChanged = ::syncReplyState + ) +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_sql/ui/delegate/SqlStepQuizFormDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_sql/ui/delegate/SqlStepQuizFormDelegate.kt index 0cc42cea63..b1b9e95d07 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_sql/ui/delegate/SqlStepQuizFormDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_sql/ui/delegate/SqlStepQuizFormDelegate.kt @@ -1,10 +1,12 @@ package org.stepik.android.view.step_quiz_sql.ui.delegate import android.view.View +import android.widget.TextView import androidx.core.widget.doAfterTextChanged -import kotlinx.android.synthetic.main.fragment_step_quiz.view.* -import kotlinx.android.synthetic.main.layout_step_quiz_code.view.* import org.stepic.droid.R +import org.stepic.droid.code.ui.CodeEditorLayout +import org.stepic.droid.databinding.FragmentStepQuizBinding +import org.stepic.droid.databinding.LayoutStepQuizSqlBinding import org.stepic.droid.model.code.ProgrammingLanguage import org.stepik.android.model.Reply import org.stepik.android.presentation.step_quiz.StepQuizFeature @@ -13,14 +15,33 @@ import org.stepik.android.view.step_quiz.resolver.StepQuizFormResolver import org.stepik.android.view.step_quiz.ui.delegate.StepQuizFormDelegate class SqlStepQuizFormDelegate( - containerView: View, + private val quizDescription: TextView, + private val codeLayout: CodeEditorLayout, private val onFullscreenClicked: (lang: String, code: String) -> Unit, private val onQuizChanged: (ReplyResult) -> Unit ) : StepQuizFormDelegate { + constructor( + stepQuizBinding: FragmentStepQuizBinding, + sqlStepQuizBinding: LayoutStepQuizSqlBinding, + onFullscreenClicked: (lang: String, code: String) -> Unit, + onQuizChanged: (ReplyResult) -> Unit + ) : this( + stepQuizBinding.stepQuizDescription, + sqlStepQuizBinding.codeStepLayout, + onFullscreenClicked, + onQuizChanged + ) - private val quizDescription = containerView.stepQuizDescription - - private val codeLayout = containerView.codeStepLayout + constructor( + containerView: View, + onFullscreenClicked: (lang: String, code: String) -> Unit, + onQuizChanged: (ReplyResult) -> Unit + ) : this( + containerView.findViewById(R.id.stepQuizDescription), + containerView.findViewById(R.id.codeStepLayout), + onFullscreenClicked, + onQuizChanged + ) init { quizDescription.setText(R.string.step_quiz_sql_description) @@ -51,4 +72,4 @@ class SqlStepQuizFormDelegate( fun updateCodeLayoutFromDialog(code: String) { codeLayout.setText(code) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_sql/ui/fragment/SqlStepQuizFragment.kt b/app/src/main/java/org/stepik/android/view/step_quiz_sql/ui/fragment/SqlStepQuizFragment.kt index a8b0ba837d..a2597df139 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_sql/ui/fragment/SqlStepQuizFragment.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_sql/ui/fragment/SqlStepQuizFragment.kt @@ -1,9 +1,10 @@ package org.stepik.android.view.step_quiz_sql.ui.fragment +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import androidx.fragment.app.Fragment -import kotlinx.android.synthetic.main.layout_step_quiz_code.stepQuizCodeContainer -import org.stepic.droid.R +import org.stepic.droid.databinding.LayoutStepQuizSqlBinding import org.stepic.droid.model.code.ProgrammingLanguage import org.stepik.android.presentation.step_quiz.StepQuizFeature import org.stepik.android.view.step_quiz.ui.delegate.StepQuizFormDelegate @@ -27,15 +28,23 @@ class SqlStepQuizFragment : private lateinit var sqlStepQuizFormDelegate: SqlStepQuizFormDelegate - override val quizLayoutRes: Int = - R.layout.layout_step_quiz_sql + private var _binding: LayoutStepQuizSqlBinding? = null + private val binding: LayoutStepQuizSqlBinding + get() = requireNotNull(_binding) override val quizViews: Array - get() = arrayOf(stepQuizCodeContainer) + get() = arrayOf(binding.root) - override fun createStepQuizFormDelegate(view: View): StepQuizFormDelegate { + override fun createStepView(layoutInflater: LayoutInflater, parent: ViewGroup): View { + return LayoutStepQuizSqlBinding.inflate(layoutInflater, parent, false).also { + _binding = it + }.root + } + + override fun createStepQuizFormDelegate(): StepQuizFormDelegate { sqlStepQuizFormDelegate = SqlStepQuizFormDelegate( - containerView = view, + stepQuizBinding = stepQuizBinding, + sqlStepQuizBinding = binding, onFullscreenClicked = ::onFullScreenClicked, onQuizChanged = ::syncReplyState ) @@ -56,4 +65,4 @@ class SqlStepQuizFragment : .newInstance(lang, code, mapOf(ProgrammingLanguage.SQL.serverPrintableName to ""), stepWrapper, lessonData.lesson.title.orEmpty()) .showIfNotExists(childFragmentManager, CodeStepQuizFullScreenDialogFragment.TAG) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/adapter/delegate/TableColumnMultipleSelectionItemAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/adapter/delegate/TableColumnMultipleSelectionItemAdapterDelegate.kt index b8557303ab..5c94622b31 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/adapter/delegate/TableColumnMultipleSelectionItemAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/adapter/delegate/TableColumnMultipleSelectionItemAdapterDelegate.kt @@ -2,8 +2,9 @@ package org.stepik.android.view.step_quiz_table.ui.adapter.delegate import android.view.View import android.view.ViewGroup -import kotlinx.android.synthetic.main.item_table_column_selection_checkbox.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemTableColumnSelectionCheckboxBinding import org.stepik.android.model.Cell import org.stepik.android.view.latex.ui.widget.ProgressableWebViewClient import ru.nobird.android.ui.adapterdelegates.AdapterDelegate @@ -21,21 +22,19 @@ class TableColumnMultipleSelectionItemAdapterDelegate( ViewHolder(createView(parent, R.layout.item_table_column_selection_checkbox)) private inner class ViewHolder(root: View) : DelegateViewHolder(root) { - private val tableColumnCheckBox = root.tableColumnSelectionCheckBox - private val tableColumnText = root.tableColumnSelectionText - private val tableColumnTextProgress = root.tableColumnSelectionTextProgress + private val viewBinding: ItemTableColumnSelectionCheckboxBinding by viewBinding { ItemTableColumnSelectionCheckboxBinding.bind(root) } init { root.setOnClickListener { onClick(itemData as Cell) } - tableColumnText.webViewClient = ProgressableWebViewClient(tableColumnTextProgress, tableColumnText.webView) + viewBinding.tableColumnSelectionText.webViewClient = ProgressableWebViewClient(viewBinding.tableColumnSelectionTextProgress, viewBinding.tableColumnSelectionText.webView) } override fun onBind(data: Cell) { itemView.isSelected = selectionHelper.isSelected(adapterPosition) - tableColumnCheckBox.isChecked = selectionHelper.isSelected(adapterPosition) - tableColumnText.setText(data.name) + viewBinding.tableColumnSelectionCheckBox.isChecked = selectionHelper.isSelected(adapterPosition) + viewBinding.tableColumnSelectionText.setText(data.name) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/adapter/delegate/TableColumnSingleSelectionItemAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/adapter/delegate/TableColumnSingleSelectionItemAdapterDelegate.kt index 4e52104013..70bf69e788 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/adapter/delegate/TableColumnSingleSelectionItemAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/adapter/delegate/TableColumnSingleSelectionItemAdapterDelegate.kt @@ -2,10 +2,9 @@ package org.stepik.android.view.step_quiz_table.ui.adapter.delegate import android.view.View import android.view.ViewGroup -import kotlinx.android.synthetic.main.item_table_column_selection_radiobutton.view.* -import kotlinx.android.synthetic.main.item_table_column_selection_radiobutton.view.tableColumnSelectionText -import kotlinx.android.synthetic.main.item_table_column_selection_radiobutton.view.tableColumnSelectionTextProgress +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemTableColumnSelectionRadiobuttonBinding import org.stepik.android.model.Cell import org.stepik.android.view.latex.ui.widget.ProgressableWebViewClient import ru.nobird.android.ui.adapterdelegates.AdapterDelegate @@ -23,21 +22,19 @@ class TableColumnSingleSelectionItemAdapterDelegate( ViewHolder(createView(parent, R.layout.item_table_column_selection_radiobutton)) private inner class ViewHolder(root: View) : DelegateViewHolder(root) { - private val tableColumnRadioButton = root.tableColumnSelectionRadioButton - private val tableColumnText = root.tableColumnSelectionText - private val tableColumnTextProgress = root.tableColumnSelectionTextProgress + private val viewBinding: ItemTableColumnSelectionRadiobuttonBinding by viewBinding { ItemTableColumnSelectionRadiobuttonBinding.bind(root) } init { root.setOnClickListener { onClick(itemData as Cell) } - tableColumnText.webViewClient = ProgressableWebViewClient(tableColumnTextProgress, tableColumnText.webView) + viewBinding.tableColumnSelectionText.webViewClient = ProgressableWebViewClient(viewBinding.tableColumnSelectionTextProgress, viewBinding.tableColumnSelectionText.webView) } override fun onBind(data: Cell) { itemView.isSelected = selectionHelper.isSelected(adapterPosition) - tableColumnRadioButton.isChecked = selectionHelper.isSelected(adapterPosition) - tableColumnText.setText(data.name) + viewBinding.tableColumnSelectionRadioButton.isChecked = selectionHelper.isSelected(adapterPosition) + viewBinding.tableColumnSelectionText.setText(data.name) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/adapter/delegate/TableSelectionItemAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/adapter/delegate/TableSelectionItemAdapterDelegate.kt index b9388f6e1c..a8c7ffbdc1 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/adapter/delegate/TableSelectionItemAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/adapter/delegate/TableSelectionItemAdapterDelegate.kt @@ -3,8 +3,9 @@ package org.stepik.android.view.step_quiz_table.ui.adapter.delegate import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible -import kotlinx.android.synthetic.main.item_table_selection.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemTableSelectionBinding import org.stepik.android.model.Cell import org.stepik.android.view.latex.ui.widget.ProgressableWebViewClient import org.stepik.android.view.step_quiz_table.ui.model.TableSelectionItem @@ -24,27 +25,22 @@ class TableSelectionItemAdapterDelegate( ViewHolder(createView(parent, R.layout.item_table_selection)) private inner class ViewHolder(root: View) : DelegateViewHolder(root) { - private val viewOverlay = root.viewOverlay - private val stepQuizTableTitle = root.stepQuizTableTitleText - private val stepQuizTableTitleProgress = root.stepQuizTitleProgress - private val stepQuizTableChoice = root.stepQuizTableChoiceText - private val stepQuizTableChoiceProgress = root.stepQuizChoiceProgress - private val stepQuizTableChevron = root.stepQuizTableChevron + private val viewBinding: ItemTableSelectionBinding by viewBinding { ItemTableSelectionBinding.bind(root) } init { - viewOverlay.setOnClickListener { onItemClicked(adapterPosition, (itemData as TableSelectionItem).titleText, (itemData as TableSelectionItem).tableChoices) } - stepQuizTableTitle.webViewClient = ProgressableWebViewClient(stepQuizTableTitleProgress, stepQuizTableTitle.webView) - stepQuizTableChoice.webViewClient = ProgressableWebViewClient(stepQuizTableChoiceProgress, stepQuizTableChoice.webView) + viewBinding.viewOverlay.setOnClickListener { onItemClicked(adapterPosition, (itemData as TableSelectionItem).titleText, (itemData as TableSelectionItem).tableChoices) } + viewBinding.stepQuizTableTitleText.webViewClient = ProgressableWebViewClient(viewBinding.stepQuizTitleProgress, viewBinding.stepQuizTableTitleText.webView) + viewBinding.stepQuizTableChoiceText.webViewClient = ProgressableWebViewClient(viewBinding.stepQuizChoiceProgress, viewBinding.stepQuizTableChoiceText.webView) } override fun onBind(data: TableSelectionItem) { - viewOverlay.isEnabled = data.isEnabled - stepQuizTableChevron.alpha = if (data.isEnabled) 1f else 0.2f - stepQuizTableTitle.setText(data.titleText) + viewBinding.viewOverlay.isEnabled = data.isEnabled + viewBinding.stepQuizTableChevron.alpha = if (data.isEnabled) 1f else 0.2f + viewBinding.stepQuizTableTitleText.setText(data.titleText) val selectedChoices = data.tableChoices.filter { it.answer } - stepQuizTableChoice.isVisible = selectedChoices.isNotEmpty() - stepQuizTableChoice.setText(selectedChoices.joinToString(separator = SEPARATOR) { it.name }) + viewBinding.stepQuizTableChoiceText.isVisible = selectedChoices.isNotEmpty() + viewBinding.stepQuizTableChoiceText.setText(selectedChoices.joinToString(separator = SEPARATOR) { it.name }) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/delegate/TableStepQuizFormDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/delegate/TableStepQuizFormDelegate.kt index d18ec2a157..4d7984c35a 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/delegate/TableStepQuizFormDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/delegate/TableStepQuizFormDelegate.kt @@ -1,13 +1,15 @@ package org.stepik.android.view.step_quiz_table.ui.delegate import android.view.View +import android.widget.TextView import androidx.appcompat.content.res.AppCompatResources import androidx.fragment.app.FragmentManager import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.fragment_step_quiz.view.* -import kotlinx.android.synthetic.main.layout_step_quiz_table.view.* +import androidx.recyclerview.widget.RecyclerView import org.stepic.droid.R +import org.stepic.droid.databinding.FragmentStepQuizBinding +import org.stepic.droid.databinding.LayoutStepQuizTableBinding import org.stepik.android.model.Cell import org.stepik.android.model.Reply import org.stepik.android.model.TableChoiceAnswer @@ -24,11 +26,33 @@ import ru.nobird.android.ui.adapters.DefaultDelegateAdapter import ru.nobird.android.view.base.ui.extension.showIfNotExists class TableStepQuizFormDelegate( - containerView: View, + private val quizDescription: TextView, + private val tableRecycler: RecyclerView, private val fragmentManager: FragmentManager, private val onQuizChanged: (ReplyResult) -> Unit ) : StepQuizFormDelegate { - private val quizDescription = containerView.stepQuizDescription + constructor( + stepQuizBinding: FragmentStepQuizBinding, + tableStepQuizBinding: LayoutStepQuizTableBinding, + fragmentManager: FragmentManager, + onQuizChanged: (ReplyResult) -> Unit + ) : this( + stepQuizBinding.stepQuizDescription, + tableStepQuizBinding.root, + fragmentManager, + onQuizChanged + ) + + constructor( + containerView: View, + fragmentManager: FragmentManager, + onQuizChanged: (ReplyResult) -> Unit + ) : this( + containerView.findViewById(R.id.stepQuizDescription), + containerView.findViewById(R.id.tableRecycler), + fragmentManager, + onQuizChanged + ) private val tableAdapter = DefaultDelegateAdapter() @@ -44,7 +68,7 @@ class TableStepQuizFormDelegate( .newInstance(index, rowTitle, chosenColumns, isCheckBox) .showIfNotExists(fragmentManager, TableColumnSelectionBottomSheetDialogFragment.TAG) } - with(containerView.tableRecycler) { + with(tableRecycler) { itemAnimator = null adapter = tableAdapter isNestedScrollingEnabled = false @@ -81,4 +105,4 @@ class TableStepQuizFormDelegate( } onQuizChanged(createReply()) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/fragment/TableColumnSelectionBottomSheetDialogFragment.kt b/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/fragment/TableColumnSelectionBottomSheetDialogFragment.kt index a66f394ff4..5789b8f31c 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/fragment/TableColumnSelectionBottomSheetDialogFragment.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/fragment/TableColumnSelectionBottomSheetDialogFragment.kt @@ -7,11 +7,12 @@ import android.view.ViewGroup import androidx.annotation.StringRes import androidx.fragment.app.DialogFragment import androidx.recyclerview.widget.LinearLayoutManager +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import kotlinx.android.synthetic.main.bottom_sheet_dialog_table_columns_selection.* import org.stepic.droid.R +import org.stepic.droid.databinding.BottomSheetDialogTableColumnsSelectionBinding import org.stepik.android.model.Cell import org.stepik.android.view.step_quiz_table.ui.adapter.delegate.TableColumnMultipleSelectionItemAdapterDelegate import org.stepik.android.view.step_quiz_table.ui.adapter.delegate.TableColumnSingleSelectionItemAdapterDelegate @@ -37,6 +38,8 @@ class TableColumnSelectionBottomSheetDialogFragment : BottomSheetDialogFragment( } } + private val binding: BottomSheetDialogTableColumnsSelectionBinding by viewBinding(BottomSheetDialogTableColumnsSelectionBinding::bind) + private lateinit var selectionHelper: SelectionHelper private var index: Int by argument() @@ -68,8 +71,8 @@ class TableColumnSelectionBottomSheetDialogFragment : BottomSheetDialogFragment( chosenColumns.map { it.answer }.toBooleanArray() } - tableColumnSelectionRowTitle.setText(rowTitle) - with(tableColumnsRecycler) { + binding.tableColumnSelectionRowTitle.setText(rowTitle) + with(binding.tableColumnsRecycler) { adapter = columnsAdapter isNestedScrollingEnabled = false layoutManager = LinearLayoutManager(context) @@ -86,13 +89,13 @@ class TableColumnSelectionBottomSheetDialogFragment : BottomSheetDialogFragment( } else { R.string.step_quiz_table_single_choice to TableColumnSingleSelectionItemAdapterDelegate(selectionHelper, ::handleColumnSelectionClick) } - tableColumnSelectionInformation.setText(description) + binding.tableColumnSelectionInformation.setText(description) columnsAdapter += delegate columnsAdapter.items = chosenColumns mapSelection(selected) // This is necessary to avoid java.lang.IllegalArgumentException: parameter must be a descendant of this view - tableNested.descendantFocusability = ViewGroup.FOCUS_BLOCK_DESCENDANTS - confirmColumnsAction.setOnClickListener { dismiss() } + binding.tableNested.descendantFocusability = ViewGroup.FOCUS_BLOCK_DESCENDANTS + binding.confirmColumnsAction.setOnClickListener { dismiss() } } override fun onPause() { @@ -133,4 +136,4 @@ class TableColumnSelectionBottomSheetDialogFragment : BottomSheetDialogFragment( interface Callback { fun onSyncChosenColumnsWithParent(index: Int, chosenRows: List) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/fragment/TableStepQuizFragment.kt b/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/fragment/TableStepQuizFragment.kt index e0a93ccdcb..b209bc05ff 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/fragment/TableStepQuizFragment.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_table/ui/fragment/TableStepQuizFragment.kt @@ -1,9 +1,10 @@ package org.stepik.android.view.step_quiz_table.ui.fragment +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import androidx.fragment.app.Fragment -import kotlinx.android.synthetic.main.layout_step_quiz_table.* -import org.stepic.droid.R +import org.stepic.droid.databinding.LayoutStepQuizTableBinding import org.stepik.android.model.Cell import org.stepik.android.presentation.step_quiz.StepQuizFeature import org.stepik.android.view.step_quiz.ui.delegate.StepQuizFormDelegate @@ -25,18 +26,30 @@ class TableStepQuizFragment : private lateinit var tableStepQuizFormDelegate: TableStepQuizFormDelegate - override val quizLayoutRes: Int = - R.layout.layout_step_quiz_table + private var _binding: LayoutStepQuizTableBinding? = null + private val binding: LayoutStepQuizTableBinding + get() = requireNotNull(_binding) override val quizViews: Array - get() = arrayOf(tableRecycler) + get() = arrayOf(binding.root) - override fun createStepQuizFormDelegate(view: View): StepQuizFormDelegate { - tableStepQuizFormDelegate = TableStepQuizFormDelegate(view, childFragmentManager, onQuizChanged = ::syncReplyState) + override fun createStepView(layoutInflater: LayoutInflater, parent: ViewGroup): View { + return LayoutStepQuizTableBinding.inflate(layoutInflater, parent, false).also { + _binding = it + }.root + } + + override fun createStepQuizFormDelegate(): StepQuizFormDelegate { + tableStepQuizFormDelegate = TableStepQuizFormDelegate( + stepQuizBinding = stepQuizBinding, + tableStepQuizBinding = binding, + fragmentManager = childFragmentManager, + onQuizChanged = ::syncReplyState + ) return tableStepQuizFormDelegate } override fun onSyncChosenColumnsWithParent(index: Int, chosenRows: List) { tableStepQuizFormDelegate.updateTableSelectionItem(index, chosenRows) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_text/ui/delegate/TextStepQuizFormDelegate.kt b/app/src/main/java/org/stepik/android/view/step_quiz_text/ui/delegate/TextStepQuizFormDelegate.kt index 9388d01a87..4725a650e9 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_text/ui/delegate/TextStepQuizFormDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_text/ui/delegate/TextStepQuizFormDelegate.kt @@ -9,9 +9,9 @@ import androidx.annotation.StringRes import androidx.appcompat.content.res.AppCompatResources import androidx.core.widget.TextViewCompat import androidx.core.widget.doAfterTextChanged -import kotlinx.android.synthetic.main.fragment_step_quiz.view.* -import kotlinx.android.synthetic.main.layout_step_quiz_text.view.* import org.stepic.droid.R +import org.stepic.droid.databinding.FragmentStepQuizBinding +import org.stepic.droid.databinding.LayoutStepQuizTextBinding import org.stepic.droid.util.AppConstants import org.stepik.android.model.Reply import org.stepik.android.model.Submission @@ -23,23 +23,44 @@ import org.stepik.android.view.step_quiz.ui.delegate.StepQuizFormDelegate import ru.nobird.android.view.base.ui.extension.setTextIfChanged class TextStepQuizFormDelegate( - containerView: View, + private val quizTextField: TextView, + private val quizDescription: TextView, private val stepBlockName: String?, private val onQuizChanged: (ReplyResult) -> Unit ) : StepQuizFormDelegate { companion object { - private const val MINUS = "-\\\u002D\u00AD\u2012\u2013\u2014\u2015\u02D7" + private const val MINUS = "-\\-­‒–—―˗" private const val PLUS = "+" private const val POINT = ",\\." private const val EXP = "eEеЕ" - private const val NUMBER_VALIDATION_REGEX = "^[$MINUS$PLUS]?[0-9]*[$POINT]?[0-9]+([$EXP][$$MINUS$PLUS]?[0-9]+)?$" + private const val NUMBER_VALIDATION_REGEX = "^[$MINUS$PLUS]?[0-9]*[$POINT]?[0-9]+([$EXP][$MINUS$PLUS]?[0-9]+)?$" } - private val context = containerView.context - - private val quizTextField = containerView.stringStepQuizField as TextView - private val quizDescription = containerView.stepQuizDescription + constructor( + stepQuizBinding: FragmentStepQuizBinding, + textStepQuizBinding: LayoutStepQuizTextBinding, + stepBlockName: String?, + onQuizChanged: (ReplyResult) -> Unit + ) : this( + textStepQuizBinding.root as TextView, + stepQuizBinding.stepQuizDescription, + stepBlockName, + onQuizChanged + ) + + constructor( + containerView: View, + stepBlockName: String?, + onQuizChanged: (ReplyResult) -> Unit + ) : this( + containerView.findViewById(R.id.stringStepQuizField), + containerView.findViewById(R.id.stepQuizDescription), + stepBlockName, + onQuizChanged + ) + + private val context = quizTextField.context init { val (inputType, @StringRes descriptionTextRes) = @@ -126,4 +147,4 @@ class TextStepQuizFormDelegate( TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(quizTextField, null, null, drawable, null) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_text/ui/fragment/TextStepQuizFragment.kt b/app/src/main/java/org/stepik/android/view/step_quiz_text/ui/fragment/TextStepQuizFragment.kt index 6ededf2de7..6a0fbf9ec6 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_text/ui/fragment/TextStepQuizFragment.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_text/ui/fragment/TextStepQuizFragment.kt @@ -1,9 +1,10 @@ package org.stepik.android.view.step_quiz_text.ui.fragment +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import androidx.fragment.app.Fragment -import kotlinx.android.synthetic.main.layout_step_quiz_text.* -import org.stepic.droid.R +import org.stepic.droid.databinding.LayoutStepQuizTextBinding import org.stepik.android.presentation.step_quiz.StepQuizFeature import org.stepik.android.view.step_quiz.ui.delegate.StepQuizFormDelegate import org.stepik.android.view.step_quiz.ui.fragment.DefaultStepQuizFragment @@ -21,12 +22,24 @@ class TextStepQuizFragment : } } - override val quizLayoutRes: Int = - R.layout.layout_step_quiz_text + private var _binding: LayoutStepQuizTextBinding? = null + private val binding: LayoutStepQuizTextBinding + get() = requireNotNull(_binding) override val quizViews: Array - get() = arrayOf(stringStepQuizField) + get() = arrayOf(binding.root) - override fun createStepQuizFormDelegate(view: View): StepQuizFormDelegate = - TextStepQuizFormDelegate(view, stepWrapper.step.block?.name, onQuizChanged = ::syncReplyState) -} \ No newline at end of file + override fun createStepView(layoutInflater: LayoutInflater, parent: ViewGroup): View { + return LayoutStepQuizTextBinding.inflate(layoutInflater, parent, false).also { + _binding = it + }.root + } + + override fun createStepQuizFormDelegate(): StepQuizFormDelegate = + TextStepQuizFormDelegate( + stepQuizBinding = stepQuizBinding, + textStepQuizBinding = binding, + stepBlockName = stepWrapper.step.block?.name, + onQuizChanged = ::syncReplyState + ) +} diff --git a/app/src/main/java/org/stepik/android/view/step_quiz_unsupported/ui/fragment/UnsupportedStepQuizFragment.kt b/app/src/main/java/org/stepik/android/view/step_quiz_unsupported/ui/fragment/UnsupportedStepQuizFragment.kt index d14de39e28..24323fe541 100644 --- a/app/src/main/java/org/stepik/android/view/step_quiz_unsupported/ui/fragment/UnsupportedStepQuizFragment.kt +++ b/app/src/main/java/org/stepik/android/view/step_quiz_unsupported/ui/fragment/UnsupportedStepQuizFragment.kt @@ -3,10 +3,11 @@ package org.stepik.android.view.step_quiz_unsupported.ui.fragment import android.os.Bundle import android.view.View import androidx.fragment.app.Fragment -import kotlinx.android.synthetic.main.fragment_step_quiz_unsupported.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager +import org.stepic.droid.databinding.FragmentStepQuizUnsupportedBinding import org.stepic.droid.persistence.model.StepPersistentWrapper import org.stepik.android.domain.lesson.model.LessonData import org.stepik.android.view.in_app_web_view.ui.dialog.InAppWebViewDialogFragment @@ -38,6 +39,8 @@ class UnsupportedStepQuizFragment : Fragment(R.layout.fragment_step_quiz_unsuppo private var stepId: Long by argument() + private val binding: FragmentStepQuizUnsupportedBinding by viewBinding(FragmentStepQuizUnsupportedBinding::bind) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) injectComponent() @@ -52,10 +55,10 @@ class UnsupportedStepQuizFragment : Fragment(R.layout.fragment_step_quiz_unsuppo override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - stepQuizAction.setOnClickListener { + binding.stepQuizAction.setOnClickListener { InAppWebViewDialogFragment .newInstance(lessonData.lesson.title.orEmpty(), stepDeepLinkBuilder.createStepLink(stepWrapper.step), isProvideAuth = true) .showIfNotExists(childFragmentManager, InAppWebViewDialogFragment.TAG) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/step_source/ui/dialog/EditStepSourceDialogFragment.kt b/app/src/main/java/org/stepik/android/view/step_source/ui/dialog/EditStepSourceDialogFragment.kt index 03192abe57..0924a8cf0c 100644 --- a/app/src/main/java/org/stepik/android/view/step_source/ui/dialog/EditStepSourceDialogFragment.kt +++ b/app/src/main/java/org/stepik/android/view/step_source/ui/dialog/EditStepSourceDialogFragment.kt @@ -9,13 +9,15 @@ import android.view.View import android.view.ViewGroup import android.view.Window import android.view.WindowManager +import android.widget.TextView +import androidx.appcompat.widget.Toolbar import androidx.fragment.app.DialogFragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider -import kotlinx.android.synthetic.main.dialog_step_source_edit.* -import kotlinx.android.synthetic.main.view_centered_toolbar.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.base.App +import org.stepic.droid.databinding.DialogStepSourceEditBinding import org.stepic.droid.persistence.model.StepPersistentWrapper import org.stepic.droid.ui.dialogs.DiscardTextDialogFragment import org.stepic.droid.ui.dialogs.LoadingProgressDialogFragment @@ -48,6 +50,8 @@ class EditStepSourceDialogFragment : @Inject internal lateinit var viewModelFactory: ViewModelProvider.Factory + private val binding: DialogStepSourceEditBinding by viewBinding(DialogStepSourceEditBinding::bind) + private val editStepContentPresenter: EditStepSourcePresenter by viewModels { viewModelFactory } private var stepWrapper: StepPersistentWrapper by argument() @@ -86,6 +90,9 @@ class EditStepSourceDialogFragment : inflater.inflate(R.layout.dialog_step_source_edit, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + val centeredToolbar = view.findCenteredToolbar() + val centeredToolbarTitle = view.findCenteredToolbarTitle() + centeredToolbarTitle.text = "$lessonTitle - ${stepWrapper.originalStep.position}" centeredToolbar.setNavigationOnClickListener { dismiss() } centeredToolbar.setTintedNavigationIcon(R.drawable.ic_close_dark) @@ -102,20 +109,20 @@ class EditStepSourceDialogFragment : if (savedInstanceState == null) { editStepContentPresenter.fetchStepContent(stepWrapper) } - invalidateMenuState() + invalidateMenuState(centeredToolbar) - stepContentEditText.addTextChangedListener(object : TextWatcher { + binding.stepContentEditText.addTextChangedListener(object : TextWatcher { override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun afterTextChanged(s: Editable?) { - invalidateMenuState() + invalidateMenuState(centeredToolbar) } }) } - private fun invalidateMenuState() { + private fun invalidateMenuState(centeredToolbar: Toolbar = requireView().findCenteredToolbar()) { centeredToolbar.menu.findItem(R.id.comment_submit)?.isEnabled = - stepContentEditText.text?.toString() != stepWrapper.originalStep.block?.text + binding.stepContentEditText.text?.toString() != stepWrapper.originalStep.block?.text } override fun onStart() { @@ -137,8 +144,8 @@ class EditStepSourceDialogFragment : } private fun submit() { - stepContentEditText.hideKeyboard() - editStepContentPresenter.changeStepBlockText(stepWrapper, stepContentEditText.text.toString()) + binding.stepContentEditText.hideKeyboard() + editStepContentPresenter.changeStepBlockText(stepWrapper, binding.stepContentEditText.text.toString()) } override fun setState(state: EditStepSourceView.State) { @@ -173,7 +180,7 @@ class EditStepSourceDialogFragment : } private fun onClose() { - if (stepContentEditText.text?.toString() == stepWrapper.originalStep.block?.text) { + if (binding.stepContentEditText.text?.toString() == stepWrapper.originalStep.block?.text) { super.dismiss() } else { DiscardTextDialogFragment @@ -193,10 +200,32 @@ class EditStepSourceDialogFragment : ?.onStepContentChanged(stepWrapper) this.stepWrapper = stepWrapper - stepContentEditText.setText(stepWrapper.originalStep.block?.text) + binding.stepContentEditText.setText(stepWrapper.originalStep.block?.text) + } + + private fun View.findCenteredToolbar(): Toolbar = + findViewById(R.id.centeredToolbar) + ?: findCenteredToolbarTitle().findParentToolbar() + ?: throw IllegalStateException("View with R.id.centeredToolbarTitle must be inside a Toolbar") + + private fun View.findCenteredToolbarTitle(): TextView = + findViewById(R.id.centeredToolbarTitle) + ?: throw IllegalStateException("View with R.id.centeredToolbarTitle was not found") + + // An overrides the included toolbar root id at runtime, + // so centeredToolbarTitle can be the only stable id. Walk up from it to recover the Toolbar. + private fun View.findParentToolbar(): Toolbar? { + var currentParent = parent + while (currentParent is View) { + if (currentParent is Toolbar) { + return currentParent + } + currentParent = (currentParent as View).parent + } + return null } interface Callback { fun onStepContentChanged(stepWrapper: StepPersistentWrapper) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/stories/ui/fragment/StoriesFragment.kt b/app/src/main/java/org/stepik/android/view/stories/ui/fragment/StoriesFragment.kt index fdf282a40b..a8be05549d 100644 --- a/app/src/main/java/org/stepik/android/view/stories/ui/fragment/StoriesFragment.kt +++ b/app/src/main/java/org/stepik/android/view/stories/ui/fragment/StoriesFragment.kt @@ -5,11 +5,12 @@ import android.view.View import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.view_stories_container.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App +import org.stepic.droid.databinding.ViewStoriesContainerBinding import org.stepic.droid.features.stories.ui.activity.StoriesActivity import org.stepic.droid.features.stories.ui.adapter.StoriesAdapter import org.stepik.android.presentation.stories.StoriesFeature @@ -43,6 +44,8 @@ class StoriesFragment : Fragment(R.layout.view_stories_container), ReduxView() viewStateDelegate.addState() - viewStateDelegate.addState(storiesContainerLoadingPlaceholder) - viewStateDelegate.addState(storiesRecycler) + viewStateDelegate.addState(binding.storiesContainerLoadingPlaceholder) + viewStateDelegate.addState(binding.storiesRecycler) storiesAdapter = StoriesAdapter { _, position -> requireContext().startActivity( @@ -75,7 +78,7 @@ class StoriesFragment : Fragment(R.layout.view_stories_container), ReduxView analytic.reportEvent(positiveEvent) @@ -60,4 +60,4 @@ class StreakNotificationDialogFragment : DialogFragment() { fun onStreakNotificationDialogAccepted() fun onStreakNotificationDialogCancelled() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/submission/ui/adapter/delegate/SubmissionDataAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/submission/ui/adapter/delegate/SubmissionDataAdapterDelegate.kt index 7a496629d9..7e167066c3 100644 --- a/app/src/main/java/org/stepik/android/view/submission/ui/adapter/delegate/SubmissionDataAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/submission/ui/adapter/delegate/SubmissionDataAdapterDelegate.kt @@ -11,9 +11,9 @@ import androidx.core.text.buildSpannedString import androidx.core.text.color import androidx.core.view.isVisible import androidx.core.widget.TextViewCompat -import kotlinx.android.synthetic.main.item_submission_data.view.* -import kotlinx.android.synthetic.main.view_submission_review.view.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemSubmissionDataBinding import org.stepik.android.view.glide.ui.extension.wrapWithGlide import org.stepic.droid.util.DateTimeHelper import org.stepic.droid.util.resolveFloatAttribute @@ -41,44 +41,32 @@ class SubmissionDataAdapterDelegate( data is SubmissionItem.Data private inner class ViewHolder(root: View) : DelegateViewHolder(root), View.OnClickListener { - private val submissionContainer = root.submissionContainer - private val submissionUserIcon = root.submissionUserIcon - private val submissionUserIconWrapper = submissionUserIcon.wrapWithGlide() - private val submissionUserName = root.submissionUserName - - private val submissionTime = root.submissionTime - private val submissionSolution = root.submissionSolution - private val submissionMoreIcon = root.submissionMoreIcon - private val submissionSelect = root.submissionSelect - private val submissionStatus = root.submissionStatus - private val submissionScoreValue = root.submissionScoreValue - private val submissionScoreText = root.submissionScoreText - private val reviewSelect = root.reviewSelect - private val reviewSelectText = root.reviewSelectText - private val reviewSelectArrow = root.reviewSelectArrow + private val viewBinding: ItemSubmissionDataBinding by viewBinding { ItemSubmissionDataBinding.bind(root) } + + private val submissionUserIconWrapper = viewBinding.submissionUserIcon.wrapWithGlide() init { - submissionContainer.setOnClickListener(this) - submissionUserIcon.setOnClickListener(this) - submissionUserName.setOnClickListener(this) - submissionMoreIcon.setOnClickListener(this) - reviewSelect.setOnClickListener(this) + viewBinding.submissionContainer.setOnClickListener(this) + viewBinding.submissionUserIcon.setOnClickListener(this) + viewBinding.submissionUserName.setOnClickListener(this) + viewBinding.submissionMoreIcon.setOnClickListener(this) + viewBinding.reviewSelect.root.setOnClickListener(this) if (isSelectionEnabled) { - submissionSelect.setOnClickListener(this) + viewBinding.submissionSelect.root.setOnClickListener(this) } - root.submissionDivider.isVisible = isSelectionEnabled || reviewInstructionData != null - reviewSelect.isVisible = reviewInstructionData != null - submissionSelect.isVisible = isSelectionEnabled - submissionMoreIcon.isVisible = isTeacher + viewBinding.submissionDivider.root.isVisible = isSelectionEnabled || reviewInstructionData != null + viewBinding.reviewSelect.root.isVisible = reviewInstructionData != null + viewBinding.submissionSelect.root.isVisible = isSelectionEnabled + viewBinding.submissionMoreIcon.isVisible = isTeacher } override fun onBind(data: SubmissionItem) { data as SubmissionItem.Data - submissionUserName.text = data.user.fullName + viewBinding.submissionUserName.text = data.user.fullName submissionUserIconWrapper.setImagePath(data.user.avatar ?: "", AppCompatResources.getDrawable(context, R.drawable.general_placeholder)) - submissionTime.text = DateMapper.mapToRelativeDate(context, DateTimeHelper.nowUtc(), data.submission.time?.time ?: 0) + viewBinding.submissionTime.text = DateMapper.mapToRelativeDate(context, DateTimeHelper.nowUtc(), data.submission.time?.time ?: 0) setupSubmission(data) setupReviewView(data, getSubmissionReviewState(data)) @@ -153,15 +141,15 @@ class SubmissionDataAdapterDelegate( val formattedScore = getFormattedScore(itemData) - submissionStatus.setTextColor(tintColor) - submissionStatus.text = statusText - submissionScoreValue.text = formattedScore - submissionSolution.text = context.getString(R.string.comment_solution_number, itemData.submission.id) - TextViewCompat.setCompoundDrawableTintList(submissionSolution, ColorStateList.valueOf(tintColor)) + viewBinding.submissionStatus.setTextColor(tintColor) + viewBinding.submissionStatus.text = statusText + viewBinding.submissionScoreValue.text = formattedScore + viewBinding.submissionSolution.text = context.getString(R.string.comment_solution_number, itemData.submission.id) + TextViewCompat.setCompoundDrawableTintList(viewBinding.submissionSolution, ColorStateList.valueOf(tintColor)) val needShowScore = formattedScore != null - submissionScoreValue.isVisible = needShowScore - submissionScoreText.isVisible = needShowScore + viewBinding.submissionScoreValue.isVisible = needShowScore + viewBinding.submissionScoreText.isVisible = needShowScore } private fun getSubmissionValue(submission: Submission): String { @@ -223,17 +211,17 @@ class SubmissionDataAdapterDelegate( context.getString(R.string.submission_review_action_see_reviews_title) } - reviewSelectText.text = buildSpannedString { + viewBinding.reviewSelect.reviewSelectText.text = buildSpannedString { append("$title\n") if (message.isNotEmpty()) append("$message\n") else append("") color(ContextCompat.getColor(context, R.color.color_overlay_violet)) { append(actionTitle) } } - reviewSelect.isEnabled = isEnabled + viewBinding.reviewSelect.root.isEnabled = isEnabled val alpha = if (isEnabled) 1f else context.resolveFloatAttribute(R.attr.alphaEmphasisDisabled) - reviewSelectText.alpha = alpha - reviewSelectArrow.alpha = alpha + viewBinding.reviewSelect.reviewSelectText.alpha = alpha + viewBinding.reviewSelect.reviewSelectArrow.alpha = alpha } private fun getSubmissionReviewState(itemData: SubmissionItem.Data): ReviewState? { @@ -312,4 +300,4 @@ class SubmissionDataAdapterDelegate( fun onSeeSubmissionReviewAction(submissionId: Long) fun onSeeReviewsReviewAction(session: Long) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/submission/ui/dialog/SubmissionsDialogFragment.kt b/app/src/main/java/org/stepik/android/view/submission/ui/dialog/SubmissionsDialogFragment.kt index 9231cedaee..b22c7b68ad 100644 --- a/app/src/main/java/org/stepik/android/view/submission/ui/dialog/SubmissionsDialogFragment.kt +++ b/app/src/main/java/org/stepik/android/view/submission/ui/dialog/SubmissionsDialogFragment.kt @@ -18,15 +18,12 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.dialog_submissions.* -import kotlinx.android.synthetic.main.empty_default.* -import kotlinx.android.synthetic.main.error_no_connection_with_button.* -import kotlinx.android.synthetic.main.view_submissions_search_toolbar.* -import kotlinx.android.synthetic.main.view_subtitled_toolbar.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager import org.stepic.droid.preferences.UserPreferences +import org.stepic.droid.databinding.DialogSubmissionsBinding import org.stepic.droid.ui.util.setTintedNavigationIcon import org.stepic.droid.ui.util.snackbar import org.stepik.android.domain.filter.model.SubmissionsFilterQuery @@ -116,6 +113,9 @@ class SubmissionsDialogFragment : private val placeholders = List(10) { SubmissionItem.Placeholder } + private val submissionsBinding: DialogSubmissionsBinding by viewBinding(DialogSubmissionsBinding::bind) + private val searchToolbar get() = submissionsBinding.submissionsSearchToolbar + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val dialog = super.onCreateDialog(savedInstanceState) dialog.setCanceledOnTouchOutside(false) @@ -147,25 +147,27 @@ class SubmissionsDialogFragment : inflater.inflate(R.layout.dialog_submissions, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + val centeredToolbar = searchToolbar.viewCenteredToolbar.centeredToolbar + centeredToolbar.isVisible = !isTeacher - searchViewContainer.isVisible = isTeacher + searchToolbar.searchViewContainer.isVisible = isTeacher - centeredToolbarTitle.setText(if (isSelectionEnabled) R.string.submissions_select_title else R.string.submissions_title) + searchToolbar.viewCenteredToolbar.centeredToolbarTitle.setText(if (isSelectionEnabled) R.string.submissions_select_title else R.string.submissions_title) centeredToolbar.setNavigationOnClickListener { dismiss() } centeredToolbar.setTintedNavigationIcon(R.drawable.ic_close_dark) AppCompatResources .getDrawable(requireContext(), R.drawable.ic_close_dark) ?.setTintList(requireContext(), R.attr.colorControlNormal) - ?.let { backIcon.setImageDrawable(it) } - backIcon.setOnClickListener { dismiss() } - clearSearchButton.setOnClickListener { - searchSubmissionsEditText.text?.clear() + ?.let { searchToolbar.backIcon.setImageDrawable(it) } + searchToolbar.backIcon.setOnClickListener { dismiss() } + searchToolbar.clearSearchButton.setOnClickListener { + searchToolbar.searchSubmissionsEditText.text?.clear() fetchSearchQuery() } - filterIcon.setOnClickListener { submissionsPresenter.onFilterMenuItemClicked() } + searchToolbar.filterIcon.setOnClickListener { submissionsPresenter.onFilterMenuItemClicked() } - searchSubmissionsEditText.background = AppCompatResources + searchToolbar.searchSubmissionsEditText.background = AppCompatResources .getDrawable(requireContext(), R.drawable.bg_shape_rounded) ?.mutate() ?.let { DrawableCompat.wrap(it) } @@ -176,11 +178,11 @@ class SubmissionsDialogFragment : viewContentStateDelegate = ViewStateDelegate() viewContentStateDelegate.addState() - viewContentStateDelegate.addState(swipeRefresh) - viewContentStateDelegate.addState(error) - viewContentStateDelegate.addState(swipeRefresh) - viewContentStateDelegate.addState(swipeRefresh) - viewContentStateDelegate.addState(report_empty) + viewContentStateDelegate.addState(submissionsBinding.swipeRefresh) + viewContentStateDelegate.addState(submissionsBinding.submissionsLoadingError.root) + viewContentStateDelegate.addState(submissionsBinding.swipeRefresh) + viewContentStateDelegate.addState(submissionsBinding.swipeRefresh) + viewContentStateDelegate.addState(submissionsBinding.submissionsEmpty.root) submissionItemAdapter = DefaultDelegateAdapter() submissionItemAdapter += SubmissionDataAdapterDelegate( @@ -207,7 +209,7 @@ class SubmissionsDialogFragment : override fun onViewSubmissionsClicked(submissionDataItem: SubmissionItem.Data) { val userIdQuery = resources.getString(R.string.submissions_user_filter, submissionDataItem.user.id) - searchSubmissionsEditText.setText(userIdQuery) + searchToolbar.searchSubmissionsEditText.setText(userIdQuery) fetchSearchQuery() } @@ -227,7 +229,7 @@ class SubmissionsDialogFragment : submissionItemAdapter += adapterDelegate(R.layout.item_submission_placeholder) - with(recycler) { + with(submissionsBinding.recycler) { adapter = submissionItemAdapter layoutManager = LinearLayoutManager(context) @@ -242,10 +244,10 @@ class SubmissionsDialogFragment : }) } - swipeRefresh.setOnRefreshListener { submissionsPresenter.fetchSubmissions(step.id, isTeacher, submissionsFilterQuery, reviewInstructionData?.reviewInstruction, forceUpdate = true) } - tryAgain.setOnClickListener { submissionsPresenter.fetchSubmissions(step.id, isTeacher, submissionsFilterQuery, reviewInstructionData?.reviewInstruction, forceUpdate = true) } + submissionsBinding.swipeRefresh.setOnRefreshListener { submissionsPresenter.fetchSubmissions(step.id, isTeacher, submissionsFilterQuery, reviewInstructionData?.reviewInstruction, forceUpdate = true) } + submissionsBinding.submissionsLoadingError.tryAgain.setOnClickListener { submissionsPresenter.fetchSubmissions(step.id, isTeacher, submissionsFilterQuery, reviewInstructionData?.reviewInstruction, forceUpdate = true) } - searchSubmissionsEditText.setOnEditorActionListener { _, actionId, _ -> + searchToolbar.searchSubmissionsEditText.setOnEditorActionListener { _, actionId, _ -> if (actionId == EditorInfo.IME_ACTION_SEARCH) { fetchSearchQuery() return@setOnEditorActionListener true @@ -253,17 +255,17 @@ class SubmissionsDialogFragment : return@setOnEditorActionListener false } - searchSubmissionsEditText.addTextChangedListener { + searchToolbar.searchSubmissionsEditText.addTextChangedListener { if (it.isNullOrEmpty()) { - clearSearchButton.isVisible = false - searchSubmissionsEditText.setPadding(resources.getDimensionPixelSize(R.dimen.submissions_search_padding_left), 0, resources.getDimensionPixelSize(R.dimen.submissions_search_padding_without_text), 0) + searchToolbar.clearSearchButton.isVisible = false + searchToolbar.searchSubmissionsEditText.setPadding(resources.getDimensionPixelSize(R.dimen.submissions_search_padding_left), 0, resources.getDimensionPixelSize(R.dimen.submissions_search_padding_without_text), 0) } else { - clearSearchButton.isVisible = true - searchSubmissionsEditText.setPadding(resources.getDimensionPixelSize(R.dimen.submissions_search_padding_left), 0, resources.getDimensionPixelSize(R.dimen.submissions_search_padding_with_text), 0) + searchToolbar.clearSearchButton.isVisible = true + searchToolbar.searchSubmissionsEditText.setPadding(resources.getDimensionPixelSize(R.dimen.submissions_search_padding_left), 0, resources.getDimensionPixelSize(R.dimen.submissions_search_padding_with_text), 0) } } val userIdQuery = if (userId == -1L) null else resources.getString(R.string.submissions_user_filter, userId) - userIdQuery?.let { searchSubmissionsEditText.setText(it) } + userIdQuery?.let { searchToolbar.searchSubmissionsEditText.setText(it) } } private fun injectComponent() { @@ -291,10 +293,10 @@ class SubmissionsDialogFragment : } override fun setState(state: SubmissionsView.State) { - swipeRefresh.isRefreshing = false + submissionsBinding.swipeRefresh.isRefreshing = false if (state is SubmissionsView.State.Data) { viewContentStateDelegate.switchState(state.contentState) - filterIcon.setImageResource(getFilterIcon(state.submissionsFilterQuery)) + searchToolbar.filterIcon.setImageResource(getFilterIcon(state.submissionsFilterQuery)) submissionsFilterQuery = state.submissionsFilterQuery submissionItemAdapter.items = when (state.contentState) { @@ -345,12 +347,12 @@ class SubmissionsDialogFragment : } private fun fetchSearchQuery() { - searchSubmissionsEditText.hideKeyboard() - searchSubmissionsEditText.clearFocus() + searchToolbar.searchSubmissionsEditText.hideKeyboard() + searchToolbar.searchSubmissionsEditText.clearFocus() submissionsPresenter.fetchSubmissions( step.id, isTeacher, - submissionsFilterQuery.copy(search = searchSubmissionsEditText.text?.toString()), + submissionsFilterQuery.copy(search = searchToolbar.searchSubmissionsEditText.text?.toString()), reviewInstructionData?.reviewInstruction, forceUpdate = true ) diff --git a/app/src/main/java/org/stepik/android/view/submission/ui/dialog/SubmissionsQueryFilterDialogFragment.kt b/app/src/main/java/org/stepik/android/view/submission/ui/dialog/SubmissionsQueryFilterDialogFragment.kt index 325dccb0e5..5384bea98b 100644 --- a/app/src/main/java/org/stepik/android/view/submission/ui/dialog/SubmissionsQueryFilterDialogFragment.kt +++ b/app/src/main/java/org/stepik/android/view/submission/ui/dialog/SubmissionsQueryFilterDialogFragment.kt @@ -11,8 +11,9 @@ import androidx.fragment.app.DialogFragment import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import kotlinx.android.synthetic.main.bottom_sheet_dialog_submissions_filter.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.BottomSheetDialogSubmissionsFilterBinding import org.stepik.android.domain.filter.model.SubmissionsFilterQuery import org.stepik.android.model.Submission import ru.nobird.android.view.base.ui.extension.argument @@ -40,6 +41,8 @@ class SubmissionsQueryFilterDialogFragment : BottomSheetDialogFragment() { private lateinit var reviewStatusRadioButtons: List private lateinit var allRadioButtons: List + private val binding: BottomSheetDialogSubmissionsFilterBinding by viewBinding(BottomSheetDialogSubmissionsFilterBinding::bind) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setStyle(DialogFragment.STYLE_NO_TITLE, R.style.ThemeOverlay_AppTheme_BottomSheetDialog) @@ -56,29 +59,29 @@ class SubmissionsQueryFilterDialogFragment : BottomSheetDialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - defaultStatusButton = anyStatusButton - defaultDateSortButton = descendingDateSortButton - defaultReviewStatusButton = anyReviewStatusButton + defaultStatusButton = binding.anyStatusButton + defaultDateSortButton = binding.descendingDateSortButton + defaultReviewStatusButton = binding.anyReviewStatusButton - submissionStatusRadioButtons = listOf(anyStatusButton, correctStatusButton, incorrectStatusButton) - dateSortRadioButtons = listOf(descendingDateSortButton, ascendingDateSortButton) - reviewStatusRadioButtons = listOf(anyReviewStatusButton, finishedReviewStatusButton, awaitingReviewStatusButton) + submissionStatusRadioButtons = listOf(binding.anyStatusButton, binding.correctStatusButton, binding.incorrectStatusButton) + dateSortRadioButtons = listOf(binding.descendingDateSortButton, binding.ascendingDateSortButton) + reviewStatusRadioButtons = listOf(binding.anyReviewStatusButton, binding.finishedReviewStatusButton, binding.awaitingReviewStatusButton) allRadioButtons = submissionStatusRadioButtons + dateSortRadioButtons + reviewStatusRadioButtons - reviewStatusTitle.isVisible = isPeerReview - anyReviewStatusButtonDivider.isVisible = isPeerReview - reviewStatusClosingDivider.isVisible = isPeerReview + binding.reviewStatusTitle.isVisible = isPeerReview + binding.anyReviewStatusButtonDivider.root.isVisible = isPeerReview + binding.reviewStatusClosingDivider.root.isVisible = isPeerReview reviewStatusRadioButtons.forEach { it.isVisible = isPeerReview } setupFilters(submissionsFilterQuery) - dismissSubmissionsFilter.isVisible = isMustShowDismiss() + binding.dismissSubmissionsFilter.isVisible = isMustShowDismiss() setupListeners(submissionStatusRadioButtons) setupListeners(dateSortRadioButtons) setupListeners(reviewStatusRadioButtons) - dismissSubmissionsFilter.setOnClickListener { + binding.dismissSubmissionsFilter.setOnClickListener { allRadioButtons.forEach { radioButton -> radioButton.isChecked = false } defaultStatusButton.isChecked = true defaultDateSortButton.isChecked = true @@ -86,7 +89,7 @@ class SubmissionsQueryFilterDialogFragment : BottomSheetDialogFragment() { it.isVisible = false } - applyFilterAction.setOnClickListener { + binding.applyFilterAction.setOnClickListener { val newFilter = mapFiltersToQuery() if (newFilter != submissionsFilterQuery) { (parentFragment as? Callback) @@ -101,7 +104,7 @@ class SubmissionsQueryFilterDialogFragment : BottomSheetDialogFragment() { it.setOnCheckedChangeListener { buttonView, isChecked -> if (isChecked) { onRadioButtonClicked(buttonView, radioButtons) - dismissSubmissionsFilter.isVisible = isMustShowDismiss() + binding.dismissSubmissionsFilter.isVisible = isMustShowDismiss() } } } @@ -110,34 +113,34 @@ class SubmissionsQueryFilterDialogFragment : BottomSheetDialogFragment() { private fun setupFilters(submissionsFilterQuery: SubmissionsFilterQuery) { val submissionStatusRadioButton = when (submissionsFilterQuery.status) { Submission.Status.CORRECT.scope -> - correctStatusButton + binding.correctStatusButton Submission.Status.WRONG.scope -> - incorrectStatusButton + binding.incorrectStatusButton else -> - anyStatusButton + binding.anyStatusButton } submissionStatusRadioButton.isChecked = true val dateSortRadioButton = if (submissionsFilterQuery.order == SubmissionsFilterQuery.Order.ASC) { - ascendingDateSortButton + binding.ascendingDateSortButton } else { - descendingDateSortButton + binding.descendingDateSortButton } dateSortRadioButton.isChecked = true val reviewStatusRadioButton = when (submissionsFilterQuery.reviewStatus) { SubmissionsFilterQuery.ReviewStatus.AWAITING -> - awaitingReviewStatusButton + binding.awaitingReviewStatusButton SubmissionsFilterQuery.ReviewStatus.DONE -> - finishedReviewStatusButton + binding.finishedReviewStatusButton else -> - anyReviewStatusButton + binding.anyReviewStatusButton } reviewStatusRadioButton.isChecked = true @@ -145,27 +148,27 @@ class SubmissionsQueryFilterDialogFragment : BottomSheetDialogFragment() { private fun mapFiltersToQuery(): SubmissionsFilterQuery { val status = when { - correctStatusButton.isChecked -> + binding.correctStatusButton.isChecked -> Submission.Status.CORRECT?.scope - incorrectStatusButton.isChecked -> + binding.incorrectStatusButton.isChecked -> Submission.Status.WRONG?.scope else -> null } - val dateOrder = if (ascendingDateSortButton.isChecked) { + val dateOrder = if (binding.ascendingDateSortButton.isChecked) { SubmissionsFilterQuery.Order.ASC } else { SubmissionsFilterQuery.Order.DESC } val reviewStatus = when { - awaitingReviewStatusButton.isChecked -> + binding.awaitingReviewStatusButton.isChecked -> SubmissionsFilterQuery.ReviewStatus.AWAITING - finishedReviewStatusButton.isChecked -> + binding.finishedReviewStatusButton.isChecked -> SubmissionsFilterQuery.ReviewStatus.DONE else -> diff --git a/app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsPlaceholderAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsPlaceholderAdapterDelegate.kt index 24fee33dad..f7f3319e02 100644 --- a/app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsPlaceholderAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsPlaceholderAdapterDelegate.kt @@ -2,7 +2,6 @@ package org.stepik.android.view.user_reviews.ui.adapter.delegate import android.view.View import android.view.ViewGroup -import kotlinx.android.extensions.LayoutContainer import org.stepic.droid.R import org.stepik.android.domain.user_reviews.model.UserCourseReviewItem import ru.nobird.android.ui.adapterdelegates.AdapterDelegate @@ -15,5 +14,5 @@ class UserReviewsPlaceholderAdapterDelegate : AdapterDelegate = ViewHolder(createView(parent, R.layout.view_course_content_unit_placeholder)) - class ViewHolder(override val containerView: View) : DelegateViewHolder(containerView), LayoutContainer + class ViewHolder(containerView: View) : DelegateViewHolder(containerView) } \ No newline at end of file diff --git a/app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsPotentialAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsPotentialAdapterDelegate.kt index 7e00e9aa08..6e1ea648f1 100644 --- a/app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsPotentialAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsPotentialAdapterDelegate.kt @@ -2,10 +2,10 @@ package org.stepik.android.view.user_reviews.ui.adapter.delegate import android.view.View import android.view.ViewGroup +import by.kirich1409.viewbindingdelegate.viewBinding import com.bumptech.glide.Glide -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_user_review_potential.* import org.stepic.droid.R +import org.stepic.droid.databinding.ItemUserReviewPotentialBinding import org.stepik.android.domain.user_reviews.model.UserCourseReviewItem import org.stepik.android.model.Course import ru.nobird.android.ui.adapterdelegates.AdapterDelegate @@ -25,12 +25,13 @@ class UserReviewsPotentialAdapterDelegate( override fun onCreateViewHolder(parent: ViewGroup): DelegateViewHolder = ViewHolder(createView(parent, R.layout.item_user_review_potential)) - private inner class ViewHolder(override val containerView: View) : DelegateViewHolder(containerView), LayoutContainer { + private inner class ViewHolder(root: View) : DelegateViewHolder(root) { + private val viewBinding: ItemUserReviewPotentialBinding by viewBinding { ItemUserReviewPotentialBinding.bind(root) } init { - userReviewIcon.setOnClickListener { (itemData as? UserCourseReviewItem.PotentialReviewItem)?.course?.let(onCourseTitleClicked) } - userReviewCourseTitle.setOnClickListener { (itemData as? UserCourseReviewItem.PotentialReviewItem)?.course?.let(onCourseTitleClicked) } - userReviewRating.setOnRatingBarChangeListener { ratingBar, rating, fromUser -> + viewBinding.userReviewIcon.setOnClickListener { (itemData as? UserCourseReviewItem.PotentialReviewItem)?.course?.let(onCourseTitleClicked) } + viewBinding.userReviewCourseTitle.setOnClickListener { (itemData as? UserCourseReviewItem.PotentialReviewItem)?.course?.let(onCourseTitleClicked) } + viewBinding.userReviewRating.setOnRatingBarChangeListener { ratingBar, rating, fromUser -> val potentialReview = (itemData as? UserCourseReviewItem.PotentialReviewItem) ?: return@setOnRatingBarChangeListener if (fromUser) { onWriteReviewClicked(potentialReview.course.id, potentialReview.course.title.toString(), rating) @@ -39,7 +40,7 @@ class UserReviewsPotentialAdapterDelegate( ratingBar.postDelayed({ ratingBar.rating = 0f }, RATING_RESET_DELAY_MS) } } - userReviewWriteAction.setOnClickListener { + viewBinding.userReviewWriteAction.setOnClickListener { val potentialReview = (itemData as? UserCourseReviewItem.PotentialReviewItem) ?: return@setOnClickListener onWriteReviewClicked(potentialReview.course.id, potentialReview.course.title.toString(), -1f) } @@ -47,7 +48,7 @@ class UserReviewsPotentialAdapterDelegate( override fun onBind(data: UserCourseReviewItem) { data as UserCourseReviewItem.PotentialReviewItem - userReviewCourseTitle.text = data.course.title + viewBinding.userReviewCourseTitle.text = data.course.title Glide .with(context) @@ -55,9 +56,9 @@ class UserReviewsPotentialAdapterDelegate( .load(data.course.cover) .placeholder(R.drawable.general_placeholder) .fitCenter() - .into(userReviewIcon) + .into(viewBinding.userReviewIcon) - userReviewRating.max = 5 + viewBinding.userReviewRating.max = 5 } } } \ No newline at end of file diff --git a/app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsPotentialHeaderAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsPotentialHeaderAdapterDelegate.kt index 01f03c340c..8581aaf996 100644 --- a/app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsPotentialHeaderAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsPotentialHeaderAdapterDelegate.kt @@ -2,9 +2,9 @@ package org.stepik.android.view.user_reviews.ui.adapter.delegate import android.view.View import android.view.ViewGroup -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_user_review_potential_header.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemUserReviewPotentialHeaderBinding import org.stepik.android.domain.user_reviews.model.UserCourseReviewItem import ru.nobird.android.ui.adapterdelegates.AdapterDelegate import ru.nobird.android.ui.adapterdelegates.DelegateViewHolder @@ -16,10 +16,12 @@ class UserReviewsPotentialHeaderAdapterDelegate : AdapterDelegate = ViewHolder(createView(parent, R.layout.item_user_review_potential_header)) - private class ViewHolder(override val containerView: View) : DelegateViewHolder(containerView), LayoutContainer { + private class ViewHolder(root: View) : DelegateViewHolder(root) { + private val viewBinding: ItemUserReviewPotentialHeaderBinding by viewBinding { ItemUserReviewPotentialHeaderBinding.bind(root) } + override fun onBind(data: UserCourseReviewItem) { data as UserCourseReviewItem.PotentialReviewHeader - potentialReviewText.text = context.getString( + viewBinding.potentialReviewText.text = context.getString( R.string.user_review_potential_review_header, context.resources.getQuantityString( R.plurals.potential_review, diff --git a/app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsReviewedAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsReviewedAdapterDelegate.kt index a0574b19a1..8f900e27db 100644 --- a/app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsReviewedAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsReviewedAdapterDelegate.kt @@ -6,13 +6,10 @@ import android.text.style.ForegroundColorSpan import android.view.View import android.view.ViewGroup import androidx.appcompat.widget.PopupMenu +import by.kirich1409.viewbindingdelegate.viewBinding import com.bumptech.glide.Glide -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_user_review_reviewed.* -import kotlinx.android.synthetic.main.item_user_review_reviewed.userReviewCourseTitle -import kotlinx.android.synthetic.main.item_user_review_reviewed.userReviewIcon -import kotlinx.android.synthetic.main.item_user_review_reviewed.userReviewRating import org.stepic.droid.R +import org.stepic.droid.databinding.ItemUserReviewReviewedBinding import org.stepic.droid.util.DateTimeHelper import org.stepic.droid.util.resolveColorAttribute import org.stepik.android.domain.course_reviews.model.CourseReview @@ -33,17 +30,19 @@ class UserReviewsReviewedAdapterDelegate( override fun onCreateViewHolder(parent: ViewGroup): DelegateViewHolder = ViewHolder(createView(parent, R.layout.item_user_review_reviewed)) - private inner class ViewHolder(override val containerView: View) : DelegateViewHolder(containerView), LayoutContainer { + private inner class ViewHolder(root: View) : DelegateViewHolder(root) { + private val viewBinding: ItemUserReviewReviewedBinding by viewBinding { ItemUserReviewReviewedBinding.bind(root) } + init { - userReviewIcon.setOnClickListener { (itemData as? UserCourseReviewItem.ReviewedItem)?.course?.let(onCourseTitleClicked) } - userReviewCourseTitle.setOnClickListener { (itemData as? UserCourseReviewItem.ReviewedItem)?.course?.let(onCourseTitleClicked) } - userReviewMenu.setOnClickListener(::showReviewMenu) + viewBinding.userReviewIcon.setOnClickListener { (itemData as? UserCourseReviewItem.ReviewedItem)?.course?.let(onCourseTitleClicked) } + viewBinding.userReviewCourseTitle.setOnClickListener { (itemData as? UserCourseReviewItem.ReviewedItem)?.course?.let(onCourseTitleClicked) } + viewBinding.userReviewMenu.setOnClickListener(::showReviewMenu) } override fun onBind(data: UserCourseReviewItem) { data as UserCourseReviewItem.ReviewedItem - userReviewCourseTitle.text = data.course.title - userReviewText.text = data.courseReview.text + viewBinding.userReviewCourseTitle.text = data.course.title + viewBinding.userReviewText.text = data.courseReview.text Glide .with(context) @@ -51,11 +50,11 @@ class UserReviewsReviewedAdapterDelegate( .load(data.course.cover) .placeholder(R.drawable.general_placeholder) .fitCenter() - .into(userReviewIcon) + .into(viewBinding.userReviewIcon) - userReviewTime.text = DateMapper.mapToRelativeDate(context, DateTimeHelper.nowUtc(), data.courseReview.updateDate?.time ?: 0) - userReviewRating.progress = data.courseReview.score - userReviewRating.total = 5 + viewBinding.userReviewTime.text = DateMapper.mapToRelativeDate(context, DateTimeHelper.nowUtc(), data.courseReview.updateDate?.time ?: 0) + viewBinding.userReviewRating.progress = data.courseReview.score + viewBinding.userReviewRating.total = 5 } private fun showReviewMenu(view: View) { diff --git a/app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsReviewedHeaderAdapterDelegate.kt b/app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsReviewedHeaderAdapterDelegate.kt index 721c93613b..4934ec71a3 100644 --- a/app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsReviewedHeaderAdapterDelegate.kt +++ b/app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsReviewedHeaderAdapterDelegate.kt @@ -2,9 +2,9 @@ package org.stepik.android.view.user_reviews.ui.adapter.delegate import android.view.View import android.view.ViewGroup -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_user_review_reviewed_header.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemUserReviewReviewedHeaderBinding import org.stepik.android.domain.user_reviews.model.UserCourseReviewItem import ru.nobird.android.ui.adapterdelegates.AdapterDelegate import ru.nobird.android.ui.adapterdelegates.DelegateViewHolder @@ -16,10 +16,12 @@ class UserReviewsReviewedHeaderAdapterDelegate : AdapterDelegate = ViewHolder(createView(parent, R.layout.item_user_review_reviewed_header)) - private class ViewHolder(override val containerView: View) : DelegateViewHolder(containerView), LayoutContainer { + private class ViewHolder(root: View) : DelegateViewHolder(root) { + private val viewBinding: ItemUserReviewReviewedHeaderBinding by viewBinding { ItemUserReviewReviewedHeaderBinding.bind(root) } + override fun onBind(data: UserCourseReviewItem) { data as UserCourseReviewItem.ReviewedHeader - reviewedText.text = context.resources.getQuantityString(R.plurals.learning_action_review, data.reviewedCount, data.reviewedCount) + viewBinding.reviewedText.text = context.resources.getQuantityString(R.plurals.learning_action_review, data.reviewedCount, data.reviewedCount) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/stepik/android/view/user_reviews/ui/fragment/UserReviewsFragment.kt b/app/src/main/java/org/stepik/android/view/user_reviews/ui/fragment/UserReviewsFragment.kt index 841cbce7d2..791b9247e8 100644 --- a/app/src/main/java/org/stepik/android/view/user_reviews/ui/fragment/UserReviewsFragment.kt +++ b/app/src/main/java/org/stepik/android/view/user_reviews/ui/fragment/UserReviewsFragment.kt @@ -8,9 +8,9 @@ import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.error_no_connection_with_button_small.* -import kotlinx.android.synthetic.main.fragment_user_reviews.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.FragmentUserReviewsBinding import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App import org.stepic.droid.core.ScreenManager @@ -40,6 +40,8 @@ import javax.inject.Inject class UserReviewsFragment : Fragment(R.layout.fragment_user_reviews), ReduxView { + private val binding: FragmentUserReviewsBinding by viewBinding(FragmentUserReviewsBinding::bind) + companion object { fun newInstance(): Fragment = UserReviewsFragment() @@ -91,7 +93,7 @@ class UserReviewsFragment : Fragment(R.layout.fragment_user_reviews), ReduxView< }, onRemoveReviewClicked = { courseReview -> userReviewsViewModel.onNewMessage(UserReviewsFeature.Message.DeletedReviewUserReviews(courseReview)) } ) - with(userReviewsRecycler) { + with(binding.userReviewsRecycler) { adapter = userReviewItemAdapter layoutManager = LinearLayoutManager(context) setHasFixedSize(true) @@ -102,7 +104,7 @@ class UserReviewsFragment : Fragment(R.layout.fragment_user_reviews), ReduxView< } userReviewsViewModel.onNewMessage(UserReviewsFeature.Message.InitMessage(forceUpdate = false)) userReviewsViewModel.onNewMessage(UserReviewsFeature.Message.ScreenOpenedMessage) - tryAgain.setOnClickListener { + binding.userReviewsError.tryAgain.setOnClickListener { userReviewsViewModel.onNewMessage(UserReviewsFeature.Message.InitMessage(forceUpdate = true)) } } @@ -115,10 +117,10 @@ class UserReviewsFragment : Fragment(R.layout.fragment_user_reviews), ReduxView< private fun initViewStateDelegate() { viewStateDelegate.addState() - viewStateDelegate.addState(userReviewsRecycler) - viewStateDelegate.addState(userReviewsError) - viewStateDelegate.addState(userReviewsEmpty) - viewStateDelegate.addState(userReviewsRecycler) + viewStateDelegate.addState(binding.userReviewsRecycler) + viewStateDelegate.addState(binding.userReviewsError.root) + viewStateDelegate.addState(binding.userReviewsEmpty.root) + viewStateDelegate.addState(binding.userReviewsRecycler) } override fun onAction(action: UserReviewsFeature.Action.ViewAction) { diff --git a/app/src/main/java/org/stepik/android/view/video_player/ui/activity/VideoPlayerActivity.kt b/app/src/main/java/org/stepik/android/view/video_player/ui/activity/VideoPlayerActivity.kt index 7437fdfa98..f896e3f7b1 100644 --- a/app/src/main/java/org/stepik/android/view/video_player/ui/activity/VideoPlayerActivity.kt +++ b/app/src/main/java/org/stepik/android/view/video_player/ui/activity/VideoPlayerActivity.kt @@ -34,16 +34,17 @@ import androidx.core.content.ContextCompat import androidx.core.view.GestureDetectorCompat import androidx.core.view.isVisible import androidx.lifecycle.ViewModelProvider +import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.exoplayer2.PlaybackException import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.upstream.HttpDataSource import com.google.android.exoplayer2.util.Util -import kotlinx.android.synthetic.main.activity_video_player.* -import kotlinx.android.synthetic.main.exo_player_control_view.* import org.stepic.droid.R import org.stepic.droid.analytic.AmplitudeAnalytic import org.stepic.droid.analytic.Analytic import org.stepic.droid.base.App +import org.stepic.droid.databinding.ActivityVideoPlayerBinding +import org.stepic.droid.databinding.ExoPlayerControlViewBinding import org.stepic.droid.preferences.VideoPlaybackRate import org.stepic.droid.ui.custom_exo.NavigationBarUtil import org.stepic.droid.ui.dialogs.VideoQualityDialogInPlayer @@ -112,6 +113,13 @@ class VideoPlayerActivity : AppCompatActivity(), VideoPlayerView, VideoQualityDi @Inject internal lateinit var viewModelFactory: ViewModelProvider.Factory + private val binding: ActivityVideoPlayerBinding by viewBinding(ActivityVideoPlayerBinding::bind) + + private val controlsBinding: ExoPlayerControlViewBinding by viewBinding( + vbFactory = ExoPlayerControlViewBinding::bind, + viewBindingRootId = R.id.exo_player_controls_root + ) + private var isPlaying: Boolean = false set(value) { // When seeking in video, isPlaying get set to `true` twice @@ -235,7 +243,7 @@ class VideoPlayerActivity : AppCompatActivity(), VideoPlayerView, VideoQualityDi videoPlayerPresenter.onPlayerStateChanged(value.playbackState, isAutoplayEnabled) } - playerView?.player = value + binding.playerView.player = value } private var isLandscapeVideo = false private var isPIPModeActive = false @@ -267,14 +275,14 @@ class VideoPlayerActivity : AppCompatActivity(), VideoPlayerView, VideoQualityDi setTitle(R.string.video_title) setContentView(R.layout.activity_video_player) - closeButton.setOnClickListener { + controlsBinding.closeButton.setOnClickListener { finish() } - videoRateChooser.setOnClickListener { + controlsBinding.videoRateChooser.setOnClickListener { showChooseRateMenu(it) } - playerView.setOnTouchListener { v, event -> + binding.playerView.setOnTouchListener { v, event -> if (event.action == MotionEvent.ACTION_UP) { v.performClick() } @@ -282,19 +290,19 @@ class VideoPlayerActivity : AppCompatActivity(), VideoPlayerView, VideoQualityDi true } - qualityView.isVisible = false - playerView.controllerShowTimeoutMs = TIMEOUT_BEFORE_HIDE + controlsBinding.qualityView.isVisible = false + binding.playerView.controllerShowTimeoutMs = TIMEOUT_BEFORE_HIDE - exo_pip_icon_container.isVisible = isSupportPIP() - exo_pip_icon_container.setOnClickListener { + controlsBinding.exoPipIconContainer.isVisible = isSupportPIP() + controlsBinding.exoPipIconContainer.setOnClickListener { analytic.report(PIPActivated()) enterPipMode() } - exo_fullscreen_icon_container.setOnClickListener { changeVideoRotation() } + controlsBinding.exoFullscreenIconContainer.setOnClickListener { changeVideoRotation() } - playerView.setControllerVisibilityListener { visibility -> + binding.playerView.setControllerVisibilityListener { visibility -> if (isSupportPIP() && isInPictureInPictureMode) { - playerView.hideController() + binding.playerView.hideController() return@setControllerVisibilityListener } if (visibility == View.VISIBLE) { @@ -304,39 +312,47 @@ class VideoPlayerActivity : AppCompatActivity(), VideoPlayerView, VideoQualityDi } } - autoplay_controller_panel.setOnClickListener { + controlsBinding.autoplayControllerPanel.setOnClickListener { move(StepNavigationDirection.NEXT) } - autoplayProgress.max = AUTOPLAY_PROGRESS_MAX - autoplayCancel.setOnClickListener { videoPlayerPresenter.stayOnThisStep() } - autoplaySwitch.setOnCheckedChangeListener { _, isChecked -> - if (autoplaySwitch.isUserTriggered) { + controlsBinding.autoplayProgress.max = AUTOPLAY_PROGRESS_MAX + controlsBinding.autoplayCancel.setOnClickListener { videoPlayerPresenter.stayOnThisStep() } + controlsBinding.autoplaySwitch.setOnCheckedChangeListener { _, isChecked -> + if (controlsBinding.autoplaySwitch.isUserTriggered) { videoPlayerPresenter.setAutoplayEnabled(isChecked) } } - rewind.setOnClickListener { + controlsBinding.rewind.setOnClickListener { exoPlayer?.let { player -> player.seekTo(player.currentPosition - JUMP_TIME_MILLIS) } } - forward.setOnClickListener { + controlsBinding.forward.setOnClickListener { exoPlayer?.let { player -> player.seekTo(player.currentPosition + JUMP_TIME_MILLIS) } } - skip_prev.setOnClickListener { + controlsBinding.skipPrev.setOnClickListener { analytic.report(VideoPlayerControlClickedEvent(VideoPlayerControlClickedEvent.ACTION_PREVIOS)) move(StepNavigationDirection.PREV) } - skip_next.setOnClickListener { + controlsBinding.skipNext.setOnClickListener { analytic.report(VideoPlayerControlClickedEvent(VideoPlayerControlClickedEvent.ACTION_NEXT)) move(StepNavigationDirection.NEXT) } viewStateDelegate = ViewStateDelegate() - viewStateDelegate.addState(center_controller_panel) - viewStateDelegate.addState(autoplay_controller_panel, autoplayCancel, autoplaySwitch) - viewStateDelegate.addState(autoplay_controller_panel, autoplayCancel, autoplaySwitch) + viewStateDelegate.addState(controlsBinding.centerControllerPanel) + viewStateDelegate.addState( + controlsBinding.autoplayControllerPanel, + controlsBinding.autoplayCancel, + controlsBinding.autoplaySwitch + ) + viewStateDelegate.addState( + controlsBinding.autoplayControllerPanel, + controlsBinding.autoplayCancel, + controlsBinding.autoplaySwitch + ) } private fun injectComponent() { @@ -421,11 +437,11 @@ class VideoPlayerActivity : AppCompatActivity(), VideoPlayerView, VideoQualityDi start() } } - autoplayProgress.progress = state.progress + controlsBinding.autoplayProgress.progress = state.progress // without if switch will stuck in one position - if (!autoplaySwitch.isChecked) { - autoplaySwitch.isChecked = true + if (!controlsBinding.autoplaySwitch.isChecked) { + controlsBinding.autoplaySwitch.isChecked = true } } @@ -433,9 +449,9 @@ class VideoPlayerActivity : AppCompatActivity(), VideoPlayerView, VideoQualityDi animator?.cancel() animator = null - autoplayProgress.progress = AUTOPLAY_PROGRESS_MAX - if (autoplaySwitch.isChecked) { - autoplaySwitch.isChecked = false + controlsBinding.autoplayProgress.progress = AUTOPLAY_PROGRESS_MAX + if (controlsBinding.autoplaySwitch.isChecked) { + controlsBinding.autoplaySwitch.isChecked = false } } @@ -447,11 +463,11 @@ class VideoPlayerActivity : AppCompatActivity(), VideoPlayerView, VideoQualityDi override fun setVideoPlayerData(videoPlayerData: VideoPlayerData) { Util.startForegroundService(this, VideoPlayerForegroundService.createIntent(this, videoPlayerData)) - videoRateChooser?.setImageDrawable(videoPlayerData.videoPlaybackRate.icon) + controlsBinding.videoRateChooser.setImageDrawable(videoPlayerData.videoPlaybackRate.icon) - qualityView.isVisible = true - qualityView.text = getString(R.string.video_player_quality_icon, videoPlayerData.videoQuality) - qualityView.setOnClickListener { + controlsBinding.qualityView.isVisible = true + controlsBinding.qualityView.text = getString(R.string.video_player_quality_icon, videoPlayerData.videoQuality) + controlsBinding.qualityView.setOnClickListener { val cachedVideo: Video? = videoPlayerData.mediaData.cachedVideo val externalVideo: Video? = videoPlayerData.mediaData.externalVideo val nowPlaying = videoPlayerData.videoUrl @@ -472,10 +488,10 @@ class VideoPlayerActivity : AppCompatActivity(), VideoPlayerView, VideoQualityDi true } popupMenu.setOnDismissListener { - playerView.hideController() + binding.playerView.hideController() } popupMenu.show() - playerView.showController() + binding.playerView.showController() } override fun setIsLandscapeVideo(isLandScapeVideo: Boolean) { @@ -489,21 +505,21 @@ class VideoPlayerActivity : AppCompatActivity(), VideoPlayerView, VideoQualityDi R.drawable.ic_fullscreen } - exo_fullscreen_icon.setImageResource(fullScreenIconRes) + controlsBinding.exoFullscreenIcon.setImageResource(fullScreenIconRes) } override fun showPlayInBackgroundPopup() { playerInBackroundPopup = PopupHelper .showPopupAnchoredToView( context = this, - anchorView = playerView, + anchorView = binding.playerView, popupText = getString(R.string.video_player_in_background_popup), theme = PopupHelper.PopupTheme.LIGHT, cancelableOnTouchOutside = true, gravity = Gravity.CENTER, withArrow = false ) - playerView.postDelayed({ playerInBackroundPopup?.dismiss() }, IN_BACKGROUND_POPUP_TIMEOUT_MS) + binding.playerView.postDelayed({ playerInBackroundPopup?.dismiss() }, IN_BACKGROUND_POPUP_TIMEOUT_MS) } override fun onQualityChanged(newUrlQuality: VideoUrl?) { @@ -531,7 +547,7 @@ class VideoPlayerActivity : AppCompatActivity(), VideoPlayerView, VideoQualityDi } else { unregisterReceiver(pipReceiver) if (exoPlayer?.isPlaying == false) { - playerView.showController() + binding.playerView.showController() } } } @@ -600,7 +616,7 @@ class VideoPlayerActivity : AppCompatActivity(), VideoPlayerView, VideoQualityDi private fun enterPipMode() { if (isSupportPIP()) { - playerView.hideController() + binding.playerView.hideController() val params = PictureInPictureParams.Builder() this.enterPictureInPictureMode(params.build()) } @@ -632,16 +648,16 @@ class VideoPlayerActivity : AppCompatActivity(), VideoPlayerView, VideoQualityDi val (rewindMargin, skipMargin) = resources.getDimensionPixelOffset(R.dimen.video_player_rewind_margin) to resources.getDimensionPixelOffset(R.dimen.video_player_skip_margin) - skip_prev.layoutParams = (skip_prev.layoutParams as ViewGroup.MarginLayoutParams).apply { + controlsBinding.skipPrev.layoutParams = (controlsBinding.skipPrev.layoutParams as ViewGroup.MarginLayoutParams).apply { rightMargin = skipMargin } - rewind.layoutParams = (rewind.layoutParams as ViewGroup.MarginLayoutParams).apply { + controlsBinding.rewind.layoutParams = (controlsBinding.rewind.layoutParams as ViewGroup.MarginLayoutParams).apply { rightMargin = rewindMargin } - forward.layoutParams = (forward.layoutParams as ViewGroup.MarginLayoutParams).apply { + controlsBinding.forward.layoutParams = (controlsBinding.forward.layoutParams as ViewGroup.MarginLayoutParams).apply { leftMargin = rewindMargin } - skip_next.layoutParams = (skip_next.layoutParams as ViewGroup.MarginLayoutParams).apply { + controlsBinding.skipNext.layoutParams = (controlsBinding.skipNext.layoutParams as ViewGroup.MarginLayoutParams).apply { leftMargin = skipMargin } } @@ -659,4 +675,4 @@ class VideoPlayerActivity : AppCompatActivity(), VideoPlayerView, VideoQualityDi super.finish() overridePendingTransition(R.anim.slide_in_from_start, R.anim.slide_out_to_end) } -} \ No newline at end of file +} diff --git a/app/src/main/res/layout-land/dialog_achievement_details.xml b/app/src/main/res/layout-land/dialog_achievement_details.xml index b0e7aba4a1..d0a628da09 100644 --- a/app/src/main/res/layout-land/dialog_achievement_details.xml +++ b/app/src/main/res/layout-land/dialog_achievement_details.xml @@ -4,8 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="wrap_content" - android:layout_height="wrap_content" - tools:viewBindingIgnore="true"> + android:layout_height="wrap_content"> + tools:visibility="visible"> + tools:visibility="visible"> + tools:visibility="visible"> + xmlns:tools="http://schemas.android.com/tools"> + android:layout_height="wrap_content"> + xmlns:app="http://schemas.android.com/apk/res-auto"> +> @@ -15,11 +14,17 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + - + - + + android:id="@+id/root"> + android:layout_height="match_parent"> - - + + + > + android:orientation="vertical"> - + - + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_lesson.xml b/app/src/main/res/layout/activity_lesson.xml index 391d106cd9..deaae84a26 100644 --- a/app/src/main/res/layout/activity_lesson.xml +++ b/app/src/main/res/layout/activity_lesson.xml @@ -2,17 +2,17 @@ + android:layout_height="match_parent"> - + diff --git a/app/src/main/res/layout/activity_main_feed.xml b/app/src/main/res/layout/activity_main_feed.xml index 4a6d544eb8..d3f64c0743 100644 --- a/app/src/main/res/layout/activity_main_feed.xml +++ b/app/src/main/res/layout/activity_main_feed.xml @@ -2,10 +2,8 @@ + android:layout_height="match_parent"> + android:layout_height="match_parent"> + xmlns:tools="http://schemas.android.com/tools"> diff --git a/app/src/main/res/layout/activity_onboarding_goal.xml b/app/src/main/res/layout/activity_onboarding_goal.xml index 0c6802d0ad..988b6adfda 100644 --- a/app/src/main/res/layout/activity_onboarding_goal.xml +++ b/app/src/main/res/layout/activity_onboarding_goal.xml @@ -5,7 +5,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - tools:viewBindingIgnore="true"> + > + android:layout_height="match_parent"> + android:layout_height="match_parent"> + android:layout_height="match_parent"> + > - + - + - + +> + android:keepScreenOn="true"> + android:layout_height="wrap_content"> +> +> +> + android:layout_height="match_parent"> diff --git a/app/src/main/res/layout/bottom_sheet_dialog_fill_blanks_input.xml b/app/src/main/res/layout/bottom_sheet_dialog_fill_blanks_input.xml index e2211c2c09..4335eaf03d 100644 --- a/app/src/main/res/layout/bottom_sheet_dialog_fill_blanks_input.xml +++ b/app/src/main/res/layout/bottom_sheet_dialog_fill_blanks_input.xml @@ -4,8 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="match_parent" - tools:viewBindingIgnore="true"> + android:layout_height="match_parent"> + android:scrollbarStyle="outsideOverlay"> + > + android:scrollbarStyle="outsideOverlay"> +> + android:layout_height="match_parent"> + app:cardUseCompatPadding="true"> + android:layout_height="wrap_content"> + android:layout_height="wrap_content"> + android:layout_width="match_parent" + android:layout_height="match_parent"> + android:layout_width="match_parent" + android:layout_height="match_parent"> + android:layout_height="match_parent"> + android:orientation="vertical"> - + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_rate_app.xml b/app/src/main/res/layout/dialog_rate_app.xml index aff18482dc..654027707c 100644 --- a/app/src/main/res/layout/dialog_rate_app.xml +++ b/app/src/main/res/layout/dialog_rate_app.xml @@ -3,8 +3,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="wrap_content" - tools:viewBindingIgnore="true"> + android:layout_height="wrap_content"> + > +> + android:layout_height="match_parent"> - + - + - + \ No newline at end of file diff --git a/app/src/main/res/layout/downloaded_course_item.xml b/app/src/main/res/layout/downloaded_course_item.xml index f7d9c071bf..55c72cd11e 100644 --- a/app/src/main/res/layout/downloaded_course_item.xml +++ b/app/src/main/res/layout/downloaded_course_item.xml @@ -6,7 +6,7 @@ android:layout_width="match_parent" android:layout_height="96dp" android:background="?selectableItemBackground" - tools:viewBindingIgnore="true"> +> +> + tools:visibility="visible"> +> + > + tools:visibility="visible"> + tools:visiblity="visible"> + tools:visibility="visible"> + tools:visibility="visible"> + tools:visibility="visible"> + tools:visibility="visible"> +> + tools:visibility="visible"> + android:fitsSystemWindows="true"> - diff --git a/app/src/main/res/layout/fragment_about_app.xml b/app/src/main/res/layout/fragment_about_app.xml index 429d235211..4eef0ccb3c 100644 --- a/app/src/main/res/layout/fragment_about_app.xml +++ b/app/src/main/res/layout/fragment_about_app.xml @@ -4,9 +4,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="wrap_content"> android:orientation="vertical" - tools:viewBindingIgnore="true"> + android:layout_height="match_parent"> - + + xmlns:app="http://schemas.android.com/apk/res-auto"> + xmlns:android="http://schemas.android.com/apk/res/android"> - + +> - + +> + android:layout_height="match_parent"> + android:orientation="vertical"> - + - \ No newline at end of file + diff --git a/app/src/main/res/layout/fragment_course_reviews.xml b/app/src/main/res/layout/fragment_course_reviews.xml index c85d8b019e..cefe766569 100644 --- a/app/src/main/res/layout/fragment_course_reviews.xml +++ b/app/src/main/res/layout/fragment_course_reviews.xml @@ -1,10 +1,8 @@ + android:layout_height="match_parent"> +> + android:layout_height="wrap_content"> + android:padding="8dp"> + android:orientation="vertical"> \ No newline at end of file + android:paddingBottom="16dp"/> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_notification_settings.xml b/app/src/main/res/layout/fragment_notification_settings.xml index dac557b227..f3582a7d68 100644 --- a/app/src/main/res/layout/fragment_notification_settings.xml +++ b/app/src/main/res/layout/fragment_notification_settings.xml @@ -1,12 +1,10 @@ + android:scrollbarStyle="outsideOverlay"> + android:layout_height="match_parent"> + android:background="?android:colorBackground"> + android:layout_height="wrap_content"> + android:orientation="vertical"> + android:layout_height="wrap_content"> + android:orientation="vertical"> + android:orientation="vertical"> + > + android:layout_height="wrap_content"> - + + android:id="@+id/rootView"> - + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml index 197e17e953..678646a84a 100644 --- a/app/src/main/res/layout/fragment_settings.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -1,11 +1,9 @@ + android:scrollbarStyle="outsideOverlay"> + android:scrollbars="none"> + android:fillViewport="true"> + android:layout_height="match_parent"> + > +> + > + android:layout_height="wrap_content"> @@ -30,4 +28,4 @@ app:layout_constraintTop_toBottomOf="@id/reviewStep2Status" app:layout_constraintBottom_toTopOf="@id/reviewStep5Status" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/fragment_step_quiz_review_peer.xml b/app/src/main/res/layout/fragment_step_quiz_review_peer.xml index e416589782..619591e08b 100644 --- a/app/src/main/res/layout/fragment_step_quiz_review_peer.xml +++ b/app/src/main/res/layout/fragment_step_quiz_review_peer.xml @@ -7,7 +7,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" - tools:viewBindingIgnore="true"> + > diff --git a/app/src/main/res/layout/fragment_step_quiz_review_teacher.xml b/app/src/main/res/layout/fragment_step_quiz_review_teacher.xml index 225bab8df2..dbf4e89677 100644 --- a/app/src/main/res/layout/fragment_step_quiz_review_teacher.xml +++ b/app/src/main/res/layout/fragment_step_quiz_review_teacher.xml @@ -6,7 +6,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - tools:viewBindingIgnore="true"> + > +> + > + android:orientation="vertical"> + android:layout_height="wrap_content"> + app:layout_collapseMode="pin"> + app:layout_collapseMode="pin"> + app:cardCornerRadius="0dp"> + android:orientation="vertical"> + android:background="@drawable/background_home_streak_view"> + app:cardCornerRadius="@dimen/course_item_radius"> + android:orientation="vertical"> - + + tools:layout_width="154dp"> + tools:layout_width="320dp"> + android:orientation="vertical"> - + + android:orientation="vertical"> - + + android:orientation="vertical"> + android:background="@drawable/bg_comment_item"> + android:layout_height="wrap_content"> +> +> + > + > + android:orientation="vertical"> + android:orientation="vertical"> - + + tools:layout_width="154dp"> + tools:layout_width="154dp"> + > + > + tools:layout_width="154dp"> + android:layout_margin="12dp"> + > + > + > + tools:layout_width="154dp"> + xmlns:tools="http://schemas.android.com/tools"> + > \ No newline at end of file + android:textAppearance="?textAppearanceBody1" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_step_quiz_fill_blanks_select.xml b/app/src/main/res/layout/item_step_quiz_fill_blanks_select.xml index 257cd22724..3cd60699dc 100644 --- a/app/src/main/res/layout/item_step_quiz_fill_blanks_select.xml +++ b/app/src/main/res/layout/item_step_quiz_fill_blanks_select.xml @@ -1,7 +1,6 @@ \ No newline at end of file + app:drawableEndCompat="@drawable/ic_arrow_bottom" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_step_quiz_fill_blanks_text.xml b/app/src/main/res/layout/item_step_quiz_fill_blanks_text.xml index c438e4c022..825c813e87 100644 --- a/app/src/main/res/layout/item_step_quiz_fill_blanks_text.xml +++ b/app/src/main/res/layout/item_step_quiz_fill_blanks_text.xml @@ -1,12 +1,10 @@ \ No newline at end of file + android:textAppearance="?textAppearanceBody1" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_step_quiz_sorting.xml b/app/src/main/res/layout/item_step_quiz_sorting.xml index 6b426b269a..e57ee15cd0 100644 --- a/app/src/main/res/layout/item_step_quiz_sorting.xml +++ b/app/src/main/res/layout/item_step_quiz_sorting.xml @@ -9,7 +9,7 @@ android:layout_marginBottom="4dp" app:strokeWidth="1dp" app:strokeColor="@color/color_step_quiz_sorting_stroke" - tools:viewBindingIgnore="true"> + > diff --git a/app/src/main/res/layout/item_submission_data.xml b/app/src/main/res/layout/item_submission_data.xml index f69c0dde32..acf9ace27c 100644 --- a/app/src/main/res/layout/item_submission_data.xml +++ b/app/src/main/res/layout/item_submission_data.xml @@ -4,8 +4,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="wrap_content" - tools:viewBindingIgnore="true"> + android:layout_height="wrap_content"> +> +> +> +> + xmlns:tools="http://schemas.android.com/tools"> + android:layout_height="wrap_content"> + android:layout_height="wrap_content"> +> + tools:background="@color/new_accent_color"> \ No newline at end of file + app:layout_constraintBottom_toTopOf="@id/stepQuizFeedbackBlocks" /> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_step_quiz_code.xml b/app/src/main/res/layout/layout_step_quiz_code.xml index f19a8f06be..ef35362087 100644 --- a/app/src/main/res/layout/layout_step_quiz_code.xml +++ b/app/src/main/res/layout/layout_step_quiz_code.xml @@ -18,7 +18,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toTopOf="@id/stepQuizFeedbackBlocks" - tools:viewBindingIgnore="true"> +> + > diff --git a/app/src/main/res/layout/layout_step_quiz_code_fullscreen_playground.xml b/app/src/main/res/layout/layout_step_quiz_code_fullscreen_playground.xml index b07971dca4..5aae75cd8f 100644 --- a/app/src/main/res/layout/layout_step_quiz_code_fullscreen_playground.xml +++ b/app/src/main/res/layout/layout_step_quiz_code_fullscreen_playground.xml @@ -6,7 +6,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - tools:viewBindingIgnore="true"> + > + > \ No newline at end of file + /> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_step_quiz_feedback_block.xml b/app/src/main/res/layout/layout_step_quiz_feedback_block.xml index 1851010a87..4df7ac397a 100644 --- a/app/src/main/res/layout/layout_step_quiz_feedback_block.xml +++ b/app/src/main/res/layout/layout_step_quiz_feedback_block.xml @@ -6,7 +6,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - tools:viewBindingIgnore="true"> + > \ No newline at end of file + tools:listitem="@layout/item_step_quiz_sorting" /> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_step_quiz_review_footer.xml b/app/src/main/res/layout/layout_step_quiz_review_footer.xml index 4900b54e22..d43756aa9b 100644 --- a/app/src/main/res/layout/layout_step_quiz_review_footer.xml +++ b/app/src/main/res/layout/layout_step_quiz_review_footer.xml @@ -2,7 +2,6 @@ diff --git a/app/src/main/res/layout/layout_step_quiz_review_header.xml b/app/src/main/res/layout/layout_step_quiz_review_header.xml index 930007f312..0242639ca2 100644 --- a/app/src/main/res/layout/layout_step_quiz_review_header.xml +++ b/app/src/main/res/layout/layout_step_quiz_review_header.xml @@ -2,7 +2,6 @@ diff --git a/app/src/main/res/layout/layout_step_quiz_sorting.xml b/app/src/main/res/layout/layout_step_quiz_sorting.xml index dc4729532c..a525773669 100644 --- a/app/src/main/res/layout/layout_step_quiz_sorting.xml +++ b/app/src/main/res/layout/layout_step_quiz_sorting.xml @@ -25,4 +25,4 @@ app:layout_constraintBottom_toTopOf="@id/stepQuizFeedbackBlocks" tools:listitem="@layout/item_step_quiz_sorting" - tools:viewBindingIgnore="true" /> \ No newline at end of file + /> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_step_quiz_sql.xml b/app/src/main/res/layout/layout_step_quiz_sql.xml index 6b07fbc63e..08b4328970 100644 --- a/app/src/main/res/layout/layout_step_quiz_sql.xml +++ b/app/src/main/res/layout/layout_step_quiz_sql.xml @@ -18,7 +18,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toTopOf="@id/stepQuizFeedbackBlocks" - tools:viewBindingIgnore="true"> + > diff --git a/app/src/main/res/layout/layout_step_quiz_table.xml b/app/src/main/res/layout/layout_step_quiz_table.xml index 3356af2697..7b1798cdd0 100644 --- a/app/src/main/res/layout/layout_step_quiz_table.xml +++ b/app/src/main/res/layout/layout_step_quiz_table.xml @@ -19,4 +19,4 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toTopOf="@id/stepQuizFeedbackBlocks" - tools:viewBindingIgnore="true" /> \ No newline at end of file + tools:listitem="@layout/item_table_selection" /> diff --git a/app/src/main/res/layout/layout_step_quiz_text.xml b/app/src/main/res/layout/layout_step_quiz_text.xml index a05cf620ce..b3c7dc3a93 100644 --- a/app/src/main/res/layout/layout_step_quiz_text.xml +++ b/app/src/main/res/layout/layout_step_quiz_text.xml @@ -31,5 +31,4 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toTopOf="@id/stepQuizFeedbackBlocks" - tools:targetApi="lollipop" - tools:viewBindingIgnore="true" /> \ No newline at end of file + tools:targetApi="lollipop" /> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_step_tab_icon.xml b/app/src/main/res/layout/layout_step_tab_icon.xml index d041238cf4..b0b7244acb 100644 --- a/app/src/main/res/layout/layout_step_tab_icon.xml +++ b/app/src/main/res/layout/layout_step_tab_icon.xml @@ -1,10 +1,8 @@ + android:layout_height="match_parent"> + android:layout_height="wrap_content"> - +> + android:paddingStart="8dp"> +> + android:gravity="center_horizontal"> +> - + + android:elevation="?appBarElevation"> - + \ No newline at end of file diff --git a/app/src/main/res/layout/view_centered_toolbar.xml b/app/src/main/res/layout/view_centered_toolbar.xml index 69d474993a..3622d70a5a 100644 --- a/app/src/main/res/layout/view_centered_toolbar.xml +++ b/app/src/main/res/layout/view_centered_toolbar.xml @@ -1,12 +1,10 @@ +android:layout_gravity="center"> \ No newline at end of file + tools:layout_height="300dp" /> \ No newline at end of file diff --git a/app/src/main/res/layout/view_course_benefit_summary.xml b/app/src/main/res/layout/view_course_benefit_summary.xml index 43b6615557..28b3e90461 100644 --- a/app/src/main/res/layout/view_course_benefit_summary.xml +++ b/app/src/main/res/layout/view_course_benefit_summary.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingBottom="24dp" - tools:viewBindingIgnore="true"> + > \ No newline at end of file +/> \ No newline at end of file diff --git a/app/src/main/res/layout/view_course_content_section.xml b/app/src/main/res/layout/view_course_content_section.xml index fbd85123f4..d879614c4e 100644 --- a/app/src/main/res/layout/view_course_content_section.xml +++ b/app/src/main/res/layout/view_course_content_section.xml @@ -7,7 +7,7 @@ android:layout_height="wrap_content" android:background="@color/color_elevation_overlay_1dp" android:foreground="?selectableItemBackground" - tools:viewBindingIgnore="true"> +> + > +> + > + android:layout_margin="@dimen/course_info_block_margin"> + android:orientation="vertical"> diff --git a/app/src/main/res/layout/view_course_review_compose_banner_item.xml b/app/src/main/res/layout/view_course_review_compose_banner_item.xml index 6ab6193dac..b3a3542bba 100644 --- a/app/src/main/res/layout/view_course_review_compose_banner_item.xml +++ b/app/src/main/res/layout/view_course_review_compose_banner_item.xml @@ -2,11 +2,9 @@ + android:orientation="vertical"> + android:layout_height="wrap_content"> + android:layout_height="wrap_content"> + xmlns:app="http://schemas.android.com/apk/res-auto"> +> +> + > + > +> \ No newline at end of file + tools:visibility="visible" /> \ No newline at end of file diff --git a/app/src/main/res/layout/view_notification_interval_chooser.xml b/app/src/main/res/layout/view_notification_interval_chooser.xml index 66c332dfe4..0772935ca9 100644 --- a/app/src/main/res/layout/view_notification_interval_chooser.xml +++ b/app/src/main/res/layout/view_notification_interval_chooser.xml @@ -6,8 +6,7 @@ android:background="?selectableItemBackground" android:layout_width="match_parent" android:layout_height="wrap_content" - tools:visibility="visible" - tools:viewBindingIgnore="true"> + tools:visibility="visible"> \ No newline at end of file + /> \ No newline at end of file diff --git a/app/src/main/res/layout/view_review_status.xml b/app/src/main/res/layout/view_review_status.xml index 8a43d6aaeb..436d1c7e4f 100644 --- a/app/src/main/res/layout/view_review_status.xml +++ b/app/src/main/res/layout/view_review_status.xml @@ -4,7 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" tools:parentTag="android.widget.FrameLayout" - tools:viewBindingIgnore="true"> +> + android:elevation="?appBarElevation"> - + - \ No newline at end of file + diff --git a/app/src/main/res/layout/view_step_disabled.xml b/app/src/main/res/layout/view_step_disabled.xml index 27ef21169d..db1c5e6d5f 100644 --- a/app/src/main/res/layout/view_step_disabled.xml +++ b/app/src/main/res/layout/view_step_disabled.xml @@ -8,8 +8,7 @@ android:orientation="vertical" android:gravity="center_horizontal" android:visibility="gone" - tools:visibility="visible" - tools:viewBindingIgnore="true"> + tools:visibility="visible"> + tools:visibility="visible"> + > + android:layout_height="wrap_content"> + android:gravity="center_horizontal"> + > + xmlns:tools="http://schemas.android.com/tools"> + android:layout_height="wrap_content"> + android:foreground="@drawable/story_view_foreground"> + tools:context=".features.stories.ui.activity.StoriesActivity"> + android:background="?selectableItemBackground"> + android:background="?selectableItemBackground"> + android:elevation="?appBarElevation"> - + + > + > = ViewHolder(createView(parent, R.layout.item_in_app_purchase)) - private inner class ViewHolder(override val containerView: View) : DelegateViewHolder(containerView), LayoutContainer { + private inner class ViewHolder(containerView: View) : DelegateViewHolder(containerView) { + private val viewBinding: ItemInAppPurchaseBinding by viewBinding { ItemInAppPurchaseBinding.bind(itemView) } + init { - inAppPurchaseConsumeAction.setOnClickListener { itemData?.let(onItemClick) } + viewBinding.inAppPurchaseConsumeAction.setOnClickListener { itemData?.let(onItemClick) } } override fun onBind(data: Purchase) { - inAppPurchaseSku.text = data.skus.first() - inAppPurchaseTime.text = context.getString(R.string.debug_purchase_date, DateTimeHelper.getPrintableDate(Date(data.purchaseTime), DateTimeHelper.DISPLAY_DATETIME_PATTERN, TimeZone.getDefault())) - inAppPurchaseStatus.text = context.getString(R.string.debug_purchase_status, data.purchaseState.toString()) + viewBinding.inAppPurchaseSku.text = data.skus.first() + viewBinding.inAppPurchaseTime.text = context.getString(R.string.debug_purchase_date, DateTimeHelper.getPrintableDate(Date(data.purchaseTime), DateTimeHelper.DISPLAY_DATETIME_PATTERN, TimeZone.getDefault())) + viewBinding.inAppPurchaseStatus.text = context.getString(R.string.debug_purchase_status, data.purchaseState.toString()) - inAppPurchaseCourse.isVisible = data.developerPayload.isNotEmpty() - inAppPurchaseUser.isVisible = data.developerPayload.isNotEmpty() + viewBinding.inAppPurchaseCourse.isVisible = data.developerPayload.isNotEmpty() + viewBinding.inAppPurchaseUser.isVisible = data.developerPayload.isNotEmpty() if (data.developerPayload.isNotEmpty()) { data.developerPayload.toObject().let { - inAppPurchaseCourse.text = context.getString(R.string.debug_purchase_course, it.courseId) - inAppPurchaseUser.text = context.getString(R.string.debug_purchase_profile, it.profileId) + viewBinding.inAppPurchaseCourse.text = context.getString(R.string.debug_purchase_course, it.courseId) + viewBinding.inAppPurchaseUser.text = context.getString(R.string.debug_purchase_profile, it.profileId) } } } } -} \ No newline at end of file +} diff --git a/app/src/stageDebuggable/java/org/stepik/android/view/debug/ui/adapter/delegate/SplitTestDataAdapterDelegate.kt b/app/src/stageDebuggable/java/org/stepik/android/view/debug/ui/adapter/delegate/SplitTestDataAdapterDelegate.kt index 6f6bd20719..454b573f13 100644 --- a/app/src/stageDebuggable/java/org/stepik/android/view/debug/ui/adapter/delegate/SplitTestDataAdapterDelegate.kt +++ b/app/src/stageDebuggable/java/org/stepik/android/view/debug/ui/adapter/delegate/SplitTestDataAdapterDelegate.kt @@ -2,9 +2,9 @@ package org.stepik.android.view.debug.ui.adapter.delegate import android.view.View import android.view.ViewGroup -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_split_test_data.* +import by.kirich1409.viewbindingdelegate.viewBinding import org.stepic.droid.R +import org.stepic.droid.databinding.ItemSplitTestDataBinding import org.stepik.android.domain.debug.model.SplitTestData import ru.nobird.android.ui.adapterdelegates.AdapterDelegate import ru.nobird.android.ui.adapterdelegates.DelegateViewHolder @@ -18,13 +18,15 @@ class SplitTestDataAdapterDelegate( override fun onCreateViewHolder(parent: ViewGroup): DelegateViewHolder = ViewHolder(createView(parent, R.layout.item_split_test_data)) - private inner class ViewHolder(override val containerView: View) : DelegateViewHolder(containerView), LayoutContainer { + private inner class ViewHolder(containerView: View) : DelegateViewHolder(containerView) { + private val viewBinding: ItemSplitTestDataBinding by viewBinding { ItemSplitTestDataBinding.bind(itemView) } + init { containerView.setOnClickListener { itemData?.let { onItemClick(it.splitTestName, it.splitTestValue, it.splitTestGroups) } } } override fun onBind(data: SplitTestData) { - splitTestGroupTitle.text = data.splitTestName - splitTestGroupValue.text = data.splitTestValue + viewBinding.splitTestGroupTitle.text = data.splitTestName + viewBinding.splitTestGroupValue.text = data.splitTestValue } } -} \ No newline at end of file +} diff --git a/billing/build.gradle b/billing/build.gradle index 1eb45b3e84..2f5da83018 100644 --- a/billing/build.gradle +++ b/billing/build.gradle @@ -1,6 +1,5 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' android { diff --git a/docs/kotlin-migration/kotlin-1.9.25-migration-tracker.md b/docs/kotlin-migration/kotlin-1.9.25-migration-tracker.md new file mode 100644 index 0000000000..e330613435 --- /dev/null +++ b/docs/kotlin-migration/kotlin-1.9.25-migration-tracker.md @@ -0,0 +1,275 @@ +# Kotlin 1.9.25 Migration Tracker + +Last updated: 2026-06-14. + +## Goal + +Upgrade the project from Kotlin `1.7.10` to Kotlin `1.9.25` without losing the +recent migration work from Kotlin Android Extensions synthetics to ViewBinding +and from old Kotlin parcel APIs to `kotlin-parcelize`. + +This tracker records confirmed local findings, library risks, and the expected +order of work. + +## Current Build Baseline + +Current local project state: + +- Gradle wrapper: `8.7` in `gradle/wrapper/gradle-wrapper.properties`. +- Android Gradle Plugin: `8.6.1` in `dependencies.gradle`. +- Kotlin Gradle plugin: `1.7.10` in `dependencies.gradle`. +- `compileSdk` / `targetSdk`: `35`. +- `:app` applies `kotlin-android`, `kotlin-kapt`, and `kotlin-parcelize`. +- `:model` applies `kotlin-android`, `kotlin-kapt`, and `kotlin-parcelize`. +- `:billing` applies `kotlin-android` and `kotlin-kapt`. +- `:androidsvg` is Java-only and does not apply Kotlin plugins. + +Use this build command during the migration: + +```bash +./gradlew :app:assembleDebug --rerun-tasks -Pkotlin.compiler.execution.strategy=in-process +``` + +This command is currently used to avoid Kotlin compilation errors caused by the +temporary mismatch between Kotlin, Android Gradle Plugin, and Gradle versions. +After the migration is complete and the versions are aligned, the usual build +and compilation commands should work again without this workaround. + +## Confirmed Source Migration State + +Already done: + +- No remaining `kotlinx.android.synthetic` imports were found. +- No remaining old `kotlinx.android.parcel` or `kotlin.android.parcel` imports + were found. +- `kotlin-parcelize` is already applied in `:app` and `:model`. +- Existing `@Parcelize` imports use `kotlinx.parcelize`. + +Done: + +- Removed remaining `kotlinx.android.extensions.LayoutContainer` usage: + - `app/src/main/java/org/stepik/android/view/course_list/ui/adapter/delegate/CourseListViewAllAdapterDelegate.kt` + - `app/src/main/java/org/stepik/android/view/user_reviews/ui/adapter/delegate/UserReviewsPlaceholderAdapterDelegate.kt` + +Both classes now use plain `DelegateViewHolder(...)` implementations. +They no longer use synthetic view access, so `LayoutContainer` is unnecessary. + +## Toolchain Compatibility Notes + +Kotlin `1.9.25` with AGP `8.6.1` and Gradle `8.7` is a risk area. The local +project has already moved past the old AGP 7.x blocker, but Kotlin/AGP/Gradle +support should still be checked against the official Kotlin Gradle compatibility +matrix before the final version bump. + +Reference: + +- https://kotlinlang.org/docs/gradle-configure-project.html + +Decision to make: + +- Either proceed with Kotlin `1.9.25` and keep the temporary in-process compiler + workaround during migration. +- Or consider a Kotlin version that is officially aligned with the current AGP + version if the build remains unstable. + +## Dependency Findings + +Gradle dependency insight showed that runtime Kotlin artifacts currently resolve +to Kotlin stdlib `1.8.22`, even though the project declares Kotlin `1.7.10`. +This happens through transitive dependencies, especially Firebase. + +Migration requirement: + +- Explicitly align Kotlin artifacts when moving to `1.9.25`. +- Do not let transitive dependencies decide the Kotlin stdlib version. +- Prefer a Kotlin BOM or a resolution strategy that keeps `kotlin-stdlib`, + `kotlin-stdlib-jdk7`, and `kotlin-stdlib-jdk8` consistent with the compiler. + +## High Priority Library Work + +### Room + +Current version: `androidx.room:room-runtime` / `room-compiler` `2.3.0`. + +Risk: + +- Old Room compiler runs through `kapt` and is a likely failure point after the + Kotlin compiler upgrade. + +Recommended migration: + +- Upgrade Room to at least `2.6.1`; prefer the current stable line after testing. +- Consider moving Room annotation processing from `kapt` to `ksp`. +- Re-run database schema/migration tests after the upgrade. + +References: + +- https://developer.android.com/jetpack/androidx/releases/room +- https://developer.android.com/build/migrate-to-ksp + +### Dagger + +Current version: `2.42`. + +Risk: + +- Dagger compiler runs through `kapt` in `:app` and `:billing`. +- Kotlin compiler upgrades often expose old kapt/annotation processor issues. + +Recommended migration: + +- Upgrade Dagger before or together with the Kotlin bump. +- Keep `kapt` initially unless there is a dedicated Dagger KSP migration task. +- If moving to KSP later, do it after a successful Kotlin/compiler baseline. + +### Glide Compiler + +Current version: `com.github.bumptech.glide:compiler` `4.11.0`. + +Risk: + +- Old annotation processor. + +Recommended migration: + +- Upgrade Glide and its compiler together. +- If staying on Glide 4.x, use `4.16.0` as the conservative target before + considering Glide 5.x. + +### ktlint and Custom Rules + +Current versions: + +- `com.pinterest:ktlint:0.34.2` +- `ru.nobird.android.ktlint:rules:1.0.0` + +Risk: + +- Very old ktlint and custom rule APIs may not work cleanly with modern Kotlin + syntax/parser behavior. +- Custom rules are private package dependencies and must be validated with + access to the GitHub Packages repository. + +Recommended migration: + +- Treat ktlint separately from compilation. +- First get `assembleDebug` and unit tests passing. +- Then update ktlint and custom rules, or temporarily decouple ktlint from the + Kotlin compiler migration if rule updates are not ready. + +## Firebase KTX Migration Risk + +Current project uses Firebase KTX dependencies: + +- `firebase-analytics-ktx` +- `firebase-config-ktx` + +Current code imports KTX APIs such as: + +- `com.google.firebase.ktx.Firebase` +- `com.google.firebase.analytics.ktx.analytics` +- `com.google.firebase.remoteconfig.ktx.remoteConfig` +- `com.google.firebase.remoteconfig.ktx.remoteConfigSettings` +- `com.google.firebase.remoteconfig.ktx.get` + +Risk: + +- Firebase stopped releasing KTX modules and removed them from the Firebase BoM + starting with BoM `34.0.0` in July 2025. + +Recommended migration: + +- If Firebase BoM stays on the current `32.3.1`, this is not the first Kotlin + compiler blocker. +- If Firebase BoM is upgraded as part of the Kotlin/toolchain work, migrate away + from Firebase KTX APIs to the main Firebase modules first. + +Reference: + +- https://firebase.google.com/docs/android/kotlin-migration + +## Kotlin-Authored Library Compatibility Checks + +These libraries currently request older Kotlin stdlib versions transitively. +Older Kotlin metadata is usually readable by Kotlin `1.9.25`, so this is not +automatically fatal. Still, these should be smoke-tested because they are +Kotlin-authored and used heavily in presentation/UI code. + +Check or upgrade: + +- `com.github.kirich1409:viewbindingpropertydelegate-noreflection:1.4.7` + - Maven Central metadata lists `1.5.9` as latest. +- `ru.nobird.app.core:model:1.0.8` + - Dependency insight showed Kotlin stdlib `1.5.31` requested transitively. +- `ru.nobird.app.presentation:presentation-redux:1.3.1` + - Dependency insight showed Kotlin stdlib `1.5.31` requested transitively. +- `ru.nobird.android.*` AndroidKit packages + - Private GitHub Packages dependencies; validate available versions with + credentials. +- `ru.nobird.android:storieskit:1.1.2` + - Private GitHub Packages dependency; validate available versions with + credentials. + +Lower priority old Kotlin transitive requests: + +- `io.reactivex.rxjava2:rxkotlin:2.3.0` requests Kotlin stdlib `1.2.60`. +- `jp.wasabeef:recyclerview-animators:4.0.1` requests Kotlin stdlib `1.3.72`. +- `com.vk:androidsdk:2.2.3` requests Kotlin stdlib `1.3.71`. + +These are runtime dependencies rather than compiler plugins. They should be +checked, but they are less likely to block the Kotlin compiler upgrade directly. + +## Build Script Cleanup + +Remove unnecessary kapt usage: + +- `:model` applies `kotlin-kapt`, but `:model:kaptDebug` has no dependencies. +- Remove `apply plugin: 'kotlin-kapt'` from `model/build.gradle` unless a new + processor is added there. + +Review deprecated AGP options after the Kotlin upgrade: + +- `android.defaults.buildfeatures.buildconfig=true` is deprecated and should be + moved into module-level `buildFeatures`. +- `lintOptions`, `packagingOptions`, and other old DSL blocks can be migrated + later if they are not blocking the Kotlin upgrade. + +## Suggested Migration Order + +1. LayoutContainer cleanup is complete. +2. Remove unused `kotlin-kapt` from `:model`. +3. Align Kotlin Gradle plugin and Kotlin stdlib artifacts to `1.9.25`. +4. Run the temporary safe build command: + + ```bash + ./gradlew :app:assembleDebug --rerun-tasks -Pkotlin.compiler.execution.strategy=in-process + ``` + +5. Upgrade annotation processors in this order: + - Dagger + - Room + - Glide compiler +6. Decide whether to migrate Room from `kapt` to `ksp`. +7. Validate private `ru.nobird.*` and `StoriesKit` package versions. +8. If Firebase BoM is upgraded, migrate Firebase KTX usages. +9. Restore usual build commands after toolchain versions are aligned and stable. +10. Run verification: + + ```bash + ./gradlew :model:compileDebugKotlin + ./gradlew :billing:compileDebugKotlin + ./gradlew :app:compileDebugKotlin + ./gradlew :app:testDebugUnitTest + ./gradlew :app:assembleDebug + ``` + +## Open Questions + +- Which Kotlin target is final: strict `1.9.25`, or a Kotlin version officially + aligned with AGP `8.6.1`? +- Are newer private `ru.nobird.*`, `StoriesKit`, and ktlint-rule artifacts + available in GitHub Packages? +- Should Room move to KSP during this migration, or stay on kapt until Kotlin + `1.9.25` is stable? +- Should Firebase BoM be upgraded in the same PR, or deferred to avoid mixing + Firebase KTX removal with the Kotlin compiler upgrade? diff --git a/model/build.gradle b/model/build.gradle index a88b66f19f..beed198c7d 100644 --- a/model/build.gradle +++ b/model/build.gradle @@ -1,7 +1,7 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-parcelize' apply from: '../code_quality_tools/jacoco.gradle' android { diff --git a/model/src/main/java/org/stepik/android/model/Actions.kt b/model/src/main/java/org/stepik/android/model/Actions.kt index ae6b0af422..cb64587433 100644 --- a/model/src/main/java/org/stepik/android/model/Actions.kt +++ b/model/src/main/java/org/stepik/android/model/Actions.kt @@ -2,7 +2,7 @@ package org.stepik.android.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Parcelize data class Actions( diff --git a/model/src/main/java/org/stepik/android/model/Cell.kt b/model/src/main/java/org/stepik/android/model/Cell.kt index d8248fb736..7c58b3c376 100644 --- a/model/src/main/java/org/stepik/android/model/Cell.kt +++ b/model/src/main/java/org/stepik/android/model/Cell.kt @@ -2,7 +2,7 @@ package org.stepik.android.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import ru.nobird.app.core.model.Identifiable @Parcelize diff --git a/model/src/main/java/org/stepik/android/model/Certificate.kt b/model/src/main/java/org/stepik/android/model/Certificate.kt index b5151179ab..7b1d76e9b4 100644 --- a/model/src/main/java/org/stepik/android/model/Certificate.kt +++ b/model/src/main/java/org/stepik/android/model/Certificate.kt @@ -2,7 +2,7 @@ package org.stepik.android.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import java.util.Date @Parcelize diff --git a/model/src/main/java/org/stepik/android/model/Course.kt b/model/src/main/java/org/stepik/android/model/Course.kt index 81e171297a..de07ffcc35 100644 --- a/model/src/main/java/org/stepik/android/model/Course.kt +++ b/model/src/main/java/org/stepik/android/model/Course.kt @@ -2,7 +2,7 @@ package org.stepik.android.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import ru.nobird.app.core.model.Identifiable import java.util.Date diff --git a/model/src/main/java/org/stepik/android/model/CourseActions.kt b/model/src/main/java/org/stepik/android/model/CourseActions.kt index 5051546301..56ca764ded 100644 --- a/model/src/main/java/org/stepik/android/model/CourseActions.kt +++ b/model/src/main/java/org/stepik/android/model/CourseActions.kt @@ -2,7 +2,7 @@ package org.stepik.android.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Parcelize data class CourseActions( diff --git a/model/src/main/java/org/stepik/android/model/CourseBuyAction.kt b/model/src/main/java/org/stepik/android/model/CourseBuyAction.kt index 636cd6d99d..352de691b9 100644 --- a/model/src/main/java/org/stepik/android/model/CourseBuyAction.kt +++ b/model/src/main/java/org/stepik/android/model/CourseBuyAction.kt @@ -2,7 +2,7 @@ package org.stepik.android.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Parcelize data class CourseBuyAction( diff --git a/model/src/main/java/org/stepik/android/model/CourseCollection.kt b/model/src/main/java/org/stepik/android/model/CourseCollection.kt index 471f399963..8254a46f4f 100644 --- a/model/src/main/java/org/stepik/android/model/CourseCollection.kt +++ b/model/src/main/java/org/stepik/android/model/CourseCollection.kt @@ -2,7 +2,7 @@ package org.stepik.android.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import ru.nobird.app.core.model.Identifiable @Parcelize diff --git a/model/src/main/java/org/stepik/android/model/Lesson.kt b/model/src/main/java/org/stepik/android/model/Lesson.kt index 0efd602d75..cccfc890df 100644 --- a/model/src/main/java/org/stepik/android/model/Lesson.kt +++ b/model/src/main/java/org/stepik/android/model/Lesson.kt @@ -2,8 +2,8 @@ package org.stepik.android.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.IgnoredOnParcel -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.IgnoredOnParcel +import kotlinx.parcelize.Parcelize import ru.nobird.app.core.model.Identifiable import java.util.Date diff --git a/model/src/main/java/org/stepik/android/model/Progress.kt b/model/src/main/java/org/stepik/android/model/Progress.kt index 8290fe87fd..ec5fa89c60 100644 --- a/model/src/main/java/org/stepik/android/model/Progress.kt +++ b/model/src/main/java/org/stepik/android/model/Progress.kt @@ -2,7 +2,7 @@ package org.stepik.android.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import ru.nobird.app.core.model.Identifiable @Parcelize diff --git a/model/src/main/java/org/stepik/android/model/Reply.kt b/model/src/main/java/org/stepik/android/model/Reply.kt index 3ff8f9bed3..efbc4e6e7b 100644 --- a/model/src/main/java/org/stepik/android/model/Reply.kt +++ b/model/src/main/java/org/stepik/android/model/Reply.kt @@ -2,7 +2,7 @@ package org.stepik.android.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Parcelize data class Reply( diff --git a/model/src/main/java/org/stepik/android/model/Section.kt b/model/src/main/java/org/stepik/android/model/Section.kt index 084fa97016..e81f561bf1 100644 --- a/model/src/main/java/org/stepik/android/model/Section.kt +++ b/model/src/main/java/org/stepik/android/model/Section.kt @@ -2,7 +2,7 @@ package org.stepik.android.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import ru.nobird.app.core.model.Identifiable import java.util.Date diff --git a/model/src/main/java/org/stepik/android/model/Step.kt b/model/src/main/java/org/stepik/android/model/Step.kt index b5dfccf817..e0b71598dd 100644 --- a/model/src/main/java/org/stepik/android/model/Step.kt +++ b/model/src/main/java/org/stepik/android/model/Step.kt @@ -2,7 +2,7 @@ package org.stepik.android.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import java.util.Date @Parcelize diff --git a/model/src/main/java/org/stepik/android/model/StoryTemplate.kt b/model/src/main/java/org/stepik/android/model/StoryTemplate.kt index 24174a65bb..43f068b27c 100644 --- a/model/src/main/java/org/stepik/android/model/StoryTemplate.kt +++ b/model/src/main/java/org/stepik/android/model/StoryTemplate.kt @@ -3,7 +3,7 @@ package org.stepik.android.model import android.os.Parcel import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize class StoryTemplate( @SerializedName("id") diff --git a/model/src/main/java/org/stepik/android/model/TableChoiceAnswer.kt b/model/src/main/java/org/stepik/android/model/TableChoiceAnswer.kt index 04de9b1087..4e631f4e32 100644 --- a/model/src/main/java/org/stepik/android/model/TableChoiceAnswer.kt +++ b/model/src/main/java/org/stepik/android/model/TableChoiceAnswer.kt @@ -2,7 +2,7 @@ package org.stepik.android.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Parcelize data class TableChoiceAnswer( diff --git a/model/src/main/java/org/stepik/android/model/Unit.kt b/model/src/main/java/org/stepik/android/model/Unit.kt index e22763bea8..3ce20ea234 100644 --- a/model/src/main/java/org/stepik/android/model/Unit.kt +++ b/model/src/main/java/org/stepik/android/model/Unit.kt @@ -2,7 +2,7 @@ package org.stepik.android.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import ru.nobird.app.core.model.Identifiable import java.util.Date diff --git a/model/src/main/java/org/stepik/android/model/ViewRevenue.kt b/model/src/main/java/org/stepik/android/model/ViewRevenue.kt index e373d356c0..3893268211 100644 --- a/model/src/main/java/org/stepik/android/model/ViewRevenue.kt +++ b/model/src/main/java/org/stepik/android/model/ViewRevenue.kt @@ -2,7 +2,7 @@ package org.stepik.android.model import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Parcelize data class ViewRevenue( diff --git a/model/src/main/java/org/stepik/android/model/attempts/Dataset.kt b/model/src/main/java/org/stepik/android/model/attempts/Dataset.kt index 69bce40679..f03215f24a 100644 --- a/model/src/main/java/org/stepik/android/model/attempts/Dataset.kt +++ b/model/src/main/java/org/stepik/android/model/attempts/Dataset.kt @@ -3,7 +3,7 @@ package org.stepik.android.model.attempts import android.os.Parcel import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Parcelize data class Dataset( diff --git a/model/src/main/java/org/stepik/android/model/code/CodeOptions.kt b/model/src/main/java/org/stepik/android/model/code/CodeOptions.kt index 51c4067164..5617d2601e 100644 --- a/model/src/main/java/org/stepik/android/model/code/CodeOptions.kt +++ b/model/src/main/java/org/stepik/android/model/code/CodeOptions.kt @@ -2,7 +2,7 @@ package org.stepik.android.model.code import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import org.stepik.android.model.util.ParcelableStringList @Parcelize diff --git a/model/src/main/java/org/stepik/android/model/code/UserCodeRun.kt b/model/src/main/java/org/stepik/android/model/code/UserCodeRun.kt index 5fd0f77084..cc7f505ce4 100644 --- a/model/src/main/java/org/stepik/android/model/code/UserCodeRun.kt +++ b/model/src/main/java/org/stepik/android/model/code/UserCodeRun.kt @@ -2,7 +2,7 @@ package org.stepik.android.model.code import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import java.util.* @Parcelize diff --git a/model/src/main/java/org/stepik/android/model/comments/Comment.kt b/model/src/main/java/org/stepik/android/model/comments/Comment.kt index 103fa6a3a7..a71414d1c7 100644 --- a/model/src/main/java/org/stepik/android/model/comments/Comment.kt +++ b/model/src/main/java/org/stepik/android/model/comments/Comment.kt @@ -2,7 +2,7 @@ package org.stepik.android.model.comments import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import org.stepik.android.model.Actions import org.stepik.android.model.UserRole import java.util.* diff --git a/model/src/main/java/org/stepik/android/model/user/Profile.kt b/model/src/main/java/org/stepik/android/model/user/Profile.kt index d99b3e4018..bfd924a49a 100644 --- a/model/src/main/java/org/stepik/android/model/user/Profile.kt +++ b/model/src/main/java/org/stepik/android/model/user/Profile.kt @@ -2,7 +2,7 @@ package org.stepik.android.model.user import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Parcelize data class Profile( diff --git a/model/src/main/java/org/stepik/android/model/user/User.kt b/model/src/main/java/org/stepik/android/model/user/User.kt index 99e5949674..5671b159d4 100644 --- a/model/src/main/java/org/stepik/android/model/user/User.kt +++ b/model/src/main/java/org/stepik/android/model/user/User.kt @@ -2,7 +2,7 @@ package org.stepik.android.model.user import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import ru.nobird.app.core.model.Identifiable import java.util.Date