-
Notifications
You must be signed in to change notification settings - Fork 532
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix #3651: [RTL] High-fi rotation animation for icon in Topic lesson …
…and Hints and Solution (#3652) * Update selection_interaction_item.xml * Update selection_interaction_item.xml * Update return_to_topic_button_item.xml * Update return_to_topic_button_item.xml * Fixed rotation animation for icon in Topic lesson and Hints and Solution * Optimized code * Update ViewBindingAdaptersTest.kt * Fixed tests * Fixed unwanted code changes * Update accessibility_label_exemptions.textproto * fixed nit
- Loading branch information
Showing
8 changed files
with
304 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
app/src/main/java/org/oppia/android/app/testing/ViewBindingAdaptersTestActivity.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package org.oppia.android.app.testing | ||
|
||
import android.os.Bundle | ||
import org.oppia.android.R | ||
import org.oppia.android.app.activity.InjectableAppCompatActivity | ||
|
||
/** Test activity for ViewBindingAdapters. */ | ||
class ViewBindingAdaptersTestActivity : InjectableAppCompatActivity() { | ||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
activityComponent.inject(this) | ||
setContentView(R.layout.activity_view_binding_adapters_test) | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
app/src/main/res/layout/activity_view_binding_adapters_test.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<layout xmlns:android="http://schemas.android.com/apk/res/android"> | ||
|
||
<FrameLayout | ||
android:id="@+id/expand_list_icon" | ||
android:layout_width="match_parent" | ||
android:layout_height="wrap_content" | ||
android:layout_marginStart="8dp" | ||
android:layout_marginEnd="8dp" | ||
android:layout_marginBottom="8dp"> | ||
|
||
<ImageView | ||
android:id="@+id/test_drop_down_icon" | ||
android:layout_width="48dp" | ||
android:layout_height="48dp" | ||
android:layout_gravity="center_vertical" | ||
android:padding="8dp" | ||
android:src="@drawable/ic_arrow_drop_down_black_24dp" /> | ||
</FrameLayout> | ||
</layout> |
246 changes: 246 additions & 0 deletions
246
app/src/sharedTest/java/org/oppia/android/app/databinding/ViewBindingAdaptersTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,246 @@ | ||
package org.oppia.android.app.databinding | ||
|
||
import android.app.Activity | ||
import android.app.Application | ||
import android.content.Context | ||
import android.content.Intent | ||
import android.widget.ImageView | ||
import androidx.appcompat.app.AppCompatActivity | ||
import androidx.core.view.ViewCompat | ||
import androidx.test.core.app.ActivityScenario | ||
import androidx.test.core.app.ApplicationProvider | ||
import androidx.test.espresso.intent.Intents | ||
import androidx.test.ext.junit.rules.ActivityScenarioRule | ||
import androidx.test.ext.junit.runners.AndroidJUnit4 | ||
import com.google.common.truth.Truth.assertThat | ||
import dagger.Component | ||
import org.junit.After | ||
import org.junit.Before | ||
import org.junit.Rule | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
import org.mockito.ArgumentCaptor | ||
import org.mockito.Mockito.mock | ||
import org.mockito.Mockito.verify | ||
import org.oppia.android.R | ||
import org.oppia.android.app.activity.ActivityComponent | ||
import org.oppia.android.app.application.ActivityComponentFactory | ||
import org.oppia.android.app.application.ApplicationComponent | ||
import org.oppia.android.app.application.ApplicationInjector | ||
import org.oppia.android.app.application.ApplicationInjectorProvider | ||
import org.oppia.android.app.application.ApplicationModule | ||
import org.oppia.android.app.application.ApplicationStartupListenerModule | ||
import org.oppia.android.app.databinding.ViewBindingAdapters.setRotationAnimation | ||
import org.oppia.android.app.devoptions.DeveloperOptionsModule | ||
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule | ||
import org.oppia.android.app.player.state.hintsandsolution.HintsAndSolutionConfigModule | ||
import org.oppia.android.app.shim.ViewBindingShimModule | ||
import org.oppia.android.app.testing.ViewBindingAdaptersTestActivity | ||
import org.oppia.android.app.topic.PracticeTabModule | ||
import org.oppia.android.domain.classify.InteractionsModule | ||
import org.oppia.android.domain.classify.rules.continueinteraction.ContinueModule | ||
import org.oppia.android.domain.classify.rules.dragAndDropSortInput.DragDropSortInputModule | ||
import org.oppia.android.domain.classify.rules.fractioninput.FractionInputModule | ||
import org.oppia.android.domain.classify.rules.imageClickInput.ImageClickInputModule | ||
import org.oppia.android.domain.classify.rules.itemselectioninput.ItemSelectionInputModule | ||
import org.oppia.android.domain.classify.rules.multiplechoiceinput.MultipleChoiceInputModule | ||
import org.oppia.android.domain.classify.rules.numberwithunits.NumberWithUnitsRuleModule | ||
import org.oppia.android.domain.classify.rules.numericinput.NumericInputRuleModule | ||
import org.oppia.android.domain.classify.rules.ratioinput.RatioInputModule | ||
import org.oppia.android.domain.classify.rules.textinput.TextInputRuleModule | ||
import org.oppia.android.domain.exploration.lightweightcheckpointing.ExplorationStorageModule | ||
import org.oppia.android.domain.onboarding.ExpirationMetaDataRetrieverModule | ||
import org.oppia.android.domain.oppialogger.LogStorageModule | ||
import org.oppia.android.domain.oppialogger.loguploader.LogUploadWorkerModule | ||
import org.oppia.android.domain.oppialogger.loguploader.WorkManagerConfigurationModule | ||
import org.oppia.android.domain.platformparameter.PlatformParameterModule | ||
import org.oppia.android.domain.question.QuestionModule | ||
import org.oppia.android.domain.topic.PrimeTopicAssetsControllerModule | ||
import org.oppia.android.testing.TestImageLoaderModule | ||
import org.oppia.android.testing.TestLogReportingModule | ||
import org.oppia.android.testing.robolectric.RobolectricModule | ||
import org.oppia.android.testing.threading.TestDispatcherModule | ||
import org.oppia.android.testing.time.FakeOppiaClockModule | ||
import org.oppia.android.util.accessibility.AccessibilityTestModule | ||
import org.oppia.android.util.caching.testing.CachingTestModule | ||
import org.oppia.android.util.gcsresource.GcsResourceModule | ||
import org.oppia.android.util.logging.LoggerModule | ||
import org.oppia.android.util.logging.firebase.FirebaseLogUploaderModule | ||
import org.oppia.android.util.parser.html.HtmlParserEntityTypeModule | ||
import org.oppia.android.util.parser.image.ImageParsingModule | ||
import org.robolectric.annotation.Config | ||
import org.robolectric.annotation.LooperMode | ||
import javax.inject.Inject | ||
import javax.inject.Singleton | ||
|
||
/** Default value for float comparison. */ | ||
private const val TOLERANCE = 1e-5f | ||
|
||
/** Tests for [MarginBindingAdapters]. */ | ||
@RunWith(AndroidJUnit4::class) | ||
@LooperMode(LooperMode.Mode.PAUSED) | ||
@Config(application = ViewBindingAdaptersTest.TestApplication::class, qualifiers = "port-xxhdpi") | ||
class ViewBindingAdaptersTest { | ||
|
||
@Inject | ||
lateinit var context: Context | ||
|
||
@get:Rule | ||
var activityRule: ActivityScenarioRule<ViewBindingAdaptersTestActivity> = | ||
ActivityScenarioRule( | ||
Intent( | ||
ApplicationProvider.getApplicationContext(), | ||
ViewBindingAdaptersTestActivity::class.java | ||
) | ||
) | ||
|
||
@Before | ||
fun setUp() { | ||
setUpTestApplicationComponent() | ||
Intents.init() | ||
} | ||
|
||
@After | ||
fun tearDown() { | ||
Intents.release() | ||
} | ||
|
||
@Config(qualifiers = "port") | ||
@Test | ||
fun testViewBindingAdapters_ltrIsEnabled_antiClockwise_rotationAngleForLtrIsCorrect() { | ||
val imageViewDropDown = activityRule.scenario.runWithActivity { | ||
val imageViewDropDown: ImageView = it.findViewById(R.id.test_drop_down_icon) | ||
setRotationAnimation( | ||
imageViewDropDown, | ||
/* isClockwise= */ false, | ||
/* angle= */ 180f | ||
) | ||
return@runWithActivity imageViewDropDown | ||
} | ||
assertThat(imageViewDropDown.rotation).isWithin(TOLERANCE).of(180f) | ||
} | ||
|
||
@Config(qualifiers = "port") | ||
@Test | ||
fun testViewBindingAdapters_ltrIsEnabled_clockwise_rotationAngleForLtrIsCorrect() { | ||
val imageViewDropDown = activityRule.scenario.runWithActivity { | ||
val imageViewDropDown: ImageView = it.findViewById(R.id.test_drop_down_icon) | ||
setRotationAnimation( | ||
imageViewDropDown, | ||
/* isClockwise= */ true, | ||
/* angle= */ 180f | ||
) | ||
return@runWithActivity imageViewDropDown | ||
} | ||
assertThat(imageViewDropDown.rotation).isWithin(TOLERANCE).of(0f) | ||
} | ||
|
||
@Config(qualifiers = "port") | ||
@Test | ||
fun testViewBindingAdapters_rtlIsEnabled_clockwise_rotationAngleForRtlIsCorrect() { | ||
val imageViewDropDown = activityRule.scenario.runWithActivity { | ||
val imageViewDropDown: ImageView = it.findViewById(R.id.test_drop_down_icon) | ||
ViewCompat.setLayoutDirection(imageViewDropDown, ViewCompat.LAYOUT_DIRECTION_RTL) | ||
setRotationAnimation( | ||
imageViewDropDown, | ||
/* isClockwise= */ true, | ||
/* angle= */ 180f | ||
) | ||
return@runWithActivity imageViewDropDown | ||
} | ||
assertThat(imageViewDropDown.rotation).isWithin(TOLERANCE).of(360f) | ||
} | ||
|
||
@Config(qualifiers = "port") | ||
@Test | ||
fun testViewBindingAdapters_rtlIsEnabled_antiClockwise_rotationAngleForRtlIsCorrect() { | ||
val imageViewDropDown = activityRule.scenario.runWithActivity { | ||
val imageViewDropDown: ImageView = it.findViewById(R.id.test_drop_down_icon) | ||
ViewCompat.setLayoutDirection(imageViewDropDown, ViewCompat.LAYOUT_DIRECTION_RTL) | ||
setRotationAnimation( | ||
imageViewDropDown, | ||
/* isClockwise= */ false, | ||
/* angle= */ 180f | ||
) | ||
return@runWithActivity imageViewDropDown | ||
} | ||
assertThat(imageViewDropDown.rotation).isWithin(TOLERANCE).of(180f) | ||
} | ||
|
||
private inline fun <reified V, A : Activity> ActivityScenario<A>.runWithActivity( | ||
crossinline action: (A) -> V | ||
): V { | ||
// Use Mockito to ensure the routine is actually executed before returning the result. | ||
@Suppress("UNCHECKED_CAST") // The unsafe cast is necessary to make the routine generic. | ||
val fakeMock: Consumer<V> = mock(Consumer::class.java) as Consumer<V> | ||
val valueCaptor = ArgumentCaptor.forClass(V::class.java) | ||
onActivity { fakeMock.consume(action(it)) } | ||
verify(fakeMock).consume(valueCaptor.capture()) | ||
return valueCaptor.value | ||
} | ||
|
||
private fun setUpTestApplicationComponent() { | ||
ApplicationProvider.getApplicationContext<TestApplication>().inject(this) | ||
} | ||
|
||
// TODO(#59): Figure out a way to reuse modules instead of needing to re-declare them. | ||
@Singleton | ||
@Component( | ||
modules = [ | ||
RobolectricModule::class, | ||
PlatformParameterModule::class, | ||
TestDispatcherModule::class, ApplicationModule::class, | ||
LoggerModule::class, ContinueModule::class, FractionInputModule::class, | ||
ItemSelectionInputModule::class, MultipleChoiceInputModule::class, | ||
NumberWithUnitsRuleModule::class, NumericInputRuleModule::class, TextInputRuleModule::class, | ||
DragDropSortInputModule::class, ImageClickInputModule::class, InteractionsModule::class, | ||
GcsResourceModule::class, TestImageLoaderModule::class, ImageParsingModule::class, | ||
HtmlParserEntityTypeModule::class, QuestionModule::class, TestLogReportingModule::class, | ||
AccessibilityTestModule::class, LogStorageModule::class, CachingTestModule::class, | ||
PrimeTopicAssetsControllerModule::class, ExpirationMetaDataRetrieverModule::class, | ||
ViewBindingShimModule::class, RatioInputModule::class, | ||
ApplicationStartupListenerModule::class, LogUploadWorkerModule::class, | ||
WorkManagerConfigurationModule::class, HintsAndSolutionConfigModule::class, | ||
FirebaseLogUploaderModule::class, FakeOppiaClockModule::class, PracticeTabModule::class, | ||
DeveloperOptionsStarterModule::class, DeveloperOptionsModule::class, | ||
ExplorationStorageModule::class | ||
] | ||
) | ||
/** Create a TestApplicationComponent. */ | ||
interface TestApplicationComponent : ApplicationComponent { | ||
/** Build the TestApplicationComponent. */ | ||
@Component.Builder | ||
interface Builder : ApplicationComponent.Builder | ||
|
||
/** Inject [ViewBindingAdaptersTest] in TestApplicationComponent . */ | ||
fun inject(ViewBindingAdaptersTest: ViewBindingAdaptersTest) | ||
} | ||
|
||
/** | ||
* Class to override a dependency throughout the test application, instead of overriding the | ||
* dependencies in every test class, we can just do it once by extending the Application class. | ||
*/ | ||
class TestApplication : Application(), ActivityComponentFactory, ApplicationInjectorProvider { | ||
private val component: TestApplicationComponent by lazy { | ||
DaggerViewBindingAdaptersTest_TestApplicationComponent.builder() | ||
.setApplication(this) | ||
.build() as TestApplicationComponent | ||
} | ||
|
||
/** Inject [ViewBindingAdaptersTest] in TestApplicationComponent . */ | ||
fun inject(viewBindingAdapters: ViewBindingAdaptersTest) { | ||
component.inject(viewBindingAdapters) | ||
} | ||
|
||
override fun createActivityComponent(activity: AppCompatActivity): ActivityComponent { | ||
return component.getActivityComponentBuilderProvider().get().setActivity(activity).build() | ||
} | ||
|
||
override fun getApplicationInjector(): ApplicationInjector = component | ||
} | ||
|
||
private interface Consumer<T> { | ||
/** Represents an operation that accepts a single input argument and returns no result. */ | ||
fun consume(value: T) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters