Skip to content

Commit

Permalink
Fix part of #41: Hifi-StoryChapterList (FULL) (#323)
Browse files Browse the repository at this point in the history
* Story toolbar

* story-header

* UI of recyclerview

* Inital UI for Chapter Item

* Chapter name reformatting

* Color shades for chapter

* Smooth scrolling

* Smooth scroll finish

* Accessiblity Test

* Nit changes

* Tick icon changed

* Nit change
  • Loading branch information
rt4914 authored Nov 13, 2019
1 parent 8673513 commit 2be9940
Show file tree
Hide file tree
Showing 13 changed files with 266 additions and 74 deletions.
4 changes: 3 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".story.StoryActivity" />
<activity
android:name=".story.StoryActivity"
android:theme="@style/OppiaThemeWithoutActionBar" />
<activity android:name=".story.testing.StoryFragmentTestActivity" />
<activity android:name=".testing.BindableAdapterTestActivity" />
<activity android:name=".testing.ContentCardTestActivity" />
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/java/org/oppia/app/story/StoryFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,8 @@ class StoryFragment : InjectableFragment(), ExplorationSelectionListener {
override fun selectExploration(explorationId: String) {
storyFragmentPresenter.handleSelectExploration(explorationId)
}

fun smoothScrollToPosition(position: Int) {
storyFragmentPresenter.smoothScrollToPosition(position)
}
}
57 changes: 51 additions & 6 deletions app/src/main/java/org/oppia/app/story/StoryFragmentPresenter.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package org.oppia.app.story

import android.content.res.Resources
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import org.oppia.app.databinding.StoryChapterViewBinding
import org.oppia.app.databinding.StoryFragmentBinding
import org.oppia.app.databinding.StoryHeaderViewBinding
Expand All @@ -16,25 +17,37 @@ import org.oppia.app.story.storyitemviewmodel.StoryHeaderViewModel
import org.oppia.app.story.storyitemviewmodel.StoryItemViewModel
import org.oppia.app.viewmodel.ViewModelProvider
import javax.inject.Inject
import android.util.TypedValue
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearSmoothScroller
import androidx.recyclerview.widget.RecyclerView

/** The presenter for [StoryFragment]. */
class StoryFragmentPresenter @Inject constructor(
activity: AppCompatActivity,
private val activity: AppCompatActivity,
private val fragment: Fragment,
private val viewModelProvider: ViewModelProvider<StoryViewModel>
) {
private val routeToExplorationListener = activity as RouteToExplorationListener

private lateinit var binding: StoryFragmentBinding
private lateinit var linearLayoutManager: LinearLayoutManager
private lateinit var linearSmoothScroller: RecyclerView.SmoothScroller

fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?, storyId: String): View? {
val viewModel = getStoryViewModel()
val binding = StoryFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false)
binding = StoryFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false)
viewModel.setStoryId(storyId)

viewModel.storyNameLiveData.observe(fragment, Observer<String> { storyName ->
(fragment.activity as? AppCompatActivity)?.supportActionBar?.title = storyName
})
binding.toolbar.setNavigationOnClickListener {
(activity as StoryActivity).finish()
}

linearLayoutManager = LinearLayoutManager(activity.applicationContext)
linearSmoothScroller = createSmoothScroller()

binding.storyChapterList.apply {
layoutManager = linearLayoutManager
adapter = createRecyclerViewAdapter()
}

Expand Down Expand Up @@ -84,4 +97,36 @@ class StoryFragmentPresenter @Inject constructor(
VIEW_TYPE_HEADER,
VIEW_TYPE_CHAPTER
}

fun smoothScrollToPosition(position: Int) {
linearSmoothScroller.targetPosition = position
linearLayoutManager.startSmoothScroll(linearSmoothScroller)
binding.storyChapterList.layoutManager = linearLayoutManager
}

private fun createSmoothScroller(): RecyclerView.SmoothScroller {
val milliSecondsPerInch = 100f

return object : LinearSmoothScroller(activity) {
override fun getVerticalSnapPreference(): Int {
return SNAP_TO_START
}

override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics?): Float {
return milliSecondsPerInch / displayMetrics!!.densityDpi
}

override fun calculateDyToMakeVisible(view: View, snapPreference: Int): Int {
return super.calculateDyToMakeVisible(view, snapPreference) + dipToPixels(48)
}
}
}

