diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index dcb6b8c4cc2..00000000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/WORKSPACE b/WORKSPACE
index 157e4c48167..b7e0ad81dd8 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -104,6 +104,17 @@ git_repository(
load("@tools_android//tools/googleservices:defs.bzl", "google_services_workspace_dependencies")
google_services_workspace_dependencies()
+git_repository(
+ name = "circularimageview",
+ commit = "6098dec76713b34eb8b10883cbe54189ddc08566",
+ remote = "https://github.com/oppia/CircularImageview",
+)
+
+bind(
+ name = "databinding_annotation_processor",
+ actual = "//tools/android:compiler_annotation_processor",
+)
+
load("@rules_jvm_external//:defs.bzl", "maven_install")
maven_install(
@@ -112,27 +123,47 @@ maven_install(
"androidx.annotation:annotation:1.1.0",
"androidx.appcompat:appcompat:1.0.2",
"androidx.arch.core:core-testing:2.1.0",
+ "androidx.constraintlayout:constraintlayout:1.1.3",
"androidx.core:core-ktx:1.0.1",
- "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha03",
+ "androidx.core:core:1.0.1",
+ "androidx.databinding:databinding-adapters:3.4.2",
+ "androidx.databinding:databinding-common:3.4.2",
+ "androidx.databinding:databinding-runtime:3.4.2",
+ "androidx.lifecycle:lifecycle-extensions:2.2.0",
+ "androidx.lifecycle:lifecycle-livedata-core:2.2.0",
+ "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0",
+ "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0",
+ "androidx.multidex:multidex-instrumentation:2.0.0",
+ "androidx.multidex:multidex:2.0.1",
+ "androidx.recyclerview:recyclerview:1.0.0",
"androidx.test.espresso:espresso-contrib:3.1.0",
"androidx.test.espresso:espresso-core:3.2.0",
"androidx.test.espresso:espresso-intents:3.1.0",
"androidx.test.ext:junit:1.1.1",
"androidx.test:runner:1.2.0",
+ "androidx.viewpager:viewpager:1.0.0",
"com.android.support:support-annotations:28.0.0",
"com.caverock:androidsvg-aar:1.4",
+ "com.chaos.view:pinview:1.4.3",
"com.crashlytics.sdk.android:crashlytics:2.9.8",
"com.github.bumptech.glide:glide:4.11.0",
+ "com.google.android.material:material:1.2.0-alpha02",
"com.google.firebase:firebase-analytics:17.4.4",
"com.google.firebase:firebase-crashlytics:17.1.1",
+ "com.google.gms:google-services:4.3.3",
"com.google.truth:truth:0.43",
+ "com.squareup.retrofit2:converter-gson:2.5.0",
+ "com.squareup.retrofit2:retrofit:2.9.0",
+ "de.hdodenhof:circleimageview:3.0.1",
"io.fabric.sdk.android:fabric:1.4.7",
+ "javax.annotation:javax.annotation-api:jar:1.3.2",
"junit:junit:4.12",
"org.jetbrains.kotlin:kotlin-reflect:1.3.41",
"org.jetbrains.kotlin:kotlin-stdlib-jdk7:jar:1.3.72",
"org.jetbrains.kotlin:kotlin-test-junit:1.3.72",
"org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2",
"org.jetbrains.kotlinx:kotlinx-coroutines-test:1.2.2",
+ "org.jetbrains:annotations:jar:13.0",
"org.mockito:mockito-core:2.19.0",
"org.robolectric:annotations:4.3",
"org.robolectric:robolectric:4.3",
diff --git a/app/BUILD.bazel b/app/BUILD.bazel
index f1e3fcf6d51..5744f21ce76 100644
--- a/app/BUILD.bazel
+++ b/app/BUILD.bazel
@@ -1,17 +1,345 @@
# TODO(#1532): Rename file to 'BUILD' post-Gradle.
'''
-Package for all Firebase dependencies.
-To reference these dependencies, add '//app:crashlytics' and '//app:crashlytics_deps'
-to your build rule's dependency list.
+This library contains the app's core source files and functionality
'''
load("@rules_jvm_external//:defs.bzl", "artifact")
load("@dagger//:workspace_defs.bzl", "dagger_rules")
load("@tools_android//tools/crashlytics:defs.bzl", "crashlytics_android_library")
load("@tools_android//tools/googleservices:defs.bzl", "google_services_xml")
+load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_android_library")
+
+# Source files for annotations library.
+ANNOTATION_FILES = [
+ "src/main/java/org/oppia/app/activity/ActivityScope.kt",
+ "src/main/java/org/oppia/app/fragment/FragmentScope.kt",
+ "src/main/java/org/oppia/app/utility/KeyboardHelper.kt",
+]
+
+# Source files for listener library.
+LISTENER_FILES = [
+ "src/main/java/org/oppia/app/administratorcontrols/RouteToAppVersionListener.kt",
+ "src/main/java/org/oppia/app/administratorcontrols/RouteToProfileListListener.kt",
+ "src/main/java/org/oppia/app/drawer/RouteToProfileProgressListener.kt",
+ "src/main/java/org/oppia/app/help/faq/RouteToFAQSingleListener.kt",
+ "src/main/java/org/oppia/app/help/RouteToFAQListListener.kt",
+ "src/main/java/org/oppia/app/home/recentlyplayed/OngoingStoryClickListener.kt",
+ "src/main/java/org/oppia/app/home/RouteToRecentlyPlayedListener.kt",
+ "src/main/java/org/oppia/app/home/RouteToTopicPlayStoryListener.kt",
+ "src/main/java/org/oppia/app/home/topiclist/TopicSummaryClickListener.kt",
+ "src/main/java/org/oppia/app/onboarding/RouteToProfileListListener.kt",
+ "src/main/java/org/oppia/app/options/RouteToAppLanguageListListener.kt",
+ "src/main/java/org/oppia/app/options/RouteToAudioLanguageListListener.kt",
+ "src/main/java/org/oppia/app/options/RouteToStoryTextSizeListener.kt",
+ "src/main/java/org/oppia/app/player/audio/LanguageInterface.kt",
+ "src/main/java/org/oppia/app/player/state/answerhandling/InteractionAnswerErrorOrAvailabilityCheckReceiver.kt",
+ "src/main/java/org/oppia/app/player/state/answerhandling/InteractionAnswerHandler.kt",
+ "src/main/java/org/oppia/app/player/state/listener/ContinueNavigationButtonListener.kt",
+ "src/main/java/org/oppia/app/player/state/listener/NextNavigationButtonListener.kt",
+ "src/main/java/org/oppia/app/player/state/listener/PreviousNavigationButtonListener.kt",
+ "src/main/java/org/oppia/app/player/state/listener/PreviousResponsesHeaderClickListener.kt",
+ "src/main/java/org/oppia/app/player/state/listener/ReplayButtonListener.kt",
+ "src/main/java/org/oppia/app/player/state/listener/ReturnToTopicNavigationButtonListener.kt",
+ "src/main/java/org/oppia/app/player/state/listener/RouteToHintsAndSolutionListener.kt",
+ "src/main/java/org/oppia/app/player/state/listener/StateKeyboardButtonListener.kt",
+ "src/main/java/org/oppia/app/player/state/listener/SubmitNavigationButtonListener.kt",
+ "src/main/java/org/oppia/app/profile/RouteToAdminPinListener.kt",
+ "src/main/java/org/oppia/app/profileprogress/ProfilePictureClickListener.kt",
+ "src/main/java/org/oppia/app/profileprogress/RouteToCompletedStoryListListener.kt",
+ "src/main/java/org/oppia/app/profileprogress/RouteToOngoingTopicListListener.kt",
+ "src/main/java/org/oppia/app/recyclerview/OnDragEndedListener.kt",
+ "src/main/java/org/oppia/app/recyclerview/OnItemDragListener.kt",
+ "src/main/java/org/oppia/app/story/ExplorationSelectionListener.kt",
+ "src/main/java/org/oppia/app/topic/lessons/StorySummarySelector.kt",
+ "src/main/java/org/oppia/app/topic/revision/RevisionSubtopicSelector.kt",
+ "src/main/java/org/oppia/app/topic/revisioncard/ReturnToTopicClickListener.kt",
+ "src/main/java/org/oppia/app/topic/RouteToRevisionCardListener.kt",
+ "src/main/java/org/oppia/app/utility/OnClickableAreaClickedListener.kt",
+ "src/main/java/org/oppia/app/utility/RegionClickEvent.kt",
+]
+
+# TODO(#1617): Remove genrules post-gradle
+# Files altered by genrules to be excluded in the app library
+ALTERED_VIEW_MODELS = [
+ "src/main/java/org/oppia/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsAccountActionsViewModel.kt",
+ "src/main/java/org/oppia/app/help/faq/FAQListViewModel.kt",
+ "src/main/java/org/oppia/app/help/HelpItemViewModel.kt",
+ "src/main/java/org/oppia/app/help/HelpListViewModel.kt",
+ "src/main/java/org/oppia/app/onboarding/OnboadingSlideViewModel.kt",
+ "src/main/java/org/oppia/app/onboarding/OnboardingViewModel.kt",
+ "src/main/java/org/oppia/app/parser/StringToFractionParser.kt",
+ "src/main/java/org/oppia/app/parser/StringToNumberParser.kt",
+ "src/main/java/org/oppia/app/player/state/itemviewmodel/FractionInteractionViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/itemviewmodel/ImageRegionSelectionInteractionViewModel.kt",
+ "src/main/java/org/oppia/app/topic/info/TopicInfoViewModel.kt",
+]
+
+# Source files for view_model library
+VIEW_MODEL_FILES = [
+ "src/main/java/org/oppia/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsAppInformationViewModel.kt",
+ "src/main/java/org/oppia/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsDownloadPermissionsViewModel.kt",
+ "src/main/java/org/oppia/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsGeneralViewModel.kt",
+ "src/main/java/org/oppia/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsItemViewModel.kt",
+ "src/main/java/org/oppia/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsProfileViewModel.kt",
+ "src/main/java/org/oppia/app/administratorcontrols/AdministratorControlsViewModel.kt",
+ "src/main/java/org/oppia/app/administratorcontrols/appversion/AppVersionViewModel.kt",
+ "src/main/java/org/oppia/app/completedstorylist/CompletedStoryItemViewModel.kt",
+ "src/main/java/org/oppia/app/completedstorylist/CompletedStoryListViewModel.kt",
+ "src/main/java/org/oppia/app/drawer/NavigationDrawerFooterViewModel.kt",
+ "src/main/java/org/oppia/app/drawer/NavigationDrawerHeaderViewModel.kt",
+ "src/main/java/org/oppia/app/help/faq/faqItemViewModel/FAQContentViewModel.kt",
+ "src/main/java/org/oppia/app/help/faq/faqItemViewModel/FAQHeaderViewModel.kt",
+ "src/main/java/org/oppia/app/help/faq/faqItemViewModel/FAQItemViewModel.kt",
+ "src/main/java/org/oppia/app/help/HelpItems.kt",
+ "src/main/java/org/oppia/app/hintsandsolution/HintsAndSolutionItemViewModel.kt",
+ "src/main/java/org/oppia/app/hintsandsolution/HintsViewModel.kt",
+ "src/main/java/org/oppia/app/hintsandsolution/SolutionViewModel.kt",
+ "src/main/java/org/oppia/app/home/HomeItemViewModel.kt",
+ "src/main/java/org/oppia/app/home/recentlyplayed/OngoingStoryViewModel.kt",
+ "src/main/java/org/oppia/app/home/recentlyplayed/RecentlyPlayedItemViewModel.kt",
+ "src/main/java/org/oppia/app/home/recentlyplayed/SectionTitleViewModel.kt",
+ "src/main/java/org/oppia/app/home/topiclist/AllTopicsViewModel.kt",
+ "src/main/java/org/oppia/app/home/topiclist/PromotedStoryListViewModel.kt",
+ "src/main/java/org/oppia/app/home/topiclist/PromotedStoryViewModel.kt",
+ "src/main/java/org/oppia/app/home/topiclist/TopicSummaryViewModel.kt",
+ "src/main/java/org/oppia/app/home/UserAppHistoryViewModel.kt",
+ "src/main/java/org/oppia/app/home/WelcomeViewModel.kt",
+ "src/main/java/org/oppia/app/onboarding/OnboardingSlideFinalViewModel.kt",
+ "src/main/java/org/oppia/app/onboarding/ViewPagerSlide.kt",
+ "src/main/java/org/oppia/app/ongoingtopiclist/OngoingTopicItemViewModel.kt",
+ "src/main/java/org/oppia/app/ongoingtopiclist/OngoingTopicListViewModel.kt",
+ "src/main/java/org/oppia/app/options/OptionControlsViewModel.kt",
+ "src/main/java/org/oppia/app/options/OptionsAppLanguageViewModel.kt",
+ "src/main/java/org/oppia/app/options/OptionsAudioLanguageViewModel.kt",
+ "src/main/java/org/oppia/app/options/OptionsItemViewModel.kt",
+ "src/main/java/org/oppia/app/options/OptionsStoryTextSizeViewModel.kt",
+ "src/main/java/org/oppia/app/player/audio/AudioViewModel.kt",
+ "src/main/java/org/oppia/app/player/exploration/ExplorationViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/itemviewmodel/ContentViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/itemviewmodel/ContinueInteractionViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/itemviewmodel/ContinueNavigationButtonViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/itemviewmodel/DragDropInteractionContentViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/itemviewmodel/FeedbackViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/itemviewmodel/NextButtonViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/itemviewmodel/NumericInputViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/itemviewmodel/PreviousButtonViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/itemviewmodel/PreviousResponsesHeaderViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/itemviewmodel/ReplayButtonViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/itemviewmodel/ReturnToTopicButtonViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/itemviewmodel/SelectionInteractionContentViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/itemviewmodel/StateItemViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/itemviewmodel/SubmitButtonViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/itemviewmodel/SubmittedAnswerViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/itemviewmodel/TextInputViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/StateViewModel.kt",
+ "src/main/java/org/oppia/app/player/state/testing/StateFragmentTestViewModel.kt",
+ "src/main/java/org/oppia/app/profile/AddProfileViewModel.kt",
+ "src/main/java/org/oppia/app/profile/AdminAuthViewModel.kt",
+ "src/main/java/org/oppia/app/profile/AdminPinViewModel.kt",
+ "src/main/java/org/oppia/app/profile/AdminSettingsViewModel.kt",
+ "src/main/java/org/oppia/app/profile/PinPasswordViewModel.kt",
+ "src/main/java/org/oppia/app/profile/ProfileChooserViewModel.kt",
+ "src/main/java/org/oppia/app/profile/ResetPinViewModel.kt",
+ "src/main/java/org/oppia/app/profileprogress/ProfilePictureActivityViewModel.kt",
+ "src/main/java/org/oppia/app/profileprogress/ProfileProgressHeaderViewModel.kt",
+ "src/main/java/org/oppia/app/profileprogress/ProfileProgressItemViewModel.kt",
+ "src/main/java/org/oppia/app/profileprogress/ProfileProgressViewModel.kt",
+ "src/main/java/org/oppia/app/profileprogress/RecentlyPlayedStorySummaryViewModel.kt",
+ "src/main/java/org/oppia/app/recyclerview/BindableAdapter.kt",
+ "src/main/java/org/oppia/app/recyclerview/DividerItemDecorator.kt",
+ "src/main/java/org/oppia/app/recyclerview/DragAndDropItemFacilitator.kt",
+ "src/main/java/org/oppia/app/settings/profile/ProfileEditViewModel.kt",
+ "src/main/java/org/oppia/app/settings/profile/ProfileListViewModel.kt",
+ "src/main/java/org/oppia/app/settings/profile/ProfileRenameViewModel.kt",
+ "src/main/java/org/oppia/app/settings/profile/ProfileResetPinViewModel.kt",
+ "src/main/java/org/oppia/app/shim/IntentFactoryShim.kt",
+ "src/main/java/org/oppia/app/story/StoryFragmentScroller.kt",
+ "src/main/java/org/oppia/app/story/storyitemviewmodel/StoryChapterSummaryViewModel.kt",
+ "src/main/java/org/oppia/app/story/storyitemviewmodel/StoryHeaderViewModel.kt",
+ "src/main/java/org/oppia/app/story/storyitemviewmodel/StoryItemViewModel.kt",
+ "src/main/java/org/oppia/app/story/StoryViewModel.kt",
+ "src/main/java/org/oppia/app/testing/BindableAdapterTestViewModel.kt",
+ "src/main/java/org/oppia/app/topic/conceptcard/ConceptCardViewModel.kt",
+ "src/main/java/org/oppia/app/topic/lessons/StorySummaryViewModel.kt",
+ "src/main/java/org/oppia/app/topic/lessons/TopicLessonsItemViewModel.kt",
+ "src/main/java/org/oppia/app/topic/lessons/TopicLessonsTitleViewModel.kt",
+ "src/main/java/org/oppia/app/topic/practice/practiceitemviewmodel/TopicPracticeFooterViewModel.kt",
+ "src/main/java/org/oppia/app/topic/practice/practiceitemviewmodel/TopicPracticeHeaderViewModel.kt",
+ "src/main/java/org/oppia/app/topic/practice/practiceitemviewmodel/TopicPracticeItemViewModel.kt",
+ "src/main/java/org/oppia/app/topic/practice/practiceitemviewmodel/TopicPracticeSubtopicViewModel.kt",
+ "src/main/java/org/oppia/app/topic/practice/TopicPracticeViewModel.kt",
+ "src/main/java/org/oppia/app/topic/questionplayer/QuestionPlayerViewModel.kt",
+ "src/main/java/org/oppia/app/topic/revision/revisionitemviewmodel/TopicRevisionItemViewModel.kt",
+ "src/main/java/org/oppia/app/topic/revision/TopicRevisionViewModel.kt",
+ "src/main/java/org/oppia/app/topic/revisioncard/RevisionCardViewModel.kt",
+ "src/main/java/org/oppia/app/topic/TopicViewModel.kt",
+ "src/main/java/org/oppia/app/viewmodel/ObservableArrayList.kt",
+ "src/main/java/org/oppia/app/viewmodel/ObservableViewModel.kt",
+ "src/main/java/org/oppia/app/walkthrough/end/WalkthroughFinalViewModel.kt",
+ "src/main/java/org/oppia/app/walkthrough/topiclist/topiclistviewmodel/WalkthroughTopicHeaderViewModel.kt",
+ "src/main/java/org/oppia/app/walkthrough/topiclist/topiclistviewmodel/WalkthroughTopicSummaryViewModel.kt",
+ "src/main/java/org/oppia/app/walkthrough/topiclist/WalkthroughTopicItemViewModel.kt",
+ "src/main/java/org/oppia/app/walkthrough/topiclist/WalkthroughTopicViewModel.kt",
+ "src/main/java/org/oppia/app/walkthrough/WalkthroughViewModel.kt",
+ "src/main/java/org/oppia/app/walkthrough/welcome/WalkthroughWelcomeViewModel.kt",
+] + [
+ "update_" + altered_view_models[0:-3]
+ for altered_view_models in ALTERED_VIEW_MODELS
+]
+
+# TODO(#1617): Remove genrules post-gradle
+'''
+Genrule for source files in the view_models library.
+
+Because each databinding library must have a unique package name and manifest, resources must be
+imported using the proper package name when building with Bazel. This genrule alters those imports
+in order to keep Gradle building.
+'''
+[genrule(
+ name = "update_" + file[0:-3],
+ srcs = [file],
+ outs = [file[0:-3] + "_updated.kt"],
+ cmd = '''
+ cat $(SRCS) |
+ sed 's/import org.oppia.app.R/import org.oppia.app.vm.R/g' > $(OUTS)
+ ''',
+)
+for file in ALTERED_VIEW_MODELS]
+
+# TODO(#1617): Remove genrules post-gradle
+# View files altered by genrules to be excluded in the app library.
+ALTERED_VIEWS = [
+ "src/main/java/org/oppia/app/customview/LessonThumbnailImageView.kt",
+ "src/main/java/org/oppia/app/customview/SegmentedCircularProgressView.kt",
+ "src/main/java/org/oppia/app/profile/ProfileInputView.kt",
+ "src/main/java/org/oppia/app/utility/ClickableAreasImage.kt",
+]
+
+# Source files and genrule targets for views library.
+VIEW_FILES = [
+ "src/main/java/org/oppia/app/customview/interaction/FractionInputInteractionView.kt",
+ "src/main/java/org/oppia/app/customview/interaction/NumericInputInteractionView.kt",
+ "src/main/java/org/oppia/app/customview/interaction/TextInputInteractionView.kt",
+ "src/main/java/org/oppia/app/player/state/DragDropSortInteractionView.kt",
+ "src/main/java/org/oppia/app/player/state/ImageRegionSelectionInteractionView.kt",
+ "src/main/java/org/oppia/app/player/state/SelectionInteractionView.kt",
+ "src/main/java/org/oppia/app/shim/ViewBindingShim.kt",
+ "src/main/java/org/oppia/app/shim/ViewComponentFactory.kt",
+ "src/main/java/org/oppia/app/view/ViewComponent.kt",
+ "src/main/java/org/oppia/app/view/ViewScope.kt",
+] + [
+ "update_" + altered_views[0:-3]
+ for altered_views in ALTERED_VIEWS
+]
+
+# TODO(#1617): Remove genrules post-gradle
+'''
+Genrule for source files in the views library.
+
+Because each databinding library must have a unique package name and manifest, resources must be
+imported using the proper package name when building with Bazel. This genrule alters those imports
+in order to keep Gradle building.
+'''
+[genrule(
+ name = "update_" + file[0:-3],
+ srcs = [file],
+ outs = [file[0:-3] + "_updated.kt"],
+ cmd = '''
+ cat $(SRCS) |
+ sed 's/import org.oppia.app.R/import org.oppia.app.views.R/g' > $(OUTS)
+ ''',
+ )
+for file in ALTERED_VIEWS]
+
+# Library for non-layout resource files
+android_library(
+ name = "resources",
+ custom_package = "org.oppia.app",
+ manifest = "src/main/AndroidManifest.xml",
+ resource_files = glob(["src/main/res/**",], exclude = ["src/main/res/layout*/**"]),
+ exports_manifest = True,
+ deps = [
+ artifact("com.google.android.material:material"),
+ ],
+ visibility = ["//visibility:private"],
+)
+
+# Library for listener files required to build views and view_model libraries
+kt_android_library(
+ name = "listeners",
+ custom_package = "org.oppia.app",
+ srcs = LISTENER_FILES,
+ deps = [
+ ":dagger",
+ "//model",
+ artifact("androidx.recyclerview:recyclerview:1.0.0"),
+ ],
+ visibility = ["//visibility:private"],
+)
+
+# Library for all view files required to build layout files
+kt_android_library(
+ name = "views",
+ custom_package = "org.oppia.app.views",
+ manifest = "src/main/ViewsManifest.xml",
+ srcs = VIEW_FILES,
+ deps = [
+ ":annotations",
+ ":listeners",
+ ":resources",
+ ":view_models",
+ "//model",
+ "@circularimageview//circularimageview:circular_image_view",
+ artifact("androidx.appcompat:appcompat"),
+ artifact("androidx.core:core-ktx"),
+ artifact("androidx.databinding:databinding-common"),
+ artifact("androidx.databinding:databinding-runtime"),
+ ],
+ visibility = ["//visibility:public"],
+)
+
+# Library for scope annotations required to build views and view_model libraries
+kt_android_library(
+ name = "annotations",
+ custom_package = "org.oppia.app",
+ srcs = ANNOTATION_FILES,
+ deps = [
+ ":dagger",
+ "//model",
+ ],
+ visibility = ["//visibility:private"],
+)
+# Library for all view model files
+kt_android_library(
+ name = "view_models",
+ custom_package = "org.oppia.app.vm",
+ srcs = VIEW_MODEL_FILES,
+ enable_data_binding = 1,
+ manifest = "src/main/ViewModelManifest.xml",
+ deps = [
+ ":annotations",
+ ":dagger",
+ ":listeners",
+ ":resources",
+ "//domain",
+ "//model",
+ "//utility",
+ artifact("androidx.databinding:databinding-common"),
+ artifact("androidx.databinding:databinding-runtime"),
+ ],
+ visibility = ["//visibility:private"],
+)
+
+# TODO(#1566): Move Firebase rules to their own package & remove default visibility
+'''
+Package for all Firebase dependencies.
+To reference these dependencies, add '//app:crashlytics' and '//app:crashlytics_deps'
+to your build rule's dependency list.
+'''
package(default_visibility = ["//visibility:public"])
-# TODO(#1566): Move Firebase rules to their own package
GOOGLE_SERVICES_RESOURCES = google_services_xml(
package_name = "org.oppia.app",
google_services_json = "google-services.json",
@@ -33,3 +361,5 @@ android_library(
artifact("com.google.firebase:firebase-crashlytics"),
],
)
+
+dagger_rules()
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6ddfa2bbb70..0a09ea7f726 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,6 +4,8 @@
+
+
+
+
+
diff --git a/app/src/main/ViewsManifest.xml b/app/src/main/ViewsManifest.xml
new file mode 100644
index 00000000000..0b89c5bf0fd
--- /dev/null
+++ b/app/src/main/ViewsManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/app/src/main/java/org/oppia/app/administratorcontrols/AdministratorControlsViewModel.kt b/app/src/main/java/org/oppia/app/administratorcontrols/AdministratorControlsViewModel.kt
index 659927f2e26..ef3862ab3c8 100644
--- a/app/src/main/java/org/oppia/app/administratorcontrols/AdministratorControlsViewModel.kt
+++ b/app/src/main/java/org/oppia/app/administratorcontrols/AdministratorControlsViewModel.kt
@@ -14,6 +14,7 @@ import org.oppia.app.administratorcontrols.administratorcontrolsitemviewmodel.Ad
import org.oppia.app.fragment.FragmentScope
import org.oppia.app.model.DeviceSettings
import org.oppia.app.model.ProfileId
+import org.oppia.app.shim.IntentFactoryShim
import org.oppia.app.viewmodel.ObservableViewModel
import org.oppia.domain.profile.ProfileManagementController
import org.oppia.util.data.AsyncResult
@@ -26,7 +27,8 @@ class AdministratorControlsViewModel @Inject constructor(
private val activity: AppCompatActivity,
private val fragment: Fragment,
private val logger: ConsoleLogger,
- private val profileManagementController: ProfileManagementController
+ private val profileManagementController: ProfileManagementController,
+ private val IntentFactoryShim: IntentFactoryShim
) : ObservableViewModel() {
private val routeToProfileListListener = activity as RouteToProfileListListener
private lateinit var userProfileId: ProfileId
@@ -72,7 +74,12 @@ class AdministratorControlsViewModel @Inject constructor(
)
)
itemViewModelList.add(AdministratorControlsAppInformationViewModel(activity))
- itemViewModelList.add(AdministratorControlsAccountActionsViewModel(fragment))
+ itemViewModelList.add(
+ AdministratorControlsAccountActionsViewModel(
+ fragment,
+ IntentFactoryShim
+ )
+ )
return itemViewModelList
}
diff --git a/app/src/main/java/org/oppia/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsAccountActionsViewModel.kt b/app/src/main/java/org/oppia/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsAccountActionsViewModel.kt
index 2878ccfe8ef..805eb2b4821 100644
--- a/app/src/main/java/org/oppia/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsAccountActionsViewModel.kt
+++ b/app/src/main/java/org/oppia/app/administratorcontrols/administratorcontrolsitemviewmodel/AdministratorControlsAccountActionsViewModel.kt
@@ -4,11 +4,12 @@ import android.content.Intent
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import org.oppia.app.R
-import org.oppia.app.profile.ProfileActivity
+import org.oppia.app.shim.IntentFactoryShim
/** [ViewModel] for the recycler view in [AdministratorControlsFragment]. */
class AdministratorControlsAccountActionsViewModel(
- private val fragment: Fragment
+ private val fragment: Fragment,
+ private val intentFactoryShim: IntentFactoryShim
) : AdministratorControlsItemViewModel() {
fun onLogOutClicked() {
@@ -19,7 +20,7 @@ class AdministratorControlsAccountActionsViewModel(
}
.setPositiveButton(R.string.log_out_dialog_okay_button) { _, _ ->
// TODO(#762): Replace [ProfileChooserActivity] to [LoginActivity] once it is added.
- val intent = Intent(fragment.activity, ProfileActivity::class.java)
+ val intent = intentFactoryShim.createProfileActivityIntent(fragment.activity!!)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
fragment.activity!!.startActivity(intent)
fragment.activity!!.finish()
diff --git a/app/src/main/java/org/oppia/app/administratorcontrols/appversion/AppVersionViewModel.kt b/app/src/main/java/org/oppia/app/administratorcontrols/appversion/AppVersionViewModel.kt
index c78c71f6a25..78c32f1a782 100644
--- a/app/src/main/java/org/oppia/app/administratorcontrols/appversion/AppVersionViewModel.kt
+++ b/app/src/main/java/org/oppia/app/administratorcontrols/appversion/AppVersionViewModel.kt
@@ -1,8 +1,9 @@
package org.oppia.app.administratorcontrols.appversion
+import android.content.Context
import androidx.databinding.ObservableField
import androidx.fragment.app.Fragment
-import org.oppia.app.BuildConfig
+import androidx.lifecycle.ViewModel
import org.oppia.app.fragment.FragmentScope
import org.oppia.app.viewmodel.ObservableViewModel
import org.oppia.util.system.OppiaDateTimeFormatter
@@ -13,10 +14,12 @@ import javax.inject.Inject
@FragmentScope
class AppVersionViewModel @Inject constructor(
fragment: Fragment,
- private val oppiaDateTimeFormatter: OppiaDateTimeFormatter
+ private val oppiaDateTimeFormatter: OppiaDateTimeFormatter,
+ context: Context
) : ObservableViewModel() {
- val versionName = ObservableField(BuildConfig.VERSION_NAME)
+ val versionName: String = context.packageManager
+ .getPackageInfo(context.packageName, 0).versionName
private val lastUpdateDateTime =
fragment.activity!!.packageManager.getPackageInfo(
diff --git a/app/src/main/java/org/oppia/app/application/ApplicationComponent.kt b/app/src/main/java/org/oppia/app/application/ApplicationComponent.kt
index 3a3038144e8..730a41438bc 100644
--- a/app/src/main/java/org/oppia/app/application/ApplicationComponent.kt
+++ b/app/src/main/java/org/oppia/app/application/ApplicationComponent.kt
@@ -4,6 +4,8 @@ import android.app.Application
import dagger.BindsInstance
import dagger.Component
import org.oppia.app.activity.ActivityComponent
+import org.oppia.app.shim.IntentFactoryShimModule
+import org.oppia.app.shim.ViewBindingShimModule
import org.oppia.data.backends.gae.NetworkModule
import org.oppia.domain.classify.InteractionsModule
import org.oppia.domain.classify.rules.continueinteraction.ContinueModule
@@ -45,7 +47,8 @@ import javax.inject.Singleton
InteractionsModule::class, GcsResourceModule::class, GlideImageLoaderModule::class,
ImageParsingModule::class, HtmlParserEntityTypeModule::class, CachingModule::class,
QuestionModule::class, LogReportingModule::class, AccessibilityModule::class,
- ImageClickInputModule::class, LogStorageModule::class, PrimeTopicAssetsControllerModule::class,
+ ImageClickInputModule::class, LogStorageModule::class, IntentFactoryShimModule::class,
+ ViewBindingShimModule::class, PrimeTopicAssetsControllerModule::class,
ExpirationMetaDataRetrieverModule::class
]
)
diff --git a/app/src/main/java/org/oppia/app/customview/LessonThumbnailImageView.kt b/app/src/main/java/org/oppia/app/customview/LessonThumbnailImageView.kt
index b21fc34a62a..a1bc7fc5f5d 100644
--- a/app/src/main/java/org/oppia/app/customview/LessonThumbnailImageView.kt
+++ b/app/src/main/java/org/oppia/app/customview/LessonThumbnailImageView.kt
@@ -3,11 +3,12 @@ package org.oppia.app.customview
import android.content.Context
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView
+import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import org.oppia.app.R
-import org.oppia.app.fragment.InjectableFragment
import org.oppia.app.model.LessonThumbnail
import org.oppia.app.model.LessonThumbnailGraphic
+import org.oppia.app.shim.ViewComponentFactory
import org.oppia.util.gcsresource.DefaultResourceBucketName
import org.oppia.util.parser.DefaultGcsPrefix
import org.oppia.util.parser.ImageLoader
@@ -98,9 +99,8 @@ class LessonThumbnailImageView @JvmOverloads constructor(
override fun onAttachedToWindow() {
super.onAttachedToWindow()
- FragmentManager.findFragment(this)
- .createViewComponent(this)
- .inject(this)
+ (FragmentManager.findFragment(this) as ViewComponentFactory)
+ .createViewComponent(this).inject(this)
}
private fun getLessonDrawableResource(lessonThumbnail: LessonThumbnail): Int {
diff --git a/app/src/main/java/org/oppia/app/databinding/TextViewBindingAdapters.java b/app/src/main/java/org/oppia/app/databinding/TextViewBindingAdapters.java
index 066010be643..b762b9f39c2 100644
--- a/app/src/main/java/org/oppia/app/databinding/TextViewBindingAdapters.java
+++ b/app/src/main/java/org/oppia/app/databinding/TextViewBindingAdapters.java
@@ -14,8 +14,6 @@
/** Holds all custom binding adapters that bind to [TextView]. */
public final class TextViewBindingAdapters {
- private static int MINUTE_MILLIS = (int) TimeUnit.MINUTES.toMillis(1);
-
/** Binds date text with relative time. */
@BindingAdapter("profile:created")
public static void setProfileDataText(@NonNull TextView textView, long timestamp) {
@@ -57,7 +55,7 @@ private static String getTimeAgo(long lastVisitedTimeStamp, Context context) {
Resources res = context.getResources();
long timeDifferenceMillis = currentTimeMillis - timeStampMillis;
- if (timeDifferenceMillis < MINUTE_MILLIS) {
+ if (timeDifferenceMillis < (int) TimeUnit.MINUTES.toMillis(1)) {
return context.getString(R.string.just_now);
} else if (timeDifferenceMillis < TimeUnit.MINUTES.toMillis(50)) {
return getPluralString(
diff --git a/app/src/main/java/org/oppia/app/fragment/FragmentComponent.kt b/app/src/main/java/org/oppia/app/fragment/FragmentComponent.kt
index bdada11db7e..ff6294faa3f 100644
--- a/app/src/main/java/org/oppia/app/fragment/FragmentComponent.kt
+++ b/app/src/main/java/org/oppia/app/fragment/FragmentComponent.kt
@@ -32,6 +32,8 @@ import org.oppia.app.profile.AdminSettingsDialogFragment
import org.oppia.app.profile.ProfileChooserFragment
import org.oppia.app.profile.ResetPinDialogFragment
import org.oppia.app.profileprogress.ProfileProgressFragment
+import org.oppia.app.shim.IntentFactoryShimModule
+import org.oppia.app.shim.ViewBindingShimModule
import org.oppia.app.story.StoryFragment
import org.oppia.app.testing.BindableAdapterTestFragment
import org.oppia.app.testing.ImageRegionSelectionTestFragment
@@ -51,7 +53,12 @@ import org.oppia.app.walkthrough.welcome.WalkthroughWelcomeFragment
import javax.inject.Provider
/** Root subcomponent for all fragments. */
-@Subcomponent(modules = [FragmentModule::class, InteractionViewModelModule::class])
+@Subcomponent(
+ modules = [
+ FragmentModule::class, InteractionViewModelModule::class, IntentFactoryShimModule::class,
+ ViewBindingShimModule::class
+ ]
+)
@FragmentScope
interface FragmentComponent {
@Subcomponent.Builder
diff --git a/app/src/main/java/org/oppia/app/fragment/InjectableFragment.kt b/app/src/main/java/org/oppia/app/fragment/InjectableFragment.kt
index cd8acf9f97b..f18a4541cdb 100644
--- a/app/src/main/java/org/oppia/app/fragment/InjectableFragment.kt
+++ b/app/src/main/java/org/oppia/app/fragment/InjectableFragment.kt
@@ -4,13 +4,14 @@ import android.content.Context
import android.view.View
import androidx.fragment.app.Fragment
import org.oppia.app.activity.InjectableAppCompatActivity
+import org.oppia.app.shim.ViewComponentFactory
import org.oppia.app.view.ViewComponent
/**
* A fragment that facilitates field injection to children. This fragment can only be used with
* [InjectableAppCompatActivity] contexts.
*/
-abstract class InjectableFragment : Fragment() {
+abstract class InjectableFragment : Fragment(), ViewComponentFactory {
/**
* The [FragmentComponent] corresponding to this fragment. This cannot be used before [onAttach] is called, and can be
* used to inject lateinit fields in child fragments during fragment attachment (which is recommended to be done in an
@@ -24,7 +25,7 @@ abstract class InjectableFragment : Fragment() {
(requireActivity() as InjectableAppCompatActivity).createFragmentComponent(this)
}
- fun createViewComponent(view: View): ViewComponent {
+ override fun createViewComponent(view: View): ViewComponent {
return fragmentComponent.getViewComponentBuilderProvider().get().setView(view).build()
}
}
diff --git a/app/src/main/java/org/oppia/app/home/HomeFragmentPresenter.kt b/app/src/main/java/org/oppia/app/home/HomeFragmentPresenter.kt
index 04724cc9c33..ce6f039b4f5 100644
--- a/app/src/main/java/org/oppia/app/home/HomeFragmentPresenter.kt
+++ b/app/src/main/java/org/oppia/app/home/HomeFragmentPresenter.kt
@@ -25,6 +25,7 @@ import org.oppia.app.model.Profile
import org.oppia.app.model.ProfileId
import org.oppia.app.model.TopicList
import org.oppia.app.model.TopicSummary
+import org.oppia.app.shim.IntentFactoryShim
import org.oppia.domain.oppialogger.OppiaLogger
import org.oppia.domain.profile.ProfileManagementController
import org.oppia.domain.topic.TopicListController
@@ -46,6 +47,7 @@ class HomeFragmentPresenter @Inject constructor(
private val oppiaClock: OppiaClock,
private val logger: ConsoleLogger,
private val oppiaLogger: OppiaLogger,
+ private val intentFactoryShim: IntentFactoryShim,
@TopicHtmlParserEntityType private val topicEntityType: String,
@StoryHtmlParserEntityType private val storyEntityType: String
) {
@@ -71,7 +73,11 @@ class HomeFragmentPresenter @Inject constructor(
logHomeActivityEvent()
welcomeViewModel = WelcomeViewModel()
- promotedStoryListViewModel = PromotedStoryListViewModel(activity, internalProfileId)
+ promotedStoryListViewModel = PromotedStoryListViewModel(
+ activity,
+ internalProfileId,
+ intentFactoryShim
+ )
allTopicsViewModel = AllTopicsViewModel()
itemList.add(welcomeViewModel)
itemList.add(promotedStoryListViewModel)
@@ -191,14 +197,24 @@ class HomeFragmentPresenter @Inject constructor(
promotedStoryList.clear()
if (it.recentStoryCount != 0) {
it.recentStoryList.take(limit).forEach { promotedStory ->
- val recentStory = PromotedStoryViewModel(activity, internalProfileId, storyEntityType)
+ val recentStory = PromotedStoryViewModel(
+ activity,
+ internalProfileId,
+ storyEntityType,
+ intentFactoryShim
+ )
recentStory.setPromotedStory(promotedStory)
promotedStoryList.add(recentStory)
}
} else {
// TODO(#936): Optimise this as part of recommended stories.
it.olderStoryList.take(limit).forEach { promotedStory ->
- val oldStory = PromotedStoryViewModel(activity, internalProfileId, storyEntityType)
+ val oldStory = PromotedStoryViewModel(
+ activity,
+ internalProfileId,
+ storyEntityType,
+ intentFactoryShim
+ )
oldStory.setPromotedStory(promotedStory)
promotedStoryList.add(oldStory)
}
diff --git a/app/src/main/java/org/oppia/app/home/topiclist/PromotedStoryListViewModel.kt b/app/src/main/java/org/oppia/app/home/topiclist/PromotedStoryListViewModel.kt
index 84f039a17d1..135e69c2386 100644
--- a/app/src/main/java/org/oppia/app/home/topiclist/PromotedStoryListViewModel.kt
+++ b/app/src/main/java/org/oppia/app/home/topiclist/PromotedStoryListViewModel.kt
@@ -4,12 +4,13 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModel
import org.oppia.app.home.HomeItemViewModel
import org.oppia.app.home.RouteToRecentlyPlayedListener
-import org.oppia.app.home.recentlyplayed.RecentlyPlayedActivity
+import org.oppia.app.shim.IntentFactoryShim
/** [ViewModel] promoted story list in [HomeFragment]. */
class PromotedStoryListViewModel(
private val activity: AppCompatActivity,
- private val internalProfileId: Int
+ private val internalProfileId: Int,
+ private val IntentFactoryShim: IntentFactoryShim
) :
HomeItemViewModel(),
RouteToRecentlyPlayedListener {
@@ -19,11 +20,10 @@ class PromotedStoryListViewModel(
}
override fun routeToRecentlyPlayed() {
- activity.startActivity(
- RecentlyPlayedActivity.createRecentlyPlayedActivityIntent(
- activity.applicationContext,
- internalProfileId
- )
+ val intent = IntentFactoryShim.createRecentlyPlayedActivityIntent(
+ activity.applicationContext,
+ internalProfileId
)
+ activity.startActivity(intent)
}
}
diff --git a/app/src/main/java/org/oppia/app/home/topiclist/PromotedStoryViewModel.kt b/app/src/main/java/org/oppia/app/home/topiclist/PromotedStoryViewModel.kt
index 6d202dc4a91..7978fec262c 100755
--- a/app/src/main/java/org/oppia/app/home/topiclist/PromotedStoryViewModel.kt
+++ b/app/src/main/java/org/oppia/app/home/topiclist/PromotedStoryViewModel.kt
@@ -6,7 +6,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import org.oppia.app.home.RouteToTopicPlayStoryListener
import org.oppia.app.model.PromotedStory
-import org.oppia.app.topic.TopicActivity
+import org.oppia.app.shim.IntentFactoryShim
import org.oppia.app.viewmodel.ObservableViewModel
// TODO(#283): Add download status information to promoted-story-card.
@@ -15,7 +15,8 @@ import org.oppia.app.viewmodel.ObservableViewModel
class PromotedStoryViewModel(
private val activity: AppCompatActivity,
private val internalProfileId: Int,
- val entityType: String
+ val entityType: String,
+ private val IntentFactoryShim: IntentFactoryShim
) :
ObservableViewModel(),
RouteToTopicPlayStoryListener {
@@ -40,13 +41,12 @@ class PromotedStoryViewModel(
}
override fun routeToTopicPlayStory(internalProfileId: Int, topicId: String, storyId: String) {
- activity.startActivity(
- TopicActivity.createTopicPlayStoryActivityIntent(
- activity.applicationContext,
- internalProfileId,
- topicId,
- storyId
- )
+ val intent = IntentFactoryShim.createTopicPlayStoryActivityIntent(
+ activity.applicationContext,
+ internalProfileId,
+ topicId,
+ storyId
)
+ activity.startActivity(intent)
}
}
diff --git a/app/src/main/java/org/oppia/app/parser/StringToFractionParser.kt b/app/src/main/java/org/oppia/app/parser/StringToFractionParser.kt
index 55f5338d3a7..10a6fbf8c07 100644
--- a/app/src/main/java/org/oppia/app/parser/StringToFractionParser.kt
+++ b/app/src/main/java/org/oppia/app/parser/StringToFractionParser.kt
@@ -3,7 +3,6 @@ package org.oppia.app.parser
import android.content.Context
import androidx.annotation.StringRes
import org.oppia.app.R
-import org.oppia.app.customview.interaction.FractionInputInteractionView
import org.oppia.app.model.Fraction
import org.oppia.domain.util.normalizeWhitespace
diff --git a/app/src/main/java/org/oppia/app/player/state/DragDropSortInteractionView.kt b/app/src/main/java/org/oppia/app/player/state/DragDropSortInteractionView.kt
index efbf937249a..8a0e726b4c3 100644
--- a/app/src/main/java/org/oppia/app/player/state/DragDropSortInteractionView.kt
+++ b/app/src/main/java/org/oppia/app/player/state/DragDropSortInteractionView.kt
@@ -5,18 +5,17 @@ import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.core.view.isVisible
import androidx.databinding.BindingAdapter
-import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
-import org.oppia.app.databinding.DragDropInteractionItemsBinding
-import org.oppia.app.databinding.DragDropSingleItemBinding
-import org.oppia.app.fragment.InjectableFragment
import org.oppia.app.player.state.itemviewmodel.DragDropInteractionContentViewModel
import org.oppia.app.recyclerview.BindableAdapter
import org.oppia.app.recyclerview.DragAndDropItemFacilitator
import org.oppia.app.recyclerview.OnDragEndedListener
import org.oppia.app.recyclerview.OnItemDragListener
+import org.oppia.app.shim.ViewBindingShim
+import org.oppia.app.shim.ViewComponentFactory
import org.oppia.util.accessibility.CustomAccessibilityManager
import org.oppia.util.gcsresource.DefaultResourceBucketName
import org.oppia.util.parser.ExplorationHtmlParserEntityType
@@ -50,11 +49,17 @@ class DragDropSortInteractionView @JvmOverloads constructor(
@field:DefaultResourceBucketName
lateinit var resourceBucketName: String
+ @Inject
+ lateinit var viewBindingShim: ViewBindingShim
+
private lateinit var entityId: String
+ private lateinit var onDragEnd: OnDragEndedListener
+ private lateinit var onItemDrag: OnItemDragListener
override fun onAttachedToWindow() {
super.onAttachedToWindow()
- FragmentManager.findFragment(this).createViewComponent(this).inject(this)
+ (FragmentManager.findFragment(this) as ViewComponentFactory)
+ .createViewComponent(this).inject(this)
isAccessibilityEnabled = accessibilityManager.isScreenReaderEnabled()
}
@@ -78,18 +83,24 @@ class DragDropSortInteractionView @JvmOverloads constructor(
.newBuilder()
.registerViewBinder(
inflateView = { parent ->
- DragDropInteractionItemsBinding.inflate(
- LayoutInflater.from(parent.context), parent, /* attachToParent= */ false
- ).root
+ viewBindingShim.provideDragDropSortInteractionInflatedView(
+ LayoutInflater.from(parent.context),
+ parent,
+ /* attachToParent= */ false
+ )
},
bindView = { view, viewModel ->
- val binding = DataBindingUtil.findBinding(view)!!
- binding.dragDropItemRecyclerview.adapter = createNestedAdapter()
- binding.adapter = adapter
- binding.dragDropContentGroupItem.isVisible = isMultipleItemsInSamePositionAllowed
- binding.dragDropContentUnlinkItems.isVisible = viewModel.htmlContent.htmlList.size > 1
- binding.dragDropAccessibleContainer.isVisible = isAccessibilityEnabled
- binding.viewModel = viewModel
+ viewBindingShim.setDragDropInteractionItemsBinding(view)
+ viewBindingShim.getDragDropInteractionItemsBindingRecyclerView().adapter =
+ createNestedAdapter()
+ adapter?.let { viewBindingShim.setDragDropInteractionItemsBindingAdapter(it) }
+ viewBindingShim.getDragDropInteractionItemsBindingGroupItem().isVisible =
+ isMultipleItemsInSamePositionAllowed
+ viewBindingShim.getDragDropInteractionItemsBindingUnlinkItems().isVisible =
+ viewModel.htmlContent.htmlList.size > 1
+ viewBindingShim.getDragDropInteractionItemsBindingAccessibleContainer().isVisible =
+ isAccessibilityEnabled
+ viewBindingShim.setDragDropInteractionItemsBindingViewModel(viewModel)
}
)
.build()
@@ -100,39 +111,49 @@ class DragDropSortInteractionView @JvmOverloads constructor(
.newBuilder()
.registerViewBinder(
inflateView = { parent ->
- DragDropSingleItemBinding.inflate(
- LayoutInflater.from(parent.context), parent, /* attachToParent= */ false
- ).root
+ viewBindingShim.provideDragDropSingleItemInflatedView(
+ LayoutInflater.from(parent.context),
+ parent,
+ /* attachToParent= */ false
+ )
},
bindView = { view, viewModel ->
- val binding = DataBindingUtil.findBinding(view)!!
- binding.htmlContent = htmlParserFactory.create(
+ viewBindingShim.setDragDropSingleItemBinding(view)
+ viewBindingShim.setDragDropSingleItemBindingHtmlContent(
+ htmlParserFactory,
resourceBucketName,
entityType,
entityId,
- /* imageCenterAlign= */ false
+ viewModel
)
- .parseOppiaHtml(
- viewModel, binding.dragDropContentTextView
- )
}
)
.build()
}
-}
-/** Bind ItemTouchHelperSimpleCallback with RecyclerView for a [DragDropSortInteractionView] via data-binding. */
-@BindingAdapter(value = ["onDragEnded", "onItemDrag"], requireAll = false)
-fun setItemDragToRecyclerView(
- dragDropSortInteractionView: DragDropSortInteractionView,
- onDragEnd: OnDragEndedListener,
- onItemDrag: OnItemDragListener
-) {
- val dragCallback: ItemTouchHelper.Callback =
- DragAndDropItemFacilitator(onItemDrag, onDragEnd)
-
- val itemTouchHelper = ItemTouchHelper(dragCallback)
- itemTouchHelper.attachToRecyclerView(dragDropSortInteractionView)
+ fun setOnDragEnded(onDragEnd: OnDragEndedListener) {
+ this.onDragEnd = onDragEnd
+ checkIfSettingIsPossible()
+ }
+
+ fun setOnItemDrag(onItemDrag: OnItemDragListener) {
+ this.onItemDrag = onItemDrag
+ checkIfSettingIsPossible()
+ }
+
+ private fun checkIfSettingIsPossible() {
+ if (::onDragEnd.isInitialized && ::onItemDrag.isInitialized) {
+ performAttachment()
+ }
+ }
+
+ private fun performAttachment() {
+ val dragCallback: ItemTouchHelper.Callback =
+ DragAndDropItemFacilitator(onItemDrag, onDragEnd)
+
+ val itemTouchHelper = ItemTouchHelper(dragCallback)
+ itemTouchHelper.attachToRecyclerView(this)
+ }
}
/** Sets the exploration ID for a specific [DragDropSortInteractionView] via data-binding. */
diff --git a/app/src/main/java/org/oppia/app/player/state/ImageRegionSelectionInteractionView.kt b/app/src/main/java/org/oppia/app/player/state/ImageRegionSelectionInteractionView.kt
index 4a2add5bd4c..426b1cc1bb2 100644
--- a/app/src/main/java/org/oppia/app/player/state/ImageRegionSelectionInteractionView.kt
+++ b/app/src/main/java/org/oppia/app/player/state/ImageRegionSelectionInteractionView.kt
@@ -6,10 +6,11 @@ import android.view.View
import android.widget.FrameLayout
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.view.forEachIndexed
-import androidx.databinding.BindingAdapter
+import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
-import org.oppia.app.fragment.InjectableFragment
import org.oppia.app.model.ImageWithRegions
+import org.oppia.app.shim.ViewBindingShim
+import org.oppia.app.shim.ViewComponentFactory
import org.oppia.app.utility.ClickableAreasImage
import org.oppia.app.utility.OnClickableAreaClickedListener
import org.oppia.util.accessibility.CustomAccessibilityManager
@@ -61,7 +62,12 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor(
@field:DefaultGcsPrefix
lateinit var gcsPrefix: String
+ @Inject
+ lateinit var bindingInterface: ViewBindingShim
+
private lateinit var entityId: String
+ private lateinit var overlayView: FrameLayout
+ private lateinit var onRegionClicked: OnClickableAreaClickedListener
/**
* Sets the URL for the image & initiates loading it. This is intended to be called via data-binding.
@@ -107,7 +113,8 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor(
val area = ClickableAreasImage(
this,
this.parent as FrameLayout,
- listener
+ listener,
+ bindingInterface
)
area.addRegionViews()
}
@@ -122,27 +129,40 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor(
override fun onAttachedToWindow() {
super.onAttachedToWindow()
- FragmentManager.findFragment(this).createViewComponent(this).inject(this)
+ (FragmentManager.findFragment(this) as ViewComponentFactory)
+ .createViewComponent(this)
+ .inject(this)
isAccessibilityEnabled = accessibilityManager.isScreenReaderEnabled()
}
-}
-/** Bind ItemTouchHelperSimpleCallback with RecyclerView for a [DragDropSortInteractionView] via data-binding. */
-@BindingAdapter(value = ["onRegionClicked", "overlayView"], requireAll = false)
-fun setRegionClickToImageView(
- imageRegionSelectionInteractionView: ImageRegionSelectionInteractionView,
- onClickableAreaClickedListener: OnClickableAreaClickedListener,
- parentView: FrameLayout
-) {
- val area = ClickableAreasImage(
- imageRegionSelectionInteractionView,
- parentView,
- onClickableAreaClickedListener
- )
-
- imageRegionSelectionInteractionView.addOnLayoutChangeListener { _, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom -> // ktlint-disable max-line-length
- // Update the regions, as the bounds have changed
- if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom)
- area.addRegionViews()
+ fun setOnRegionClicked(onRegionClicked: OnClickableAreaClickedListener) {
+ this.onRegionClicked = onRegionClicked
+ checkIfSettingIsPossible()
+ }
+
+ fun setOverlayView(overlayView: FrameLayout) {
+ this.overlayView = overlayView
+ checkIfSettingIsPossible()
+ }
+
+ private fun checkIfSettingIsPossible() {
+ if (::onRegionClicked.isInitialized && ::overlayView.isInitialized) {
+ performAttachment()
+ }
+ }
+
+ private fun performAttachment() {
+ val area = ClickableAreasImage(
+ this,
+ overlayView,
+ onRegionClicked,
+ bindingInterface
+ )
+
+ this.addOnLayoutChangeListener { _, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom -> // ktlint-disable max-line-length
+ // Update the regions, as the bounds have changed
+ if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom)
+ area.addRegionViews()
+ }
}
}
diff --git a/app/src/main/java/org/oppia/app/player/state/SelectionInteractionView.kt b/app/src/main/java/org/oppia/app/player/state/SelectionInteractionView.kt
index ad3a2939673..42df791b83a 100644
--- a/app/src/main/java/org/oppia/app/player/state/SelectionInteractionView.kt
+++ b/app/src/main/java/org/oppia/app/player/state/SelectionInteractionView.kt
@@ -4,25 +4,19 @@ import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.databinding.BindingAdapter
-import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.RecyclerView
-import org.oppia.app.databinding.ItemSelectionInteractionItemsBinding
-import org.oppia.app.databinding.MultipleChoiceInteractionItemsBinding
-import org.oppia.app.fragment.InjectableFragment
import org.oppia.app.player.state.itemviewmodel.SelectionInteractionContentViewModel
+import org.oppia.app.player.state.itemviewmodel.SelectionItemInputType
import org.oppia.app.recyclerview.BindableAdapter
+import org.oppia.app.shim.ViewBindingShim
+import org.oppia.app.shim.ViewComponentFactory
import org.oppia.util.gcsresource.DefaultResourceBucketName
import org.oppia.util.parser.ExplorationHtmlParserEntityType
import org.oppia.util.parser.HtmlParser
import javax.inject.Inject
-/** Corresponds to the type of input that should be used for an item selection interaction view. */
-enum class SelectionItemInputType {
- CHECKBOXES,
- RADIO_BUTTONS
-}
-
/**
* A custom [RecyclerView] for displaying a variable list of items that may be selected by a user as part of the item
* selection or multiple choice interactions.
@@ -46,11 +40,15 @@ class SelectionInteractionView @JvmOverloads constructor(
@field:DefaultResourceBucketName
lateinit var resourceBucketName: String
+ @Inject
+ lateinit var bindingInterface: ViewBindingShim
+
private lateinit var entityId: String
override fun onAttachedToWindow() {
super.onAttachedToWindow()
- FragmentManager.findFragment(this).createViewComponent(this).inject(this)
+ (FragmentManager.findFragment(this) as ViewComponentFactory)
+ .createViewComponent(this).inject(this)
}
fun setAllOptionsItemInputType(selectionItemInputType: SelectionItemInputType) {
@@ -75,20 +73,21 @@ class SelectionInteractionView @JvmOverloads constructor(
.newBuilder()
.registerViewBinder(
inflateView = { parent ->
- ItemSelectionInteractionItemsBinding.inflate(
- LayoutInflater.from(parent.context), parent, /* attachToParent= */ false
- ).root
+ bindingInterface.provideSelectionInteractionViewInflatedView(
+ LayoutInflater.from(parent.context),
+ parent,
+ /* attachToParent= */ false
+ )
},
bindView = { view, viewModel ->
- val binding =
- DataBindingUtil.findBinding(view)!!
- binding.htmlContent =
- htmlParserFactory.create(
- resourceBucketName, entityType, entityId, /* imageCenterAlign= */ false
- ).parseOppiaHtml(
- viewModel.htmlContent, binding.itemSelectionContentsTextView
- )
- binding.viewModel = viewModel
+ bindingInterface.provideSelectionInteractionViewModel(
+ view,
+ viewModel,
+ htmlParserFactory,
+ resourceBucketName,
+ entityType,
+ entityId
+ )
}
)
.build()
@@ -97,20 +96,21 @@ class SelectionInteractionView @JvmOverloads constructor(
.newBuilder()
.registerViewBinder(
inflateView = { parent ->
- MultipleChoiceInteractionItemsBinding.inflate(
- LayoutInflater.from(parent.context), parent, /* attachToParent= */ false
- ).root
+ bindingInterface.provideMultipleChoiceInteractionItemsInflatedView(
+ LayoutInflater.from(parent.context),
+ parent,
+ /* attachToParent= */ false
+ )
},
bindView = { view, viewModel ->
- val binding =
- DataBindingUtil.findBinding(view)!!
- binding.htmlContent =
- htmlParserFactory.create(
- resourceBucketName, entityType, entityId, /* imageCenterAlign= */ false
- ).parseOppiaHtml(
- viewModel.htmlContent, binding.multipleChoiceContentTextView
- )
- binding.viewModel = viewModel
+ bindingInterface.provideMultipleChoiceInteractionItemsViewModel(
+ view,
+ viewModel,
+ htmlParserFactory,
+ resourceBucketName,
+ entityType,
+ entityId
+ )
}
)
.build()
@@ -118,12 +118,9 @@ class SelectionInteractionView @JvmOverloads constructor(
}
}
-/** Sets the [SelectionItemInputType] for a specific [SelectionInteractionView] via data-binding. */
-@BindingAdapter("allOptionsItemInputType")
-fun setAllOptionsItemInputType(
- selectionInteractionView: SelectionInteractionView,
- selectionItemInputType: SelectionItemInputType
-) = selectionInteractionView.setAllOptionsItemInputType(selectionItemInputType)
+fun setAllOptionsItemInputType(selectionItemInputType: SelectionItemInputType) {
+ setAllOptionsItemInputType(selectionItemInputType)
+}
/** Sets the exploration ID for a specific [SelectionInteractionView] via data-binding. */
@BindingAdapter("entityId")
diff --git a/app/src/main/java/org/oppia/app/player/state/StateFragmentPresenter.kt b/app/src/main/java/org/oppia/app/player/state/StateFragmentPresenter.kt
index 917edd903f6..7007e00a512 100755
--- a/app/src/main/java/org/oppia/app/player/state/StateFragmentPresenter.kt
+++ b/app/src/main/java/org/oppia/app/player/state/StateFragmentPresenter.kt
@@ -96,7 +96,11 @@ class StateFragmentPresenter @Inject constructor(
this.storyId = storyId
this.explorationId = explorationId
- binding = StateFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false)
+ binding = StateFragmentBinding.inflate(
+ inflater,
+ container,
+ /* attachToRoot= */ false
+ )
recyclerViewAssembler = createRecyclerViewAssembler(
assemblerBuilderFactory.create(resourceBucketName, entityType),
binding.congratulationsTextView
@@ -120,7 +124,9 @@ class StateFragmentPresenter @Inject constructor(
if (bottom < oldBottom) {
binding.stateRecyclerView.postDelayed(
{
- binding.stateRecyclerView.scrollToPosition(stateRecyclerViewAdapter.itemCount - 1)
+ binding.stateRecyclerView.scrollToPosition(
+ stateRecyclerViewAdapter.itemCount - 1
+ )
},
100
)
@@ -173,7 +179,7 @@ class StateFragmentPresenter @Inject constructor(
fun onSubmitButtonClicked() {
hideKeyboard()
- handleSubmitAnswer(viewModel.getPendingAnswer(recyclerViewAssembler))
+ handleSubmitAnswer(viewModel.getPendingAnswer(recyclerViewAssembler::getPendingAnswerHandler))
}
fun onResponsesHeaderClicked() {
@@ -208,7 +214,7 @@ class StateFragmentPresenter @Inject constructor(
fun handleKeyboardAction() {
hideKeyboard()
if (viewModel.getCanSubmitAnswer().get() == true) {
- handleSubmitAnswer(viewModel.getPendingAnswer(recyclerViewAssembler))
+ handleSubmitAnswer(viewModel.getPendingAnswer(recyclerViewAssembler::getPendingAnswerHandler))
}
}
@@ -281,7 +287,11 @@ class StateFragmentPresenter @Inject constructor(
private fun processEphemeralStateResult(result: AsyncResult) {
if (result.isFailure()) {
- logger.e("StateFragment", "Failed to retrieve ephemeral state", result.getErrorOrNull()!!)
+ logger.e(
+ "StateFragment",
+ "Failed to retrieve ephemeral state",
+ result.getErrorOrNull()!!
+ )
return
} else if (result.isPending()) {
// Display nothing until a valid result is available.
@@ -327,7 +337,8 @@ class StateFragmentPresenter @Inject constructor(
/**
* This function listens to the result of RevealHint.
* Whenever a hint is revealed using ExplorationProgressController.submitHintIsRevealed function,
- * this function will wait for the response from that function and based on which we can move to next state.
+ * this function will wait for the response from that function and based on which we can move to
+ * next state.
*/
private fun subscribeToHint(hintResultLiveData: LiveData>) {
val hintLiveData = getHintIsRevealed(hintResultLiveData)
@@ -345,7 +356,8 @@ class StateFragmentPresenter @Inject constructor(
/**
* This function listens to the result of RevealSolution.
* Whenever a hint is revealed using ExplorationProgressController.submitHintIsRevealed function,
- * this function will wait for the response from that function and based on which we can move to next state.
+ * this function will wait for the response from that function and based on which we can move to
+ * next state.
*/
private fun subscribeToSolution(solutionResultLiveData: LiveData>) {
val solutionLiveData = getSolutionIsRevealed(solutionResultLiveData)
@@ -363,7 +375,8 @@ class StateFragmentPresenter @Inject constructor(
/**
* This function listens to the result of submitAnswer.
* Whenever an answer is submitted using ExplorationProgressController.submitAnswer function,
- * this function will wait for the response from that function and based on which we can move to next state.
+ * this function will wait for the response from that function and based on which we can move to
+ * next state.
*/
private fun subscribeToAnswerOutcome(
answerOutcomeResultLiveData: LiveData>
diff --git a/app/src/main/java/org/oppia/app/player/state/StateViewModel.kt b/app/src/main/java/org/oppia/app/player/state/StateViewModel.kt
index 012ffc3ebf8..c7bacbf461a 100644
--- a/app/src/main/java/org/oppia/app/player/state/StateViewModel.kt
+++ b/app/src/main/java/org/oppia/app/player/state/StateViewModel.kt
@@ -6,6 +6,7 @@ import androidx.lifecycle.ViewModel
import org.oppia.app.fragment.FragmentScope
import org.oppia.app.model.UserAnswer
import org.oppia.app.player.state.answerhandling.AnswerErrorCategory
+import org.oppia.app.player.state.answerhandling.InteractionAnswerHandler
import org.oppia.app.player.state.itemviewmodel.StateItemViewModel
import org.oppia.app.viewmodel.ObservableArrayList
import org.oppia.app.viewmodel.ObservableViewModel
@@ -48,25 +49,30 @@ class StateViewModel @Inject constructor() : ObservableViewModel() {
fun getCanSubmitAnswer(): ObservableField = canSubmitAnswer
fun getPendingAnswer(
- statePlayerRecyclerViewAssembler: StatePlayerRecyclerViewAssembler
+ retrieveAnswerHandler: (List) -> InteractionAnswerHandler?
): UserAnswer {
- return getPendingAnswerWithoutError(statePlayerRecyclerViewAssembler)
- ?: UserAnswer.getDefaultInstance()
+ return getPendingAnswerWithoutError(
+ retrieveAnswerHandler(
+ getAnswerItemList()
+ )
+ ) ?: UserAnswer.getDefaultInstance()
}
private fun getPendingAnswerWithoutError(
- statePlayerRecyclerViewAssembler: StatePlayerRecyclerViewAssembler
+ answerHandler: InteractionAnswerHandler?
): UserAnswer? {
- val items = if (isSplitView.get() == true) {
- rightItemList
- } else {
- itemList
- }
- val answerHandler = statePlayerRecyclerViewAssembler.getPendingAnswerHandler(items)
return if (answerHandler?.checkPendingAnswerError(AnswerErrorCategory.SUBMIT_TIME) == null) {
answerHandler?.getPendingAnswer()
} else {
null
}
}
+
+ private fun getAnswerItemList(): List {
+ return if (isSplitView.get() == true) {
+ rightItemList
+ } else {
+ itemList
+ }
+ }
}
diff --git a/app/src/main/java/org/oppia/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt b/app/src/main/java/org/oppia/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt
index 79fb2a402a0..9b4450ca04c 100644
--- a/app/src/main/java/org/oppia/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt
+++ b/app/src/main/java/org/oppia/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt
@@ -7,12 +7,17 @@ import org.oppia.app.model.Interaction
import org.oppia.app.model.InteractionObject
import org.oppia.app.model.StringList
import org.oppia.app.model.UserAnswer
-import org.oppia.app.player.state.SelectionItemInputType
import org.oppia.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver
import org.oppia.app.player.state.answerhandling.InteractionAnswerHandler
import org.oppia.app.player.state.answerhandling.InteractionAnswerReceiver
import org.oppia.app.viewmodel.ObservableArrayList
+/** Corresponds to the type of input that should be used for an item selection interaction view. */
+enum class SelectionItemInputType {
+ CHECKBOXES,
+ RADIO_BUTTONS
+}
+
/** [StateItemViewModel] for multiple or item-selection input choice list. */
class SelectionInteractionViewModel(
val entityId: String,
diff --git a/app/src/main/java/org/oppia/app/profile/ProfileInputView.kt b/app/src/main/java/org/oppia/app/profile/ProfileInputView.kt
index c19f595830b..492bedfddf3 100644
--- a/app/src/main/java/org/oppia/app/profile/ProfileInputView.kt
+++ b/app/src/main/java/org/oppia/app/profile/ProfileInputView.kt
@@ -10,9 +10,12 @@ import android.widget.EditText
import android.widget.LinearLayout
import android.widget.TextView
import androidx.databinding.BindingAdapter
-import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentManager
import org.oppia.app.R
-import org.oppia.app.databinding.ProfileInputViewBinding
+import org.oppia.app.shim.ViewBindingShim
+import org.oppia.app.shim.ViewComponentFactory
+import javax.inject.Inject
/** Custom view that is used for name or pin input with error messages. */
class ProfileInputView @JvmOverloads constructor(
@@ -20,6 +23,16 @@ class ProfileInputView @JvmOverloads constructor(
attrs: AttributeSet? = null,
defStyle: Int = 0
) : LinearLayout(context, attrs, defStyle) {
+
+ @Inject
+ lateinit var bindingInterface: ViewBindingShim
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ (FragmentManager.findFragment(this) as ViewComponentFactory)
+ .createViewComponent(this).inject(this)
+ }
+
companion object {
@JvmStatic
@BindingAdapter("profile:label")
@@ -27,42 +40,11 @@ class ProfileInputView @JvmOverloads constructor(
profileInputView.label.text = label
}
- @JvmStatic
- @BindingAdapter("profile:labelMargin")
- fun setLayoutMarginStart(profileInputView: ProfileInputView, dimen: Float) {
- val layoutParams = profileInputView.label.layoutParams as MarginLayoutParams
- layoutParams.marginStart = dimen.toInt()
- profileInputView.label.layoutParams = layoutParams
- }
-
- @JvmStatic
- @BindingAdapter("profile:inputLength")
- fun setInputLength(profileInputView: ProfileInputView, inputLength: Int) {
- profileInputView.input.filters = arrayOf(InputFilter.LengthFilter(inputLength))
- }
-
- @JvmStatic
- @BindingAdapter("profile:error")
- fun setProfileImage(profileInputView: ProfileInputView, errorMessage: String?) {
- var errMessage: String = errorMessage ?: ""
- if (errMessage.isEmpty()) {
- profileInputView.clearErrorText()
- } else {
- profileInputView.setErrorText(errMessage)
- }
- }
-
/** Binding adapter for setting a [TextWatcher] as a change listener for an [EditText]. */
@BindingAdapter("android:addTextChangedListener")
fun bindTextWatcher(editText: EditText, textWatcher: TextWatcher) {
editText.addTextChangedListener(textWatcher)
}
-
- @JvmStatic
- @BindingAdapter("profile:singleLine")
- fun setSingleLine(profileInputView: ProfileInputView, type: Boolean) {
- profileInputView.input.setSingleLine(type)
- }
}
private var label: TextView
@@ -70,16 +52,23 @@ class ProfileInputView @JvmOverloads constructor(
private var input: EditText
init {
- val binding = DataBindingUtil.inflate(
+ val attributes = context.obtainStyledAttributes(attrs, R.styleable.ProfileInputView)
+ label = bindingInterface.provideProfileInputViewBindingLabelText(
LayoutInflater.from(context),
- R.layout.profile_input_view, this,
- /* attachToRoot= */ true
+ this,
+ true
+ )
+ label.text = attributes.getString(R.styleable.ProfileInputView_label)
+ input = bindingInterface.provideProfileInputViewBindingInput(
+ LayoutInflater.from(context),
+ this,
+ true
+ )
+ errorText = bindingInterface.provideProfileInputViewBindingErrorText(
+ LayoutInflater.from(context),
+ this,
+ true
)
- val attributes = context.obtainStyledAttributes(attrs, R.styleable.ProfileInputView)
- binding.labelText.text = attributes.getString(R.styleable.ProfileInputView_label)
- label = binding.labelText
- input = binding.input
- errorText = binding.errorText
orientation = VERTICAL
if (
attributes.getBoolean(
@@ -123,6 +112,29 @@ class ProfileInputView @JvmOverloads constructor(
errorText.text = errorMessage
}
+ fun setSingleLine(type: Boolean) {
+ input.setSingleLine(type)
+ }
+
+ fun setInputLength(inputLength: Int) {
+ input.filters = arrayOf(InputFilter.LengthFilter(inputLength))
+ }
+
+ fun setLabelMargin(dimen: Float) {
+ val layoutParams = label.layoutParams as MarginLayoutParams
+ layoutParams.marginStart = dimen.toInt()
+ label.layoutParams = layoutParams
+ }
+
+ fun setError(errorMessage: String) {
+ val errMessage: String = errorMessage ?: ""
+ if (errMessage.isEmpty()) {
+ clearErrorText()
+ } else {
+ setErrorText(errMessage)
+ }
+ }
+
fun setLabel(labelText: String) {
label.text = labelText
}
diff --git a/app/src/main/java/org/oppia/app/settings/profile/ProfileEditActivityPresenter.kt b/app/src/main/java/org/oppia/app/settings/profile/ProfileEditActivityPresenter.kt
index 39554484286..9cd0b1dd857 100644
--- a/app/src/main/java/org/oppia/app/settings/profile/ProfileEditActivityPresenter.kt
+++ b/app/src/main/java/org/oppia/app/settings/profile/ProfileEditActivityPresenter.kt
@@ -1,6 +1,7 @@
package org.oppia.app.settings.profile
import android.content.Intent
+import android.widget.Switch
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
@@ -35,7 +36,10 @@ class ProfileEditActivityPresenter @Inject constructor(
R.layout.profile_edit_activity
)
val profileId = activity.intent.getIntExtra(KEY_PROFILE_EDIT_PROFILE_ID, 0)
- editViewModel.setProfileId(profileId)
+ editViewModel.setProfileId(
+ profileId,
+ activity.findViewById(R.id.profile_edit_allow_download_switch)
+ )
binding.apply {
viewModel = editViewModel
lifecycleOwner = activity
diff --git a/app/src/main/java/org/oppia/app/settings/profile/ProfileEditViewModel.kt b/app/src/main/java/org/oppia/app/settings/profile/ProfileEditViewModel.kt
index 5949d131932..1a59305c91c 100644
--- a/app/src/main/java/org/oppia/app/settings/profile/ProfileEditViewModel.kt
+++ b/app/src/main/java/org/oppia/app/settings/profile/ProfileEditViewModel.kt
@@ -4,7 +4,6 @@ import android.widget.Switch
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
-import org.oppia.app.R
import org.oppia.app.activity.ActivityScope
import org.oppia.app.model.Profile
import org.oppia.app.model.ProfileId
@@ -14,6 +13,7 @@ import org.oppia.util.data.AsyncResult
import org.oppia.util.logging.ConsoleLogger
import javax.inject.Inject
+// TODO(#1633): Fix ViewModel to not depend on View
/** The ViewModel for [ProfileEditActivity]. */
@ActivityScope
class ProfileEditViewModel @Inject constructor(
@@ -22,6 +22,7 @@ class ProfileEditViewModel @Inject constructor(
private val profileManagementController: ProfileManagementController
) : ObservableViewModel() {
private lateinit var profileId: ProfileId
+ private lateinit var switch: Switch
lateinit var profileName: String
@@ -34,8 +35,9 @@ class ProfileEditViewModel @Inject constructor(
var isAdmin = false
- fun setProfileId(id: Int) {
+ fun setProfileId(id: Int, switch: Switch) {
profileId = ProfileId.newBuilder().setInternalId(id).build()
+ this.switch = switch
}
private fun processGetProfileResult(profileResult: AsyncResult): Profile {
@@ -47,7 +49,6 @@ class ProfileEditViewModel @Inject constructor(
)
}
val profile = profileResult.getOrDefault(Profile.getDefaultInstance())
- val switch = activity.findViewById(R.id.profile_edit_allow_download_switch)
switch.isChecked = profile.allowDownloadAccess
activity.title = profile.name
profileName = profile.name
diff --git a/app/src/main/java/org/oppia/app/shim/IntentFactoryShim.kt b/app/src/main/java/org/oppia/app/shim/IntentFactoryShim.kt
new file mode 100644
index 00000000000..4676edc3411
--- /dev/null
+++ b/app/src/main/java/org/oppia/app/shim/IntentFactoryShim.kt
@@ -0,0 +1,37 @@
+package org.oppia.app.shim
+
+import android.content.Context
+import android.content.Intent
+import androidx.fragment.app.FragmentActivity
+
+/**
+ * Creates intents for ViewModels in order to avoid ViewModel files directly depending on Activites.
+ * When working on a ViewModel file, developers should refrain from directly referencing Activities
+ * by adding all Intent functionality here.
+ *
+ * Please note that this file is temporary and all functionality will be returned to its respective
+ * ViewModel once Gradle has been removed.
+ */
+// TODO(#1619): Remove file post-Gradle
+interface IntentFactoryShim {
+
+ /** Returns [ProfileActivity] intent for [AdministratorControlsAccountActionsViewModel]. */
+ fun createProfileActivityIntent(fragment: FragmentActivity): Intent
+
+ /**
+ * Creates a [TopicActivity] intent for [PromotedStoryViewModel] and passes necessary string
+ * data.
+ * */
+ fun createTopicPlayStoryActivityIntent(
+ context: Context,
+ internalProfileId: Int,
+ topicId: String,
+ storyId: String
+ ): Intent
+
+ /**
+ * Creates a [RecentlyPlayedActivity] intent for [PromotedStoryListViewModel] and passes
+ * necessary string data.
+ * */
+ fun createRecentlyPlayedActivityIntent(context: Context, internalProfileId: Int): Intent
+}
diff --git a/app/src/main/java/org/oppia/app/shim/IntentFactoryShimImpl.kt b/app/src/main/java/org/oppia/app/shim/IntentFactoryShimImpl.kt
new file mode 100644
index 00000000000..4d57271bff5
--- /dev/null
+++ b/app/src/main/java/org/oppia/app/shim/IntentFactoryShimImpl.kt
@@ -0,0 +1,63 @@
+package org.oppia.app.shim
+
+import android.content.Context
+import android.content.Intent
+import androidx.fragment.app.FragmentActivity
+import org.oppia.app.drawer.KEY_NAVIGATION_PROFILE_ID
+import org.oppia.app.home.recentlyplayed.RecentlyPlayedActivity
+import org.oppia.app.profile.ProfileActivity
+import org.oppia.app.topic.TopicActivity
+import javax.inject.Inject
+
+/**
+ * Creates intents for ViewModels in order to avoid ViewModel files directly depending on Activites.
+ * When working on a ViewModel file, developers should refrain from directly referencing Activities
+ * by adding all Intent functionality here.
+ *
+ * Please note that this file is temporary and all functionality will be returned to its respective
+ * ViewModel once Gradle has been removed.
+ */
+// TODO(#1619): Remove file post-Gradle
+class IntentFactoryShimImpl @Inject constructor() : IntentFactoryShim {
+
+ private val TOPIC_ACTIVITY_TOPIC_ID_ARGUMENT_KEY = "TopicActivity.topic_id"
+ private val TOPIC_ACTIVITY_STORY_ID_ARGUMENT_KEY = "TopicActivity.story_id"
+
+ /** Returns [ProfileActivity] intent for [AdministratorControlsAccountActionsViewModel]. */
+ override fun createProfileActivityIntent(fragment: FragmentActivity): Intent {
+ return Intent(fragment, ProfileActivity::class.java)
+ }
+
+ /**
+ * Creates a [TopicActivity] intent for [PromotedStoryViewModel] and passes necessary string
+ * data.
+ * */
+ override fun createTopicPlayStoryActivityIntent(
+ context: Context,
+ internalProfileId: Int,
+ topicId: String,
+ storyId: String
+ ): Intent {
+ val intent = Intent(context, TopicActivity::class.java)
+ intent.putExtra(KEY_NAVIGATION_PROFILE_ID, internalProfileId)
+ intent.putExtra(TOPIC_ACTIVITY_TOPIC_ID_ARGUMENT_KEY, topicId)
+ intent.putExtra(TOPIC_ACTIVITY_STORY_ID_ARGUMENT_KEY, storyId)
+ return intent
+ }
+
+ /**
+ * Creates a [RecentlyPlayedActivity] intent for [PromotedStoryListViewModel] and passes
+ * necessary string data.
+ * */
+ override fun createRecentlyPlayedActivityIntent(
+ context: Context,
+ internalProfileId: Int
+ ): Intent {
+ val intent = Intent(context, RecentlyPlayedActivity::class.java)
+ intent.putExtra(
+ RecentlyPlayedActivity.RECENTLY_PLAYED_ACTIVITY_INTERNAL_PROFILE_ID_KEY,
+ internalProfileId
+ )
+ return intent
+ }
+}
diff --git a/app/src/main/java/org/oppia/app/shim/IntentFactoryShimModule.kt b/app/src/main/java/org/oppia/app/shim/IntentFactoryShimModule.kt
new file mode 100644
index 00000000000..3021aa97811
--- /dev/null
+++ b/app/src/main/java/org/oppia/app/shim/IntentFactoryShimModule.kt
@@ -0,0 +1,11 @@
+package org.oppia.app.shim
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface IntentFactoryShimModule {
+
+ @Binds
+ fun provideIntentFactoryShim(intentFactoryShim: IntentFactoryShimImpl): IntentFactoryShim
+}
diff --git a/app/src/main/java/org/oppia/app/shim/ViewBindingShim.kt b/app/src/main/java/org/oppia/app/shim/ViewBindingShim.kt
new file mode 100644
index 00000000000..fa3962a5b77
--- /dev/null
+++ b/app/src/main/java/org/oppia/app/shim/ViewBindingShim.kt
@@ -0,0 +1,163 @@
+package org.oppia.app.shim
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.EditText
+import android.widget.FrameLayout
+import android.widget.ImageButton
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import org.oppia.app.player.state.itemviewmodel.DragDropInteractionContentViewModel
+import org.oppia.app.player.state.itemviewmodel.SelectionInteractionContentViewModel
+import org.oppia.util.parser.HtmlParser
+
+/**
+ * Creates bindings for Views in order to avoid View files directly depending on Binding files.
+ * When working on a View file, developers should refrain from directly referencing Binding files
+ * by adding all related functionality here.
+ *
+ * Please note that this file is temporary and all functionality will be returned to it's respective
+ * View once Gradle has been removed.
+ */
+// TODO(#1619): Remove file post-Gradle
+interface ViewBindingShim {
+
+ /**
+ * Handles binding inflation for [ProfileInputView] and returns the binding's label text.
+ */
+ fun provideProfileInputViewBindingLabelText(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ attachToParent: Boolean
+ ): TextView
+
+ /**
+ * Handles binding inflation for [ProfileInputView] and returns the binding's input.
+ */
+ fun provideProfileInputViewBindingInput(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ attachToParent: Boolean
+ ): EditText
+
+ /**
+ * Handles binding inflation for [ProfileInputView] and returns the binding's error text.
+ */
+ fun provideProfileInputViewBindingErrorText(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ attachToParent: Boolean
+ ): TextView
+
+ /**
+ * Handles binding inflation for [SelectionInteractionView]'s ItemSelectionInteraction and
+ * returns the binding's root.
+ */
+ fun provideSelectionInteractionViewInflatedView(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ attachToParent: Boolean
+ ): View
+
+ /**
+ * Handles binding inflation for [SelectionInteractionView]'s ItemSelectionInteraction and
+ * returns the binding's view model.
+ */
+ fun provideSelectionInteractionViewModel(
+ view: View,
+ viewModel: SelectionInteractionContentViewModel,
+ htmlParserFactory: HtmlParser.Factory,
+ resourceBucketName: String,
+ entityType: String,
+ entityId: String
+ )
+
+ /**
+ * Handles binding inflation for [SelectionInteractionView]'s MultipleChoiceInteraction and
+ * returns the binding's view.
+ */
+ fun provideMultipleChoiceInteractionItemsInflatedView(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ attachToParent: Boolean
+ ): View
+
+ /**
+ * Handles binding inflation for [SelectionInteractionView]'s MultipleChoiceInteraction and
+ * returns the binding's view model.
+ */
+ fun provideMultipleChoiceInteractionItemsViewModel(
+ view: View,
+ viewModel: SelectionInteractionContentViewModel,
+ htmlParserFactory: HtmlParser.Factory,
+ resourceBucketName: String,
+ entityType: String,
+ entityId: String
+ )
+
+ /**
+ * Handles binding inflation for [DragDropSortInteractionView]'s SortInteraction and returns the
+ * binding's view.
+ */
+ fun provideDragDropSortInteractionInflatedView(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ attachToParent: Boolean
+ ): View
+
+ /** Handles setting [DragDropInteractionItemsBinding]. */
+ fun setDragDropInteractionItemsBinding(
+ view: View
+ )
+
+ /** Handles setting [DragDropInteractionItemsBinding]'s adapter. */
+ fun setDragDropInteractionItemsBindingAdapter(
+ adapter: RecyclerView.Adapter
+ )
+
+ /** Returns [DragDropInteractionItemsBinding]'s RecyclerView. */
+ fun getDragDropInteractionItemsBindingRecyclerView(): RecyclerView
+
+ /** Returns [DragDropInteractionItemsBinding]'s dragDropContentGroupItem. */
+ fun getDragDropInteractionItemsBindingGroupItem(): ImageButton
+
+ /** Returns [DragDropInteractionItemsBinding]'s dragDropContentUnlinkItems. */
+ fun getDragDropInteractionItemsBindingUnlinkItems(): ImageButton
+
+ /** Returns [DragDropInteractionItemsBinding]'s dragDropAccessibleContainer. */
+ fun getDragDropInteractionItemsBindingAccessibleContainer(): LinearLayout
+
+ /** Handles setting [DragDropInteractionItemsBinding]'s view model. */
+ fun setDragDropInteractionItemsBindingViewModel(
+ viewModel: DragDropInteractionContentViewModel
+ )
+
+ /**
+ * Handles binding inflation for [DragDropSortInteractionView]'s SingleItemInteraction and returns
+ * the binding's view.
+ */
+ fun provideDragDropSingleItemInflatedView(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ attachToParent: Boolean
+ ): View
+
+ /** Handles setting [DragDropSingleItemBinding]. */
+ fun setDragDropSingleItemBinding(
+ view: View
+ )
+
+ /** Handles setting [DragDropSingleItemBinding]'s html content. */
+ fun setDragDropSingleItemBindingHtmlContent(
+ htmlParserFactory: HtmlParser.Factory,
+ resourceBucketName: String,
+ entityType: String,
+ entityId: String,
+ viewModel: String
+ )
+
+ /** Returns [ClickableAreasImage]'s default region. */
+ fun getDefaultRegion(parentView: FrameLayout): View
+}
diff --git a/app/src/main/java/org/oppia/app/shim/ViewBindingShimImpl.kt b/app/src/main/java/org/oppia/app/shim/ViewBindingShimImpl.kt
new file mode 100644
index 00000000000..19994a8e845
--- /dev/null
+++ b/app/src/main/java/org/oppia/app/shim/ViewBindingShimImpl.kt
@@ -0,0 +1,229 @@
+package org.oppia.app.shim
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.EditText
+import android.widget.FrameLayout
+import android.widget.ImageButton
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.databinding.DataBindingUtil
+import androidx.recyclerview.widget.RecyclerView
+import org.oppia.app.R
+import org.oppia.app.databinding.DragDropInteractionItemsBinding
+import org.oppia.app.databinding.DragDropSingleItemBinding
+import org.oppia.app.databinding.ItemSelectionInteractionItemsBinding
+import org.oppia.app.databinding.MultipleChoiceInteractionItemsBinding
+import org.oppia.app.databinding.ProfileInputViewBinding
+import org.oppia.app.player.state.itemviewmodel.DragDropInteractionContentViewModel
+import org.oppia.app.player.state.itemviewmodel.SelectionInteractionContentViewModel
+import org.oppia.util.parser.HtmlParser
+import javax.inject.Inject
+
+/**
+ * Creates bindings for Views in order to avoid View files directly depending on Binding files.
+ * When working on a View file, developers should refrain from directly referencing Binding files
+ * by adding all related functionality here.
+ *
+ * Please note that this file is temporary and all functionality will be returned to it's respective
+ * View once Gradle has been removed.
+ */
+// TODO(#1619): Remove file post-Gradle
+class ViewBindingShimImpl @Inject constructor() : ViewBindingShim {
+
+ override fun provideProfileInputViewBindingLabelText(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ attachToParent: Boolean
+ ): TextView {
+ val binding = DataBindingUtil.inflate(
+ inflater,
+ R.layout.profile_input_view,
+ parent,
+ attachToParent
+ )
+ return binding.labelText
+ }
+
+ override fun provideProfileInputViewBindingInput(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ attachToParent: Boolean
+ ): EditText {
+ val binding = DataBindingUtil.inflate(
+ inflater,
+ R.layout.profile_input_view,
+ parent,
+ attachToParent
+ )
+ return binding.input
+ }
+
+ override fun provideProfileInputViewBindingErrorText(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ attachToParent: Boolean
+ ): TextView {
+ val binding = DataBindingUtil.inflate(
+ inflater,
+ R.layout.profile_input_view,
+ parent,
+ attachToParent
+ )
+ return binding.errorText
+ }
+
+ override fun provideSelectionInteractionViewInflatedView(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ attachToParent: Boolean
+ ): View {
+ return ItemSelectionInteractionItemsBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ /* attachToParent= */ false
+ ).root
+ }
+
+ override fun provideSelectionInteractionViewModel(
+ view: View,
+ viewModel: SelectionInteractionContentViewModel,
+ htmlParserFactory: HtmlParser.Factory,
+ resourceBucketName: String,
+ entityType: String,
+ entityId: String
+ ) {
+ val binding =
+ DataBindingUtil.findBinding(view)!!
+ binding.htmlContent =
+ htmlParserFactory.create(
+ resourceBucketName,
+ entityType,
+ entityId,
+ false
+ ).parseOppiaHtml(
+ viewModel.htmlContent,
+ binding.itemSelectionContentsTextView
+ )
+ binding.viewModel = viewModel
+ }
+
+ override fun provideMultipleChoiceInteractionItemsInflatedView(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ attachToParent: Boolean
+ ): View {
+ return MultipleChoiceInteractionItemsBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ ).root
+ }
+
+ override fun provideMultipleChoiceInteractionItemsViewModel(
+ view: View,
+ viewModel: SelectionInteractionContentViewModel,
+ htmlParserFactory: HtmlParser.Factory,
+ resourceBucketName: String,
+ entityType: String,
+ entityId: String
+ ) {
+ val binding =
+ DataBindingUtil.findBinding(view)!!
+ binding.htmlContent =
+ htmlParserFactory.create(
+ resourceBucketName, entityType, entityId, /* imageCenterAlign= */ false
+ ).parseOppiaHtml(
+ viewModel.htmlContent, binding.multipleChoiceContentTextView
+ )
+ binding.viewModel = viewModel
+ }
+
+ override fun provideDragDropSortInteractionInflatedView(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ attachToParent: Boolean
+ ): View {
+ return DragDropInteractionItemsBinding.inflate(
+ LayoutInflater.from(parent.context), parent, /* attachToParent= */ false
+ ).root
+ }
+
+ private lateinit var dragDropInteractionItemsBinding: DragDropInteractionItemsBinding
+
+ override fun setDragDropInteractionItemsBinding(
+ view: View
+ ) {
+ dragDropInteractionItemsBinding =
+ DataBindingUtil.findBinding(view)!!
+ }
+
+ override fun setDragDropInteractionItemsBindingAdapter(
+ adapter: RecyclerView.Adapter
+ ) {
+ dragDropInteractionItemsBinding.adapter = adapter
+ }
+
+ override fun getDragDropInteractionItemsBindingRecyclerView(): RecyclerView {
+ return dragDropInteractionItemsBinding.dragDropItemRecyclerview
+ }
+
+ override fun getDragDropInteractionItemsBindingGroupItem(): ImageButton {
+ return dragDropInteractionItemsBinding.dragDropContentGroupItem
+ }
+
+ override fun getDragDropInteractionItemsBindingUnlinkItems(): ImageButton {
+ return dragDropInteractionItemsBinding.dragDropContentUnlinkItems
+ }
+
+ override fun getDragDropInteractionItemsBindingAccessibleContainer(): LinearLayout {
+ return dragDropInteractionItemsBinding.dragDropAccessibleContainer
+ }
+
+ override fun setDragDropInteractionItemsBindingViewModel(
+ viewModel: DragDropInteractionContentViewModel
+ ) {
+ dragDropInteractionItemsBinding.viewModel = viewModel
+ }
+
+ override fun provideDragDropSingleItemInflatedView(
+ inflater: LayoutInflater,
+ parent: ViewGroup,
+ attachToParent: Boolean
+ ): View {
+ return DragDropSingleItemBinding.inflate(
+ LayoutInflater.from(parent.context), parent, /* attachToParent= */ false
+ ).root
+ }
+
+ private lateinit var dragDropSingleItemBinding: DragDropSingleItemBinding
+
+ override fun setDragDropSingleItemBinding(
+ view: View
+ ) {
+ dragDropSingleItemBinding =
+ DataBindingUtil.findBinding(view)!!
+ }
+
+ override fun setDragDropSingleItemBindingHtmlContent(
+ htmlParserFactory: HtmlParser.Factory,
+ resourceBucketName: String,
+ entityType: String,
+ entityId: String,
+ viewModel: String
+ ) {
+ dragDropSingleItemBinding.htmlContent = htmlParserFactory.create(
+ resourceBucketName,
+ entityType,
+ entityId,
+ /* imageCenterAlign= */ false
+ ).parseOppiaHtml(
+ viewModel, dragDropSingleItemBinding.dragDropContentTextView
+ )
+ }
+
+ override fun getDefaultRegion(parentView: FrameLayout): View {
+ return parentView.findViewById(R.id.default_selected_region)
+ }
+}
diff --git a/app/src/main/java/org/oppia/app/shim/ViewBindingShimModule.kt b/app/src/main/java/org/oppia/app/shim/ViewBindingShimModule.kt
new file mode 100644
index 00000000000..870b9789614
--- /dev/null
+++ b/app/src/main/java/org/oppia/app/shim/ViewBindingShimModule.kt
@@ -0,0 +1,11 @@
+package org.oppia.app.shim
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface ViewBindingShimModule {
+
+ @Binds
+ fun provideViewBindingShim(viewBindingShim: ViewBindingShimImpl): ViewBindingShim
+}
diff --git a/app/src/main/java/org/oppia/app/shim/ViewComponentFactory.kt b/app/src/main/java/org/oppia/app/shim/ViewComponentFactory.kt
new file mode 100644
index 00000000000..e8690bcd687
--- /dev/null
+++ b/app/src/main/java/org/oppia/app/shim/ViewComponentFactory.kt
@@ -0,0 +1,11 @@
+package org.oppia.app.shim
+
+import android.view.View
+import org.oppia.app.view.ViewComponent
+
+interface ViewComponentFactory {
+ /**
+ * Returns a new [ViewComponent] for the specified view.
+ */
+ fun createViewComponent(view: View): ViewComponent
+}
diff --git a/app/src/main/java/org/oppia/app/story/StoryFragment.kt b/app/src/main/java/org/oppia/app/story/StoryFragment.kt
index 0d1b878a918..f49ab4dd98e 100644
--- a/app/src/main/java/org/oppia/app/story/StoryFragment.kt
+++ b/app/src/main/java/org/oppia/app/story/StoryFragment.kt
@@ -13,7 +13,7 @@ private const val KEY_TOPIC_ID_ARGUMENT = "TOPIC_ID"
private const val KEY_STORY_ID_ARGUMENT = "STORY_ID"
/** Fragment for displaying a story. */
-class StoryFragment : InjectableFragment(), ExplorationSelectionListener {
+class StoryFragment : InjectableFragment(), ExplorationSelectionListener, StoryFragmentScroller {
companion object {
/** Returns a new [StoryFragment] to display the story corresponding to the specified story ID. */
fun newInstance(internalProfileId: Int, topicId: String, storyId: String): StoryFragment {
@@ -77,7 +77,7 @@ class StoryFragment : InjectableFragment(), ExplorationSelectionListener {
)
}
- fun smoothScrollToPosition(position: Int) {
+ override fun smoothScrollToPosition(position: Int) {
storyFragmentPresenter.smoothScrollToPosition(position)
}
}
diff --git a/app/src/main/java/org/oppia/app/story/StoryFragmentScroller.kt b/app/src/main/java/org/oppia/app/story/StoryFragmentScroller.kt
new file mode 100644
index 00000000000..84f438dc3cb
--- /dev/null
+++ b/app/src/main/java/org/oppia/app/story/StoryFragmentScroller.kt
@@ -0,0 +1,9 @@
+package org.oppia.app.story
+
+interface StoryFragmentScroller {
+ /**
+ * Scrolls smoothly (with animation) to the specified vertical pixel position in
+ * [StoryFragment].
+ * */
+ fun smoothScrollToPosition(position: Int)
+}
diff --git a/app/src/main/java/org/oppia/app/story/StoryViewModel.kt b/app/src/main/java/org/oppia/app/story/StoryViewModel.kt
index a2db4863150..200e96d8eeb 100644
--- a/app/src/main/java/org/oppia/app/story/StoryViewModel.kt
+++ b/app/src/main/java/org/oppia/app/story/StoryViewModel.kt
@@ -19,7 +19,7 @@ import org.oppia.util.logging.ConsoleLogger
import org.oppia.util.parser.StoryHtmlParserEntityType
import javax.inject.Inject
-/** The ViewModel for [StoryFragment]. */
+/** The ViewModel for StoryFragment. */
@FragmentScope
class StoryViewModel @Inject constructor(
private val fragment: Fragment,
@@ -83,7 +83,7 @@ class StoryViewModel @Inject constructor(
val chapterList: List = storySummary.chapterList
for (position in chapterList.indices) {
if (storySummary.chapterList[position].chapterPlayState == ChapterPlayState.NOT_STARTED) {
- (fragment as StoryFragment).smoothScrollToPosition(position + 1)
+ (fragment as StoryFragmentScroller).smoothScrollToPosition(position + 1)
break
}
}
diff --git a/app/src/main/java/org/oppia/app/story/storyitemviewmodel/StoryChapterSummaryViewModel.kt b/app/src/main/java/org/oppia/app/story/storyitemviewmodel/StoryChapterSummaryViewModel.kt
index bd2098ec8ab..46427df2a66 100644
--- a/app/src/main/java/org/oppia/app/story/storyitemviewmodel/StoryChapterSummaryViewModel.kt
+++ b/app/src/main/java/org/oppia/app/story/storyitemviewmodel/StoryChapterSummaryViewModel.kt
@@ -5,7 +5,6 @@ import androidx.lifecycle.Observer
import org.oppia.app.model.ChapterSummary
import org.oppia.app.model.LessonThumbnail
import org.oppia.app.story.ExplorationSelectionListener
-import org.oppia.app.story.StoryFragment
import org.oppia.domain.exploration.ExplorationDataController
import org.oppia.util.data.AsyncResult
import org.oppia.util.logging.ConsoleLogger
diff --git a/app/src/main/java/org/oppia/app/story/storyitemviewmodel/StoryHeaderViewModel.kt b/app/src/main/java/org/oppia/app/story/storyitemviewmodel/StoryHeaderViewModel.kt
index f2caa9b114b..a0430576b2f 100644
--- a/app/src/main/java/org/oppia/app/story/storyitemviewmodel/StoryHeaderViewModel.kt
+++ b/app/src/main/java/org/oppia/app/story/storyitemviewmodel/StoryHeaderViewModel.kt
@@ -1,7 +1,5 @@
package org.oppia.app.story.storyitemviewmodel
-import org.oppia.app.story.StoryFragment
-
/** Header view model for the recycler view in [StoryFragment]. */
class StoryHeaderViewModel(
val completedChapters: Int,
diff --git a/app/src/main/java/org/oppia/app/story/storyitemviewmodel/StoryItemViewModel.kt b/app/src/main/java/org/oppia/app/story/storyitemviewmodel/StoryItemViewModel.kt
index 2aadbd85d32..f4d4a51ee54 100644
--- a/app/src/main/java/org/oppia/app/story/storyitemviewmodel/StoryItemViewModel.kt
+++ b/app/src/main/java/org/oppia/app/story/storyitemviewmodel/StoryItemViewModel.kt
@@ -1,6 +1,5 @@
package org.oppia.app.story.storyitemviewmodel
-import org.oppia.app.story.StoryFragment
import org.oppia.app.viewmodel.ObservableViewModel
/** Super-class for generalising different views for the recyclerView in [StoryFragment] */
diff --git a/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardFragmentPresenter.kt b/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardFragmentPresenter.kt
index dfe6da2bb42..1078ae4763a 100644
--- a/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardFragmentPresenter.kt
+++ b/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardFragmentPresenter.kt
@@ -33,10 +33,11 @@ class ConceptCardFragmentPresenter @Inject constructor(
container,
/* attachToRoot= */ false
)
+ val view = binding.conceptCardExplanationText
val viewModel = getConceptCardViewModel()
skillId = id
- viewModel.setSkillIdAndBinding(skillId, binding)
+ viewModel.setSkillIdAndBinding(skillId, view)
logConceptCardEvent(skillId)
binding.conceptCardToolbar.setNavigationIcon(R.drawable.ic_close_white_24dp)
diff --git a/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardViewModel.kt b/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardViewModel.kt
index 401164989cb..6b8445ffedc 100644
--- a/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardViewModel.kt
+++ b/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardViewModel.kt
@@ -1,9 +1,9 @@
package org.oppia.app.topic.conceptcard
+import android.widget.TextView
import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
-import org.oppia.app.databinding.ConceptCardFragmentBinding
import org.oppia.app.fragment.FragmentScope
import org.oppia.app.model.ConceptCard
import org.oppia.domain.topic.TopicController
@@ -14,6 +14,7 @@ import org.oppia.util.parser.ConceptCardHtmlParserEntityType
import org.oppia.util.parser.HtmlParser
import javax.inject.Inject
+// TODO(#1633): Fix ViewModel to not depend on View
/** [ViewModel] for concept card, providing rich text and worked examples */
@FragmentScope
class ConceptCardViewModel @Inject constructor(
@@ -24,7 +25,7 @@ class ConceptCardViewModel @Inject constructor(
@DefaultResourceBucketName private val resourceBucketName: String
) : ViewModel() {
private lateinit var skillId: String
- private lateinit var binding: ConceptCardFragmentBinding
+ private lateinit var view: TextView
val conceptCardLiveData: LiveData by lazy {
processConceptCardLiveData()
@@ -35,9 +36,9 @@ class ConceptCardViewModel @Inject constructor(
}
/** Sets the value of skillId and binding. Must be called before setting ViewModel to binding */
- fun setSkillIdAndBinding(id: String, binding: ConceptCardFragmentBinding) {
+ fun setSkillIdAndBinding(id: String, view: TextView) {
skillId = id
- this.binding = binding
+ this.view = view
}
private val conceptCardResultLiveData: LiveData> by lazy {
@@ -74,6 +75,6 @@ class ConceptCardViewModel @Inject constructor(
val conceptCard = conceptCardResult.getOrDefault(ConceptCard.getDefaultInstance())
return htmlParserFactory
.create(resourceBucketName, entityType, skillId, /* imageCenterAlign= */true)
- .parseOppiaHtml(conceptCard.explanation.html, binding.conceptCardExplanationText)
+ .parseOppiaHtml(conceptCard.explanation.html, view)
}
}
diff --git a/app/src/main/java/org/oppia/app/topic/practice/practiceitemviewmodel/TopicPracticeFooterViewModel.kt b/app/src/main/java/org/oppia/app/topic/practice/practiceitemviewmodel/TopicPracticeFooterViewModel.kt
index db366b85c85..9eefef94158 100644
--- a/app/src/main/java/org/oppia/app/topic/practice/practiceitemviewmodel/TopicPracticeFooterViewModel.kt
+++ b/app/src/main/java/org/oppia/app/topic/practice/practiceitemviewmodel/TopicPracticeFooterViewModel.kt
@@ -1,6 +1,4 @@
package org.oppia.app.topic.practice.practiceitemviewmodel
-import org.oppia.app.topic.practice.TopicPracticeFragment
-
/** Footer view model for the recycler view in [TopicPracticeFragment]. */
class TopicPracticeFooterViewModel : TopicPracticeItemViewModel()
diff --git a/app/src/main/java/org/oppia/app/topic/practice/practiceitemviewmodel/TopicPracticeHeaderViewModel.kt b/app/src/main/java/org/oppia/app/topic/practice/practiceitemviewmodel/TopicPracticeHeaderViewModel.kt
index 63deda3477e..32b30195836 100644
--- a/app/src/main/java/org/oppia/app/topic/practice/practiceitemviewmodel/TopicPracticeHeaderViewModel.kt
+++ b/app/src/main/java/org/oppia/app/topic/practice/practiceitemviewmodel/TopicPracticeHeaderViewModel.kt
@@ -1,6 +1,4 @@
package org.oppia.app.topic.practice.practiceitemviewmodel
-import org.oppia.app.topic.practice.TopicPracticeFragment
-
/** Header view model for the recycler view in [TopicPracticeFragment]. */
class TopicPracticeHeaderViewModel : TopicPracticeItemViewModel()
diff --git a/app/src/main/java/org/oppia/app/topic/practice/practiceitemviewmodel/TopicPracticeItemViewModel.kt b/app/src/main/java/org/oppia/app/topic/practice/practiceitemviewmodel/TopicPracticeItemViewModel.kt
index b68f741264b..9d17c4bc894 100644
--- a/app/src/main/java/org/oppia/app/topic/practice/practiceitemviewmodel/TopicPracticeItemViewModel.kt
+++ b/app/src/main/java/org/oppia/app/topic/practice/practiceitemviewmodel/TopicPracticeItemViewModel.kt
@@ -1,6 +1,5 @@
package org.oppia.app.topic.practice.practiceitemviewmodel
-import org.oppia.app.topic.practice.TopicPracticeFragment
import org.oppia.app.viewmodel.ObservableViewModel
/** Super-class for generalising different views for the recyclerView in [TopicPracticeFragment] */
diff --git a/app/src/main/java/org/oppia/app/topic/practice/practiceitemviewmodel/TopicPracticeSubtopicViewModel.kt b/app/src/main/java/org/oppia/app/topic/practice/practiceitemviewmodel/TopicPracticeSubtopicViewModel.kt
index a2eca76bfba..986d36f7fb2 100644
--- a/app/src/main/java/org/oppia/app/topic/practice/practiceitemviewmodel/TopicPracticeSubtopicViewModel.kt
+++ b/app/src/main/java/org/oppia/app/topic/practice/practiceitemviewmodel/TopicPracticeSubtopicViewModel.kt
@@ -1,7 +1,6 @@
package org.oppia.app.topic.practice.practiceitemviewmodel
import org.oppia.app.model.Subtopic
-import org.oppia.app.topic.practice.TopicPracticeFragment
/** Subtopic view model for the recycler view in [TopicPracticeFragment]. */
class TopicPracticeSubtopicViewModel(val subtopic: Subtopic) : TopicPracticeItemViewModel()
diff --git a/app/src/main/java/org/oppia/app/topic/questionplayer/QuestionPlayerFragmentPresenter.kt b/app/src/main/java/org/oppia/app/topic/questionplayer/QuestionPlayerFragmentPresenter.kt
index a8810b7c77d..abf9e8b8478 100644
--- a/app/src/main/java/org/oppia/app/topic/questionplayer/QuestionPlayerFragmentPresenter.kt
+++ b/app/src/main/java/org/oppia/app/topic/questionplayer/QuestionPlayerFragmentPresenter.kt
@@ -164,7 +164,11 @@ class QuestionPlayerFragmentPresenter @Inject constructor(
fun onSubmitButtonClicked() {
hideKeyboard()
- handleSubmitAnswer(questionViewModel.getPendingAnswer(recyclerViewAssembler))
+ handleSubmitAnswer(
+ questionViewModel.getPendingAnswer(
+ recyclerViewAssembler::getPendingAnswerHandler
+ )
+ )
}
fun onResponsesHeaderClicked() {
diff --git a/app/src/main/java/org/oppia/app/topic/questionplayer/QuestionPlayerViewModel.kt b/app/src/main/java/org/oppia/app/topic/questionplayer/QuestionPlayerViewModel.kt
index 7ba089dae5b..fc0607bc5cc 100644
--- a/app/src/main/java/org/oppia/app/topic/questionplayer/QuestionPlayerViewModel.kt
+++ b/app/src/main/java/org/oppia/app/topic/questionplayer/QuestionPlayerViewModel.kt
@@ -5,8 +5,8 @@ import androidx.databinding.ObservableBoolean
import androidx.databinding.ObservableField
import androidx.databinding.ObservableList
import org.oppia.app.model.UserAnswer
-import org.oppia.app.player.state.StatePlayerRecyclerViewAssembler
import org.oppia.app.player.state.answerhandling.AnswerErrorCategory
+import org.oppia.app.player.state.answerhandling.InteractionAnswerHandler
import org.oppia.app.player.state.itemviewmodel.StateItemViewModel
import org.oppia.app.viewmodel.ObservableViewModel
import javax.inject.Inject
@@ -43,25 +43,30 @@ class QuestionPlayerViewModel @Inject constructor() : ObservableViewModel() {
fun getCanSubmitAnswer(): ObservableField = canSubmitAnswer
fun getPendingAnswer(
- recyclerViewAssembler: StatePlayerRecyclerViewAssembler
+ retrieveAnswerHandler: (List) -> InteractionAnswerHandler?
): UserAnswer {
- return getPendingAnswerWithoutError(recyclerViewAssembler) ?: UserAnswer.getDefaultInstance()
+ return getPendingAnswerWithoutError(
+ retrieveAnswerHandler(
+ getAnswerItemList()
+ )
+ ) ?: UserAnswer.getDefaultInstance()
}
private fun getPendingAnswerWithoutError(
- recyclerViewAssembler: StatePlayerRecyclerViewAssembler
+ answerHandler: InteractionAnswerHandler?
): UserAnswer? {
- val items = if (isSplitView.get() == true) {
- rightItemList
- } else {
- itemList
- }
- val answerHandler = recyclerViewAssembler
- .getPendingAnswerHandler(items)
return if (answerHandler?.checkPendingAnswerError(AnswerErrorCategory.SUBMIT_TIME) == null) {
answerHandler?.getPendingAnswer()
} else {
null
}
}
+
+ private fun getAnswerItemList(): List {
+ return if (isSplitView.get() == true) {
+ rightItemList
+ } else {
+ itemList
+ }
+ }
}
diff --git a/app/src/main/java/org/oppia/app/topic/revision/revisionitemviewmodel/TopicRevisionItemViewModel.kt b/app/src/main/java/org/oppia/app/topic/revision/revisionitemviewmodel/TopicRevisionItemViewModel.kt
index 3882ad1c300..0c066dea9ad 100644
--- a/app/src/main/java/org/oppia/app/topic/revision/revisionitemviewmodel/TopicRevisionItemViewModel.kt
+++ b/app/src/main/java/org/oppia/app/topic/revision/revisionitemviewmodel/TopicRevisionItemViewModel.kt
@@ -3,7 +3,6 @@ package org.oppia.app.topic.revision.revisionitemviewmodel
import androidx.lifecycle.ViewModel
import org.oppia.app.model.Subtopic
import org.oppia.app.topic.revision.RevisionSubtopicSelector
-import org.oppia.app.topic.revision.TopicRevisionFragment
/** [ViewModel] for child views of recycler view present in the [TopicRevisionFragment]. */
class TopicRevisionItemViewModel(
diff --git a/app/src/main/java/org/oppia/app/topic/revisioncard/RevisionCardFragmentPresenter.kt b/app/src/main/java/org/oppia/app/topic/revisioncard/RevisionCardFragmentPresenter.kt
index 14da3f71517..189b3f7e186 100755
--- a/app/src/main/java/org/oppia/app/topic/revisioncard/RevisionCardFragmentPresenter.kt
+++ b/app/src/main/java/org/oppia/app/topic/revisioncard/RevisionCardFragmentPresenter.kt
@@ -33,9 +33,10 @@ class RevisionCardFragmentPresenter @Inject constructor(
container,
/* attachToRoot= */ false
)
+ val view = binding.revisionCardExplanationText
val viewModel = getReviewCardViewModel()
- viewModel.setSubtopicIdAndBinding(topicId, subtopicId, binding)
+ viewModel.setSubtopicIdAndBinding(topicId, subtopicId, view)
logRevisionCardEvent(topicId, subtopicId)
binding.let {
diff --git a/app/src/main/java/org/oppia/app/topic/revisioncard/RevisionCardViewModel.kt b/app/src/main/java/org/oppia/app/topic/revisioncard/RevisionCardViewModel.kt
index 3d028ad46d0..644582ef312 100755
--- a/app/src/main/java/org/oppia/app/topic/revisioncard/RevisionCardViewModel.kt
+++ b/app/src/main/java/org/oppia/app/topic/revisioncard/RevisionCardViewModel.kt
@@ -1,11 +1,11 @@
package org.oppia.app.topic.revisioncard
import android.view.View
+import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
-import org.oppia.app.databinding.RevisionCardFragmentBinding
import org.oppia.app.fragment.FragmentScope
import org.oppia.app.model.RevisionCard
import org.oppia.domain.topic.TopicController
@@ -16,6 +16,7 @@ import org.oppia.util.parser.HtmlParser
import org.oppia.util.parser.TopicHtmlParserEntityType
import javax.inject.Inject
+// TODO(#1633): Fix ViewModel to not depend on View
/** [ViewModel] for revision card, providing rich text and worked examples */
@FragmentScope
class RevisionCardViewModel @Inject constructor(
@@ -28,7 +29,8 @@ class RevisionCardViewModel @Inject constructor(
) : ViewModel() {
private lateinit var topicId: String
private var subtopicId: Int = 0
- private lateinit var binding: RevisionCardFragmentBinding
+ private lateinit var view: TextView
+
private val returnToTopicClickListener: ReturnToTopicClickListener =
activity as ReturnToTopicClickListener
@@ -44,11 +46,11 @@ class RevisionCardViewModel @Inject constructor(
fun setSubtopicIdAndBinding(
topicId: String,
subtopicId: Int,
- binding: RevisionCardFragmentBinding
+ view: TextView
) {
this.topicId = topicId
this.subtopicId = subtopicId
- this.binding = binding
+ this.view = view
}
private val revisionCardResultLiveData: LiveData> by lazy {
@@ -73,7 +75,8 @@ class RevisionCardViewModel @Inject constructor(
RevisionCard.getDefaultInstance()
)
return htmlParserFactory.create(
+
resourceBucketName, entityType, topicId, /* imageCenterAlign= */ true
- ).parseOppiaHtml(revisionCard.pageContents.html, binding.revisionCardExplanationText)
+ ).parseOppiaHtml(revisionCard.pageContents.html, view)
}
}
diff --git a/app/src/main/java/org/oppia/app/utility/ClickableAreasImage.kt b/app/src/main/java/org/oppia/app/utility/ClickableAreasImage.kt
index 2fe85b8ddb3..8e47124d9de 100644
--- a/app/src/main/java/org/oppia/app/utility/ClickableAreasImage.kt
+++ b/app/src/main/java/org/oppia/app/utility/ClickableAreasImage.kt
@@ -9,6 +9,7 @@ import androidx.core.view.isVisible
import org.oppia.app.R
import org.oppia.app.model.ImageWithRegions
import org.oppia.app.player.state.ImageRegionSelectionInteractionView
+import org.oppia.app.shim.ViewBindingShim
import kotlin.math.roundToInt
/**
@@ -17,7 +18,8 @@ import kotlin.math.roundToInt
class ClickableAreasImage(
private val imageView: ImageRegionSelectionInteractionView,
private val parentView: FrameLayout,
- private val listener: OnClickableAreaClickedListener
+ private val listener: OnClickableAreaClickedListener,
+ private val bindingInterface: ViewBindingShim
) {
init {
imageView.setOnTouchListener { view, motionEvent ->
@@ -27,6 +29,7 @@ class ClickableAreasImage(
return@setOnTouchListener false
}
}
+
/**
* Called when an image is clicked.
*
@@ -38,7 +41,7 @@ class ClickableAreasImage(
// Show default region for non-accessibility cases and this will be only called when user taps on unspecified region.
if (!imageView.isAccessibilityEnabled()) {
resetRegionSelectionViews()
- val defaultRegion = parentView.findViewById(R.id.default_selected_region)
+ val defaultRegion = bindingInterface.getDefaultRegion(parentView)
defaultRegion.setBackgroundResource(R.drawable.selected_region_background)
defaultRegion.x = x
defaultRegion.y = y
@@ -107,7 +110,7 @@ class ClickableAreasImage(
}
if (imageView.isAccessibilityEnabled()) {
// Make default region visibility gone when talkback enabled to avoid any accidental touch.
- val defaultRegion = parentView.findViewById(R.id.default_selected_region)
+ val defaultRegion = bindingInterface.getDefaultRegion(parentView)
defaultRegion.isVisible = false
newView.setOnClickListener {
showOrHideRegion(newView, clickableArea)
diff --git a/app/src/main/java/org/oppia/app/view/ViewComponent.kt b/app/src/main/java/org/oppia/app/view/ViewComponent.kt
index d31e5702813..18db73bdd04 100644
--- a/app/src/main/java/org/oppia/app/view/ViewComponent.kt
+++ b/app/src/main/java/org/oppia/app/view/ViewComponent.kt
@@ -7,6 +7,7 @@ import org.oppia.app.customview.LessonThumbnailImageView
import org.oppia.app.player.state.DragDropSortInteractionView
import org.oppia.app.player.state.ImageRegionSelectionInteractionView
import org.oppia.app.player.state.SelectionInteractionView
+import org.oppia.app.profile.ProfileInputView
/** Root subcomponent for custom views. */
@Subcomponent
@@ -23,5 +24,6 @@ interface ViewComponent {
fun inject(selectionInteractionView: SelectionInteractionView)
fun inject(dragDropSortInteractionView: DragDropSortInteractionView)
fun inject(imageRegionSelectionInteractionView: ImageRegionSelectionInteractionView)
+ fun inject(profileInputView: ProfileInputView)
fun inject(lessonThumbnailImageView: LessonThumbnailImageView)
}
diff --git a/app/src/main/res/layout-land/selection_interaction_item.xml b/app/src/main/res/layout-land/selection_interaction_item.xml
index 0ce0352db23..f6ed2067455 100644
--- a/app/src/main/res/layout-land/selection_interaction_item.xml
+++ b/app/src/main/res/layout-land/selection_interaction_item.xml
@@ -6,7 +6,7 @@
-
+
-
+
-
+
-
+