Skip to content

Commit

Permalink
Fix part of #42: Exploration player base (Part 1) #42 (#100)
Browse files Browse the repository at this point in the history
* Exploration Player base code for activity and fragment

* Fake Data Provider

* Nit changes mainly in documentation

* Nit changes mainly in documentation for State too

* Changed Controller to Presenter and optimised the fragment transaction code

* Basic code for ExporationActivityTest

* ExplorationActivity dummy test

* Nit changes

* Code checking and refactoring in ExplorationActivityTest
  • Loading branch information
rt4914 authored Sep 18, 2019
1 parent 2a21f62 commit d256804
Show file tree
Hide file tree
Showing 14 changed files with 246 additions and 12 deletions.
15 changes: 3 additions & 12 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/OppiaTheme">
<activity android:name=".player.exploration.ExplorationActivity"/>
<activity android:name="org.oppia.app.home.HomeActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/org/oppia/app/activity/ActivityComponent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import dagger.BindsInstance
import dagger.Subcomponent
import org.oppia.app.fragment.FragmentComponent
import org.oppia.app.home.HomeActivity
import org.oppia.app.player.exploration.ExplorationActivity
import javax.inject.Provider

/** Root subcomponent for all activities. */
Expand All @@ -19,5 +20,6 @@ interface ActivityComponent {

fun getFragmentComponentBuilderProvider(): Provider<FragmentComponent.Builder>

fun inject(explorationActivity: ExplorationActivity)
fun inject(homeActivity: HomeActivity)
}
4 changes: 4 additions & 0 deletions app/src/main/java/org/oppia/app/fragment/FragmentComponent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import androidx.fragment.app.Fragment
import dagger.BindsInstance
import dagger.Subcomponent
import org.oppia.app.home.HomeFragment
import org.oppia.app.player.exploration.ExplorationFragment
import org.oppia.app.player.state.StateFragment

/** Root subcomponent for all fragments. */
@Subcomponent
Expand All @@ -15,5 +17,7 @@ interface FragmentComponent {
fun build(): FragmentComponent
}

fun inject(explorationFragment: ExplorationFragment)
fun inject(homeFragment: HomeFragment)
fun inject(stateFragment: StateFragment)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.oppia.app.player.exploration

import android.os.Bundle
import org.oppia.app.activity.InjectableAppCompatActivity
import javax.inject.Inject

/** The starting point for exploration. */
class ExplorationActivity : InjectableAppCompatActivity() {
@Inject lateinit var explorationActivityPresenter: ExplorationActivityPresenter

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityComponent.inject(this)
explorationActivityPresenter.handleOnCreate()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.oppia.app.player.exploration

import androidx.appcompat.app.AppCompatActivity
import org.oppia.app.R
import org.oppia.app.activity.ActivityScope
import org.oppia.app.home.HomeFragment
import javax.inject.Inject

/** The controller for [ExplorationActivity]. */
@ActivityScope
class ExplorationActivityPresenter @Inject constructor(private val activity: AppCompatActivity) {
fun handleOnCreate() {
activity.setContentView(R.layout.exploration_activity)
if (getExplorationFragment() == null) {
activity.supportFragmentManager.beginTransaction().add(
R.id.exploration_fragment_placeholder,
ExplorationFragment()
).commitNow()
}
}

private fun getExplorationFragment(): ExplorationFragment? {
return activity.supportFragmentManager.findFragmentById(R.id.exploration_fragment_placeholder) as ExplorationFragment?
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.oppia.app.player.exploration

import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import org.oppia.app.fragment.InjectableFragment
import javax.inject.Inject

/** Fragment that contains displays single exploration. */
class ExplorationFragment : InjectableFragment() {
@Inject lateinit var explorationFragmentPresenter: ExplorationFragmentPresenter

override fun onAttach(context: Context?) {
super.onAttach(context)
fragmentComponent.inject(this)
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return explorationFragmentPresenter.handleCreateView(inflater, container)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.oppia.app.player.exploration

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import org.oppia.app.databinding.ExplorationFragmentBinding
import org.oppia.app.fragment.FragmentScope
import javax.inject.Inject

/** The controller for [ExplorationFragment]. */
@FragmentScope
class ExplorationFragmentPresenter @Inject constructor(
private val fragment: Fragment
) {
fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View? {
return ExplorationFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false).root
}
}
23 changes: 23 additions & 0 deletions app/src/main/java/org/oppia/app/player/state/StateFragment.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.oppia.app.player.state

import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import org.oppia.app.fragment.InjectableFragment
import javax.inject.Inject

/** Fragment that represents the current state card of an exploration. */
class StateFragment : InjectableFragment() {
@Inject lateinit var stateFragmentPresenter: StateFragmentPresenter

override fun onAttach(context: Context?) {
super.onAttach(context)
fragmentComponent.inject(this)
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return stateFragmentPresenter.handleCreateView(inflater, container)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.oppia.app.player.state

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import org.oppia.app.databinding.StateFragmentBinding
import org.oppia.app.fragment.FragmentScope
import javax.inject.Inject

/** The controller for [StateFragment]. */
@FragmentScope
class StateFragmentPresenter @Inject constructor(
private val fragment: Fragment
) {
fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View? {
return StateFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false).root
}
}
8 changes: 8 additions & 0 deletions app/src/main/res/layout/exploration_activity.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/exploration_fragment_placeholder"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".player.exploration.ExplorationActivity" />
19 changes: 19 additions & 0 deletions app/src/main/res/layout/exploration_fragment.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:id="@+id/dummy_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is dummy TextView for testing"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
10 changes: 10 additions & 0 deletions app/src/main/res/layout/state_fragment.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android">

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package org.oppia.app.player.exploration

import android.app.Application
import android.content.Context
import android.os.Handler
import android.os.Looper
import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.Espresso
import androidx.test.espresso.IdlingRegistry
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.idling.CountingIdlingResource
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.ext.junit.runners.AndroidJUnit4
import dagger.BindsInstance
import dagger.Component
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.oppia.app.R
import org.oppia.util.threading.BackgroundDispatcher
import org.oppia.util.threading.BlockingDispatcher
import java.util.concurrent.AbstractExecutorService
import java.util.concurrent.TimeUnit
import javax.inject.Singleton

/** Tests for [ExplorationActivity]. */
@RunWith(AndroidJUnit4::class)
class ExplorationActivityTest {

@Test
fun testExplorationActivity_loadExplorationFragment_hasDummyString() {
ActivityScenario.launch(ExplorationActivity::class.java).use {
Espresso.onView(ViewMatchers.withId(R.id.dummy_text_view))
.check(ViewAssertions.matches(ViewMatchers.withText("This is dummy TextView for testing")))
}
}

@Module
class TestModule {
@Provides
@Singleton
fun provideContext(application: Application): Context {
return application
}

// TODO(#89): Introduce a proper IdlingResource for background dispatchers to ensure they all complete before
// proceeding in an Espresso test. This solution should also be interoperative with Robolectric contexts by using a
// test coroutine dispatcher.

@Singleton
@Provides
@BackgroundDispatcher
fun provideBackgroundDispatcher(@BlockingDispatcher blockingDispatcher: CoroutineDispatcher): CoroutineDispatcher {
return blockingDispatcher
}
}

@Singleton
@Component(modules = [TestModule::class])
interface TestApplicationComponent {
@Component.Builder
interface Builder {
@BindsInstance
fun setApplication(application: Application): Builder

fun build(): TestApplicationComponent
}
}
}

0 comments on commit d256804

Please sign in to comment.