private fun dipToPixels(dipValue: Int): Int {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dipValue.toFloat(),
Resources.getSystem().displayMetrics
).toInt()
}
}
15 changes: 11 additions & 4 deletions app/src/main/java/org/oppia/app/story/StoryViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,12 @@ import javax.inject.Inject
/** The ViewModel for [StoryFragment]. */
@FragmentScope
class StoryViewModel @Inject constructor(
fragment: Fragment,
private val fragment: Fragment,
private val topicController: TopicController,
private val logger: Logger
) : ViewModel() {
/** [storyId] needs to be set before any of the live data members can be accessed. */
private lateinit var storyId: String
private lateinit var fragment: StoryFragment
private val explorationSelectionListener = fragment as ExplorationSelectionListener

private val storyResultLiveData: LiveData<AsyncResult<StorySummary>> by lazy {
Expand Down Expand Up @@ -58,6 +57,14 @@ class StoryViewModel @Inject constructor(

private fun processStoryChapterList(storySummary: StorySummary): List<StoryItemViewModel> {
val chapterList: List<ChapterSummary> = storySummary.chapterList

for (position in 0..storySummary.chapterList.size) {
if (storySummary.chapterList[position].chapterPlayState == ChapterPlayState.NOT_STARTED) {
(fragment as StoryFragment).smoothScrollToPosition(position + 1)
break
}
}

val completedCount =
chapterList.filter { chapter -> chapter.chapterPlayState == ChapterPlayState.COMPLETED }.size

Expand All @@ -67,8 +74,8 @@ class StoryViewModel @Inject constructor(
)

// Add the rest of the list
itemViewModelList.addAll(chapterList.map { chapter ->
StoryChapterSummaryViewModel(explorationSelectionListener, chapter) as StoryItemViewModel
itemViewModelList.addAll(chapterList.mapIndexed { index, chapter ->
StoryChapterSummaryViewModel(index, explorationSelectionListener, chapter) as StoryItemViewModel
})

return itemViewModelList
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import org.oppia.app.story.StoryFragment

/** Chapter summary view model for the recycler view in [StoryFragment]. */
class StoryChapterSummaryViewModel(
val index: Int,
private val explorationSelectionListener: ExplorationSelectionListener,
chapterSummary: ChapterSummary
val chapterSummary: ChapterSummary
) : StoryItemViewModel() {
val id: String = chapterSummary.explorationId
val name: String = chapterSummary.name
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_check_primary.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#019489"
android:pathData="M9,16.17L5.53,12.7c-0.39,-0.39 -1.02,-0.39 -1.41,0 -0.39,0.39 -0.39,1.02 0,1.41l4.18,4.18c0.39,0.39 1.02,0.39 1.41,0L20.29,7.71c0.39,-0.39 0.39,-1.02 0,-1.41 -0.39,-0.39 -1.02,-0.39 -1.41,0L9,16.17z" />
</vector>
156 changes: 110 additions & 46 deletions app/src/main/res/layout/story_chapter_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,57 +4,121 @@

<data>

<import type="android.view.View" />

<import type="org.oppia.app.model.ChapterPlayState" />

<variable
name="viewModel"
type="org.oppia.app.story.storyitemviewmodel.StoryChapterSummaryViewModel" />
</data>

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="350dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:padding="16dp"
android:onClick="@{viewModel.onExplorationClicked}"
android:contentDescription="@{viewModel.name}"
app:roundedRectDrawableWithColor="@{viewModel.chapterThumbnail.backgroundColorRgb}">

<ImageView
android:id="@+id/chapter_thumbnail"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">

<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="180dp"
android:paddingStart="40dp"
android:paddingTop="40dp"
android:paddingEnd="40dp"
android:scaleType="centerCrop"
android:src="@{viewModel.chapterThumbnail.thumbnailGraphic}"
app:layout_constraintBottom_toTopOf="@id/chapter_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/chapter_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:fontFamily="sans-serif-medium"
android:text="@{viewModel.name}"
android:textColor="@color/black"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/chapter_thumbnail" />

<TextView
android:id="@+id/chapter_summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:fontFamily="sans-serif-medium"
android:text="@{viewModel.summary}"
android:textColor="@color/black"
android:textSize="12sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/chapter_title" />
</androidx.constraintlayout.widget.ConstraintLayout>
android:layout_margin="16dp"
android:clickable="@{viewModel.chapterSummary.chapterPlayState != ChapterPlayState.NOT_PLAYABLE_MISSING_PREREQUISITES}"
android:onClick="@{viewModel.onExplorationClicked}"
app:cardCornerRadius="4dp"
app:cardElevation="@{viewModel.chapterSummary.chapterPlayState != ChapterPlayState.NOT_PLAYABLE_MISSING_PREREQUISITES ? @dimen/elevation_4dp : @dimen/elevation_0dp}"
app:strokeColor="@{viewModel.chapterSummary.chapterPlayState != ChapterPlayState.NOT_PLAYABLE_MISSING_PREREQUISITES ? @color/chapterCardGreenBorder : @color/chapterCardGreyBorder}"
app:strokeWidth="2dp">

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="true"
android:contentDescription="@{viewModel.name}">

<ImageView
android:id="@+id/chapter_thumbnail"
android:layout_width="match_parent"
android:layout_height="140dp"
android:contentDescription="@{viewModel.name}"
android:paddingStart="40dp"
android:paddingTop="40dp"
android:paddingEnd="40dp"
android:scaleType="centerCrop"
android:src="@{viewModel.chapterThumbnail.thumbnailGraphic}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:roundedRectDrawableWithColor="@{viewModel.chapterThumbnail.backgroundColorRgb}" />

<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="@{viewModel.chapterSummary.chapterPlayState !=ChapterPlayState.NOT_PLAYABLE_MISSING_PREREQUISITES ? @color/chapterCardGreenBorder : @color/chapterCardGreyBorder}"
app:layout_constraintBottom_toBottomOf="@id/chapter_thumbnail"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="140dp"
android:orientation="vertical"
android:padding="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/chapter_thumbnail">

<TextView
android:id="@+id/chapter_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:fontFamily="sans-serif-medium"
android:text="@{String.format(@string/chapter_name, (viewModel.index + 1), viewModel.name)}"
android:textColor="@color/oppiaPrimaryText"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/chapter_summary"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:fontFamily="sans-serif"
android:text="@{viewModel.summary}"
android:textColor="@color/oppiaPrimaryText"
android:textSize="16sp"
app:layout_constraintBottom_toTopOf="@id/chapter_completed_tick"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/chapter_title" />

<ImageView
android:id="@+id/chapter_completed_tick"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginBottom="8dp"
android:contentDescription="@{String.format(@string/chapter_completed, (viewModel.index + 1), viewModel.name)}"
android:src="@drawable/ic_check_primary"
android:visibility="@{viewModel.chapterSummary.chapterPlayState == ChapterPlayState.COMPLETED ? View.VISIBLE : View.INVISIBLE}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>

<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp"
android:background="@color/chapterCardShadow"
android:padding="8dp"
android:visibility="@{viewModel.chapterSummary.chapterPlayState == ChapterPlayState.NOT_PLAYABLE_MISSING_PREREQUISITES ? View.VISIBLE : View.INVISIBLE}" />
</FrameLayout>
</layout>
48 changes: 41 additions & 7 deletions app/src/main/res/layout/story_fragment.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,46 @@
type="org.oppia.app.story.StoryViewModel" />
</data>

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/story_chapter_list"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:data="@{viewModel.storyChapterLiveData}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
android:background="@color/white"
android:layout_height="match_parent">

<com.google.android.material.appbar.AppBarLayout
android:id="@+id/topic_app_bar_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">

<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:fontFamily="sans-serif"
android:minHeight="?attr/actionBarSize"
android:textSize="20sp"
app:navigationContentDescription="@string/go_to_previous_page"
app:navigationIcon="?attr/homeAsUpIndicator"
app:title="@{viewModel.storyNameLiveData}"
app:titleTextColor="@color/white" />
</com.google.android.material.appbar.AppBarLayout>

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/story_chapter_list"
android:layout_width="0dp"
android:layout_height="0dp"
android:overScrollMode="never"
android:scrollbars="none"
android:paddingBottom="128dp"
android:clipToPadding="false"
app:data="@{viewModel.storyChapterLiveData}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/topic_app_bar_layout" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Loading

0 comments on commit 2be9940

Please sign in to comment.