-
Notifications
You must be signed in to change notification settings - Fork 544
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
… Classroom List Screen with Jetpack Compose (#5437) <!-- READ ME FIRST: Please fill in the explanation section below and check off every point from the Essential Checklist! --> ## Explanation <!-- - Explain what your PR does. If this PR fixes an existing bug, please include - "Fixes #bugnum:" in the explanation so that GitHub can auto-close the issue - when this PR is merged. --> Fixes part of #5344 Fixes part of #5422 Fixes part of #5012 This PR introduces the Classroom List Screen, which will replace the existing Home Screen. The new screen features a classroom carousel that remains sticky when the screen is scrolled. Various approaches were considered for implementing the sticky header, as detailed in [Decision 3: How to implement the sticky classroom carousel?](https://docs.google.com/document/d/1wGnoDS-8zHS5QSHufvTf9SXKi8N3lodirs51Xnehdvo/edit#heading=h.fv51u13soqp4). Ultimately, it was decided to use the `stickyHeader` component of Jetpack Compose. - **Introduce Jetpack Compose** - Sets the groundwork for migrating the codebase to Jetpack Compose. The Classroom List Screen uses a `ComposeView` to host the composable components. Appropriate tests have been set up to verify the functionality. - **Migrate away from `onActivityResult`** - Deprecates the use of `onActivityResult` and transitions to using `ActivityResultContracts` for handling activity results. - **Initiate Deprecation of Android KitKat** - The selected versions of Jetpack Compose dependencies require the `minSdkVersion` to be atleast 21. This PR initiates the process of deprecating support for Android 19 (KitKat). https://github.com/oppia/oppia-android/assets/84731134/2a2145e5-5bd0-4d80-9741-ff6baf20fd12 ## Screenshots ### Phone Light Mode |Potrait|Landscape| |--|--| ||| ||| ### Phone Dark Mode |Potrait|Landscape| |--|--| ||| ||| ### Tablet Light Mode |Potrait|Landscape| |--|--| ||| ||| ### Tablet Dark Mode |Potrait|Landscape| |--|--| ||| ||| ## Essential Checklist <!-- Please tick the relevant boxes by putting an "x" in them. --> - [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".) - [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation. - [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide). - [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)). - [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop". - [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)). ## For UI-specific PRs only <!-- Delete these section if this PR does not include UI-related changes. --> If your PR includes UI-related changes, then: - Add screenshots for portrait/landscape for both a tablet & phone of the before & after UI changes - For the screenshots above, include both English and pseudo-localized (RTL) screenshots (see [RTL guide](https://github.com/oppia/oppia-android/wiki/RTL-Guidelines)) - Add a video showing the full UX flow with a screen reader enabled (see [accessibility guide](https://github.com/oppia/oppia-android/wiki/Accessibility-A11y-Guide)) - For PRs introducing new UI elements or color changes, both light and dark mode screenshots must be included - Add a screenshot demonstrating that you ran affected Espresso tests locally & that they're passing --------- Co-authored-by: Adhiambo Peres <[email protected]>
- Loading branch information
1 parent
f53c7e5
commit 103f5a8
Showing
69 changed files
with
4,218 additions
and
225 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
115 changes: 115 additions & 0 deletions
115
app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivity.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
package org.oppia.android.app.classroom | ||
|
||
import android.content.Context | ||
import android.content.Intent | ||
import android.os.Bundle | ||
import org.oppia.android.R | ||
import org.oppia.android.app.activity.ActivityComponentImpl | ||
import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity | ||
import org.oppia.android.app.activity.route.ActivityRouter | ||
import org.oppia.android.app.drawer.ExitProfileDialogFragment | ||
import org.oppia.android.app.drawer.TAG_SWITCH_PROFILE_DIALOG | ||
import org.oppia.android.app.home.RouteToRecentlyPlayedListener | ||
import org.oppia.android.app.home.RouteToTopicListener | ||
import org.oppia.android.app.home.RouteToTopicPlayStoryListener | ||
import org.oppia.android.app.model.DestinationScreen | ||
import org.oppia.android.app.model.ExitProfileDialogArguments | ||
import org.oppia.android.app.model.HighlightItem | ||
import org.oppia.android.app.model.ProfileId | ||
import org.oppia.android.app.model.RecentlyPlayedActivityParams | ||
import org.oppia.android.app.model.RecentlyPlayedActivityTitle | ||
import org.oppia.android.app.model.ScreenName.CLASSROOM_LIST_ACTIVITY | ||
import org.oppia.android.app.topic.TopicActivity | ||
import org.oppia.android.app.translation.AppLanguageResourceHandler | ||
import org.oppia.android.util.logging.CurrentAppScreenNameIntentDecorator.decorateWithScreenName | ||
import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId | ||
import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId | ||
import javax.inject.Inject | ||
|
||
/** The activity for displaying [ClassroomListFragment]. */ | ||
class ClassroomListActivity : | ||
InjectableAutoLocalizedAppCompatActivity(), | ||
RouteToTopicListener, | ||
RouteToTopicPlayStoryListener, | ||
RouteToRecentlyPlayedListener { | ||
@Inject | ||
lateinit var classroomListActivityPresenter: ClassroomListActivityPresenter | ||
|
||
@Inject | ||
lateinit var resourceHandler: AppLanguageResourceHandler | ||
|
||
@Inject | ||
lateinit var activityRouter: ActivityRouter | ||
|
||
private var internalProfileId: Int = -1 | ||
|
||
companion object { | ||
/** Returns a new [Intent] to route to [ClassroomListActivity] for a specified [profileId]. */ | ||
fun createClassroomListActivity(context: Context, profileId: ProfileId?): Intent { | ||
return Intent(context, ClassroomListActivity::class.java).apply { | ||
decorateWithScreenName(CLASSROOM_LIST_ACTIVITY) | ||
profileId?.let { decorateWithUserProfileId(profileId) } | ||
} | ||
} | ||
} | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
(activityComponent as ActivityComponentImpl).inject(this) | ||
|
||
internalProfileId = intent.extractCurrentUserProfileId().internalId | ||
classroomListActivityPresenter.handleOnCreate() | ||
title = resourceHandler.getStringInLocale(R.string.classroom_list_activity_title) | ||
} | ||
|
||
override fun onRestart() { | ||
super.onRestart() | ||
classroomListActivityPresenter.handleOnRestart() | ||
} | ||
|
||
override fun onBackPressed() { | ||
val previousFragment = | ||
supportFragmentManager.findFragmentByTag(TAG_SWITCH_PROFILE_DIALOG) | ||
if (previousFragment != null) { | ||
supportFragmentManager.beginTransaction().remove(previousFragment).commitNow() | ||
} | ||
val exitProfileDialogArguments = | ||
ExitProfileDialogArguments | ||
.newBuilder() | ||
.setHighlightItem(HighlightItem.NONE) | ||
.build() | ||
val dialogFragment = ExitProfileDialogFragment | ||
.newInstance(exitProfileDialogArguments = exitProfileDialogArguments) | ||
dialogFragment.showNow(supportFragmentManager, TAG_SWITCH_PROFILE_DIALOG) | ||
} | ||
|
||
override fun routeToRecentlyPlayed(recentlyPlayedActivityTitle: RecentlyPlayedActivityTitle) { | ||
val recentlyPlayedActivityParams = | ||
RecentlyPlayedActivityParams | ||
.newBuilder() | ||
.setProfileId(ProfileId.newBuilder().setInternalId(internalProfileId).build()) | ||
.setActivityTitle(recentlyPlayedActivityTitle).build() | ||
|
||
activityRouter.routeToScreen( | ||
DestinationScreen | ||
.newBuilder() | ||
.setRecentlyPlayedActivityParams(recentlyPlayedActivityParams) | ||
.build() | ||
) | ||
} | ||
|
||
override fun routeToTopic(internalProfileId: Int, topicId: String) { | ||
startActivity(TopicActivity.createTopicActivityIntent(this, internalProfileId, topicId)) | ||
} | ||
|
||
override fun routeToTopicPlayStory(internalProfileId: Int, topicId: String, storyId: String) { | ||
startActivity( | ||
TopicActivity.createTopicPlayStoryActivityIntent( | ||
this, | ||
internalProfileId, | ||
topicId, | ||
storyId | ||
) | ||
) | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package org.oppia.android.app.classroom | ||
|
||
import android.view.View | ||
import androidx.appcompat.app.AppCompatActivity | ||
import androidx.appcompat.widget.Toolbar | ||
import androidx.drawerlayout.widget.DrawerLayout | ||
import org.oppia.android.R | ||
import org.oppia.android.app.drawer.NavigationDrawerFragment | ||
import javax.inject.Inject | ||
|
||
/** Tag for identifying the [ClassroomListFragment] in transactions. */ | ||
private const val TAG_CLASSROOM_LIST_FRAGMENT = "CLASSROOM_LIST_FRAGMENT" | ||
|
||
/** The presenter for [ClassroomListActivity]. */ | ||
class ClassroomListActivityPresenter @Inject constructor(private val activity: AppCompatActivity) { | ||
private var navigationDrawerFragment: NavigationDrawerFragment? = null | ||
|
||
/** | ||
* Handles the creation of the activity. Sets the content view, sets up the navigation drawer, | ||
* and adds the [ClassroomListFragment] if it's not already added. | ||
*/ | ||
fun handleOnCreate() { | ||
activity.setContentView(R.layout.classroom_list_activity) | ||
setUpNavigationDrawer() | ||
if (getClassroomListFragment() == null) { | ||
activity.supportFragmentManager.beginTransaction().add( | ||
R.id.classroom_list_fragment_placeholder, | ||
ClassroomListFragment(), | ||
TAG_CLASSROOM_LIST_FRAGMENT | ||
).commitNow() | ||
} | ||
} | ||
|
||
/** Handles the activity restart. Re-initializes the navigation drawer. */ | ||
fun handleOnRestart() { | ||
setUpNavigationDrawer() | ||
} | ||
|
||
private fun setUpNavigationDrawer() { | ||
val toolbar = activity.findViewById<View>(R.id.classroom_list_activity_toolbar) as Toolbar | ||
activity.setSupportActionBar(toolbar) | ||
activity.supportActionBar!!.setDisplayShowHomeEnabled(true) | ||
navigationDrawerFragment = activity | ||
.supportFragmentManager | ||
.findFragmentById( | ||
R.id.classroom_list_activity_fragment_navigation_drawer | ||
) as NavigationDrawerFragment | ||
navigationDrawerFragment!!.setUpDrawer( | ||
activity.findViewById<View>(R.id.classroom_list_activity_drawer_layout) as DrawerLayout, | ||
toolbar, R.id.nav_home | ||
) | ||
} | ||
|
||
private fun getClassroomListFragment(): ClassroomListFragment? { | ||
return activity.supportFragmentManager.findFragmentById( | ||
R.id.classroom_list_fragment_placeholder | ||
) as ClassroomListFragment? | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
app/src/main/java/org/oppia/android/app/classroom/ClassroomListFragment.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package org.oppia.android.app.classroom | ||
|
||
import android.content.Context | ||
import android.os.Bundle | ||
import android.view.LayoutInflater | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import org.oppia.android.app.fragment.FragmentComponentImpl | ||
import org.oppia.android.app.fragment.InjectableFragment | ||
import org.oppia.android.app.home.classroomlist.ClassroomSummaryClickListener | ||
import org.oppia.android.app.home.topiclist.TopicSummaryClickListener | ||
import org.oppia.android.app.model.ClassroomSummary | ||
import org.oppia.android.app.model.TopicSummary | ||
import javax.inject.Inject | ||
|
||
/** Fragment that displays the classroom list screen. */ | ||
class ClassroomListFragment : | ||
InjectableFragment(), | ||
TopicSummaryClickListener, | ||
ClassroomSummaryClickListener { | ||
@Inject | ||
lateinit var classroomListFragmentPresenter: ClassroomListFragmentPresenter | ||
|
||
override fun onAttach(context: Context) { | ||
super.onAttach(context) | ||
(fragmentComponent as FragmentComponentImpl).inject(this) | ||
} | ||
|
||
override fun onCreateView( | ||
inflater: LayoutInflater, | ||
container: ViewGroup?, | ||
savedInstanceState: Bundle? | ||
): View? { | ||
return classroomListFragmentPresenter.handleCreateView(inflater, container) | ||
} | ||
|
||
override fun onTopicSummaryClicked(topicSummary: TopicSummary) { | ||
classroomListFragmentPresenter.onTopicSummaryClicked(topicSummary) | ||
} | ||
|
||
override fun onClassroomSummaryClicked(classroomSummary: ClassroomSummary) { | ||
classroomListFragmentPresenter.onClassroomSummaryClicked(classroomSummary) | ||
} | ||
} |
Oops, something went wrong.