diff --git a/Jenkinsfile b/Jenkinsfile index 21b3636a90..d50d6169af 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -27,7 +27,25 @@ pipeline { parameters { string name: 'DEBUG_LABEL', defaultValue: '', description: 'For debugging when entered will be used as label to decide on which slaves the jobs will run.' booleanParam name: 'BUILD_WITH_CATROID', defaultValue: false, description: 'When checked then the current Paintroid build will be built with the current develop branch of Catroid' + booleanParam name: 'DEVICE_TESTING', defaultValue: true, description: 'When selected UI-testing runs locally' string name: 'CATROID_BRANCH', defaultValue: 'develop', description: 'The branch which to build catroid with, when BUILD_WITH_CATROID is checked.' + separator(name: "BROWSERSTACK", sectionHeader: "BrowserStack configuration", + separatorStyle: "border-width: 0", + sectionHeaderStyle: """ + background-color: #ffff00; + text-align: center; + padding: 4px; + color: #000000; + font-size: 20px; + font-weight: normal; + font-family: 'Orienta', sans-serif; + letter-spacing: 1px; + font-style: italic; + """) + choice choices: ['AndroidDevices', 'Samsung Galaxy S23-13.0', 'Samsung Galaxy S23 Ultra-13.0', 'Samsung Galaxy S22 Ultra-12.0', 'Samsung Galaxy S22 Plus-12.0', 'Samsung Galaxy S22-12.0', 'Samsung Galaxy S21-12.0', 'Samsung Galaxy S21 Ultra-11.0', 'Samsung Galaxy S21-11.0', 'Samsung Galaxy S21 Plus-11.0', 'Samsung Galaxy S20-10.0', 'Samsung Galaxy S20 Plus-10.0', 'Samsung Galaxy S20 Ultra-10.0', 'Samsung Galaxy M52-11.0', 'Samsung Galaxy M32-11.0', 'Samsung Galaxy A52-11.0', 'Samsung Galaxy Note 20 Ultra-10.0', 'Samsung Galaxy Note 20-10.0', 'Samsung Galaxy A51-10.0', 'Samsung Galaxy A11-10.0', 'Samsung Galaxy S10e-9.0', 'Samsung Galaxy S10 Plus-9.0', 'Samsung Galaxy S10-9.0', 'Samsung Galaxy Note 10 Plus-9.0', 'Samsung Galaxy Note 10-9.0', 'Samsung Galaxy A10-9.0', 'Samsung Galaxy Note 9-8.1', 'Samsung Galaxy J7 Prime-8.1', 'Samsung Galaxy S9 Plus-9.0', 'Samsung Galaxy S9 Plus-8.0', 'Samsung Galaxy S9-8.0', 'Samsung Galaxy Note 8-7.1', 'Samsung Galaxy A8-7.1', 'Samsung Galaxy S8 Plus-7.0', 'Samsung Galaxy S8-7.0', 'Samsung Galaxy S7-6.0', 'Samsung Galaxy S6-5.0', 'Samsung Galaxy Tab S8-12.0', 'Samsung Galaxy Tab S7-11.0', 'Samsung Galaxy Tab S7-10.0', 'Samsung Galaxy Tab S6-9.0', 'Samsung Galaxy Tab S5e-9.0', 'Samsung Galaxy Tab S4-8.1', 'Google Pixel 7 Pro-13.0', 'Google Pixel 7-13.0', 'Google Pixel 6 Pro-13.0', 'Google Pixel 6 Pro-12.0', 'Google Pixel 6-12.0', 'Google Pixel 5-12.0', 'Google Pixel 5-11.0', 'Google Pixel 4-11.0', 'Google Pixel 4 XL-10.0', 'Google Pixel 4-10.0', 'Google Pixel 3-10.0', 'Google Pixel 3a XL-9.0', 'Google Pixel 3a-9.0', 'Google Pixel 3 XL-9.0', 'Google Pixel 3-9.0', 'Google Pixel 2-9.0', 'Google Pixel 2-8.0', 'Google Pixel-7.1', 'Google Nexus 5-4.4', 'OnePlus 9-11.0', 'OnePlus 8-10.0', 'OnePlus 7T-10.0', 'OnePlus 7-9.0', 'OnePlus 6T-9.0', 'Xiaomi Redmi Note 11-11.0', 'Xiaomi Redmi Note 9-10.0', 'Xiaomi Redmi Note 8-9.0', 'Xiaomi Redmi Note 7-9.0', 'Motorola Moto G71 5G-11.0', 'Motorola Moto G9 Play-10.0', 'Motorola Moto G7 Play-9.0', 'Vivo Y21-11.0', 'Vivo Y50-10.0', 'Oppo Reno 6-11.0', 'Oppo A96-11.0', 'Oppo Reno 3 Pro-10.0', 'Huawei P30-9.0'], description: 'Available Android Devices on BrowserStack', name: 'BROWSERSTACK_ANDROID_DEVICES' + booleanParam name: 'BROWSERSTACK_TESTING', defaultValue: false, description: 'When selected testing runs over Browserstack' + choice choices: ['1', '2', '3', '4',' 5'], description: 'Number of Shards for running tests on BrowserStack. BrowserStack Dashboard', name: 'BROWSERSTACK_SHARDS' + } agent { @@ -39,6 +57,8 @@ pipeline { } } + tools {nodejs "NodeJS"} + options { timeout(time: 2, unit: 'HOURS') timestamps() @@ -59,13 +79,22 @@ pipeline { } } + stage('Build Test-APK') { + steps { + sh "./gradlew -Pindependent='#$env.BUILD_NUMBER $env.BRANCH_NAME' assembleAndroidTest" + archiveArtifacts 'Paintroid/build/outputs/apk/androidTest/debug/*.apk' + renameApks("${env.BRANCH_NAME}-${env.BUILD_NUMBER}") + plot csvFileName: 'dexcount.csv', csvSeries: [[displayTableFlag: false, exclusionValues: '', file: 'Paintroid/build/outputs/dexcount/*.csv', inclusionFlag: 'OFF', url: '']], group: 'APK Stats', numBuilds: '180', style: 'line', title: 'dexcount' + } + } + stage('Build with Catroid') { when { expression { params.BUILD_WITH_CATROID } } - + steps { sh './gradlew publishToMavenLocal -Psnapshot' sh 'rm -rf Catroid; mkdir Catroid' @@ -100,6 +129,18 @@ pipeline { } } + stage('Browserstack testing') { + when { + expression { params.BROWSERSTACK_TESTING == true } + } + steps { + withCredentials([usernamePassword(credentialsId: 'browserstack', passwordVariable: 'BROWSERSTACK_ACCESS_KEY', usernameVariable: 'BROWSERSTACK_USERNAME')]) { + script { + browserStack('app/build/outputs/apk/debug/', 'Paintroid/build/outputs/apk/androidTest/debug/', 'Paintroid') + } + } + } + } stage('Tests') { stages { stage('Unit Tests') { @@ -114,6 +155,9 @@ pipeline { } stage('Device Tests') { + when { + expression { params.DEVICE_TESTING == true } + } steps { sh "echo no | avdmanager create avd --force --name android28 --package 'system-images;android-28;default;x86_64' --skin 1080x1920" sh "/home/user/android/sdk/emulator/emulator -no-window -no-boot-anim -noaudio -avd android28 > /dev/null 2>&1 &" @@ -146,4 +190,4 @@ pipeline { notifyChat() } } -} +} \ No newline at end of file diff --git a/Jenkinsfile.releaseAPK b/Jenkinsfile.releaseAPK index c7f11e1a73..1669e33d9b 100644 --- a/Jenkinsfile.releaseAPK +++ b/Jenkinsfile.releaseAPK @@ -111,7 +111,8 @@ pipeline { ./gradlew publishToMavenLocal ''' } - archiveArtifacts artifacts: '**/*-release.aar' + archiveArtifacts artifacts: 'Paintroid/build/outputs/aar/Paintroid-signedRelease.aar' + archiveArtifacts artifacts: 'colorpicker/build/outputs/aar/colorpicker-signedRelease.aar' } } } diff --git a/Paintroid/build.gradle b/Paintroid/build.gradle index 3bc0faf935..e2bf156a11 100644 --- a/Paintroid/build.gradle +++ b/Paintroid/build.gradle @@ -125,7 +125,7 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.8' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8' implementation 'androidx.exifinterface:exifinterface:1.3.2' - implementation 'com.esotericsoftware:kryo:5.1.1' + implementation 'com.esotericsoftware:kryo:5.5.0' implementation 'id.zelory:compressor:2.1.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/BitmapIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/BitmapIntegrationTest.kt index e6604ea10a..87c63ea891 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/BitmapIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/BitmapIntegrationTest.kt @@ -24,7 +24,7 @@ package org.catrobat.paintroid.test.espresso import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.rule.ActivityTestRule import org.catrobat.paintroid.MainActivity -import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.Companion.onToolBarView import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule import org.catrobat.paintroid.tools.ToolType import org.junit.Assert diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/CatrobatImageIOIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/CatrobatImageIOIntegrationTest.kt index d7fc4ca301..86ee6283b3 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/CatrobatImageIOIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/CatrobatImageIOIntegrationTest.kt @@ -42,7 +42,7 @@ import org.catrobat.paintroid.common.CATROBAT_IMAGE_ENDING import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider import org.catrobat.paintroid.test.espresso.util.EspressoUtils import org.catrobat.paintroid.test.espresso.util.UiInteractions -import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.Companion.onDrawingSurfaceView import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule import org.hamcrest.Matchers @@ -101,7 +101,7 @@ class CatrobatImageIOIntegrationTest { Espresso.onData( AllOf.allOf( Matchers.`is`(Matchers.instanceOf(String::class.java)), - Matchers.`is`(FileIO.FileType.CATROBAT.value) + Matchers.`is`(FileIO.FileType.CATROBAT.value) ) ).inRoot(RootMatchers.isPlatformPopup()).perform(ViewActions.click()) onView(withId(R.id.pocketpaint_image_name_save_text)) diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/LandscapeIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/LandscapeIntegrationTest.kt index e7ead874bc..55ecc3cb85 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/LandscapeIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/LandscapeIntegrationTest.kt @@ -47,11 +47,11 @@ import org.catrobat.paintroid.colorpicker.PresetSelectorView import org.catrobat.paintroid.colorpicker.RgbSelectorView import org.catrobat.paintroid.test.espresso.util.UiMatcher.withBackground import org.catrobat.paintroid.test.espresso.util.UiMatcher.withBackgroundColor -import org.catrobat.paintroid.test.espresso.util.wrappers.BottomNavigationViewInteraction.onBottomNavigationView -import org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerViewInteraction.onColorPickerView -import org.catrobat.paintroid.test.espresso.util.wrappers.OptionsMenuViewInteraction.onOptionsMenu -import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView -import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.BottomNavigationViewInteraction.Companion.onBottomNavigationView +import org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerViewInteraction.Companion.onColorPickerView +import org.catrobat.paintroid.test.espresso.util.wrappers.OptionsMenuViewInteraction.Companion.onOptionsMenu +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.Companion.onToolBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.Companion.onTopBarView import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule import org.catrobat.paintroid.tools.Tool import org.catrobat.paintroid.tools.ToolType diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/LayerBackgroundTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/LayerBackgroundTest.kt new file mode 100644 index 0000000000..bfbf53ff64 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/LayerBackgroundTest.kt @@ -0,0 +1,226 @@ +package org.catrobat.paintroid.test.espresso + +import android.content.res.Configuration +import android.graphics.drawable.Drawable +import androidx.test.rule.ActivityTestRule +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.contract.LayerContracts +import org.catrobat.paintroid.test.espresso.util.wrappers.LayerMenuViewInteraction +import org.catrobat.paintroid.ui.LayerAdapter.Companion.getBottomBackground +import org.catrobat.paintroid.ui.LayerAdapter.Companion.getCenterBackground +import org.catrobat.paintroid.ui.LayerAdapter.Companion.getSingleBackground +import org.catrobat.paintroid.ui.LayerAdapter.Companion.getTopBackground +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import java.util.Locale + +@RunWith(Parameterized::class) +class LayerBackgroundTest(private val language: String) { + + private lateinit var mainActivity: MainActivity + private lateinit var layerAdapter: LayerContracts.Adapter + + companion object { + private const val ENGLISH = "en" + private const val ARABIC = "ar" + + @JvmStatic + @Parameterized.Parameters(name = "Language: {0}") + fun data() = arrayOf( + arrayOf(ARABIC), + arrayOf(ENGLISH) + ) + } + + @get:Rule + var launchActivityRule = ActivityTestRule(MainActivity::class.java) + + @Before + fun setUp() { + mainActivity = launchActivityRule.activity + layerAdapter = mainActivity.layerAdapter + setLanguage(language) + } + + @Test + fun testOneLayer() { + var actualBackground = getActualBackground(0) + Assert.assertEquals(actualBackground, getSingleBackground()?.constantState) + } + + @Test + fun testTwoLayersTopSelected() { + LayerMenuViewInteraction.onLayerMenuView() + .performOpen() + .performAddLayer() + + var backgroundTop = getActualBackground(0) + var backgroundBottom = getActualBackground(1) + + Assert.assertEquals(backgroundTop, getTopBackground(true)?.constantState) + Assert.assertEquals(backgroundBottom, getBottomBackground(false)?.constantState) + } + + @Test + fun testTwoLayersBottomSelected() { + LayerMenuViewInteraction.onLayerMenuView() + .performOpen() + .performAddLayer() + .performSelectLayer(1) + + var backgroundTop = getActualBackground(0) + var backgroundBottom = getActualBackground(1) + + Assert.assertEquals(backgroundTop, getTopBackground(false)?.constantState) + Assert.assertEquals(backgroundBottom, getBottomBackground(true)?.constantState) + } + + @Test + fun testThreeLayersTopSelected() { + LayerMenuViewInteraction.onLayerMenuView() + .performOpen() + .performAddLayer() + .performAddLayer() + + var backgroundTop = getActualBackground(0) + var backgroundCenter = getActualBackground(1) + var backgroundBottom = getActualBackground(2) + + Assert.assertEquals(backgroundTop, getTopBackground(true)?.constantState) + Assert.assertEquals(backgroundCenter, getCenterBackground(false)?.constantState) + Assert.assertEquals(backgroundBottom, getBottomBackground(false)?.constantState) + } + + @Test + fun testThreeLayersCenterSelected() { + LayerMenuViewInteraction.onLayerMenuView() + .performOpen() + .performAddLayer() + .performAddLayer() + .performSelectLayer(1) + + var backgroundTop = getActualBackground(0) + var backgroundCenter = getActualBackground(1) + var backgroundBottom = getActualBackground(2) + + Assert.assertEquals(backgroundTop, getTopBackground(false)?.constantState) + Assert.assertEquals(backgroundCenter, getCenterBackground(true)?.constantState) + Assert.assertEquals(backgroundBottom, getBottomBackground(false)?.constantState) + } + + @Test + fun testThreeLayersBottomSelected() { + LayerMenuViewInteraction.onLayerMenuView() + .performOpen() + .performAddLayer() + .performAddLayer() + .performScrollToPositionInLayerNavigation(2) + .performSelectLayer(2) + + var backgroundTop = getActualBackground(0) + var backgroundCenter = getActualBackground(1) + var backgroundBottom = getActualBackground(2) + + Assert.assertEquals(backgroundTop, getTopBackground(false)?.constantState) + Assert.assertEquals(backgroundCenter, getCenterBackground(false)?.constantState) + Assert.assertEquals(backgroundBottom, getBottomBackground(true)?.constantState) + } + + @Test + fun testFourLayersTopSelected() { + LayerMenuViewInteraction.onLayerMenuView() + .performOpen() + .performAddLayer() + .performAddLayer() + .performAddLayer() + + val backgroundTop = getActualBackground(0) + val backgroundUpperCenter = getActualBackground(1) + val backgroundLowerCenter = getActualBackground(2) + val backgroundBottom = getActualBackground(3) + + Assert.assertEquals(backgroundTop, getTopBackground(true)?.constantState) + Assert.assertEquals(backgroundUpperCenter, getCenterBackground(false)?.constantState) + Assert.assertEquals(backgroundLowerCenter, getCenterBackground(false)?.constantState) + Assert.assertEquals(backgroundBottom, getBottomBackground(false)?.constantState) + } + + @Test + fun testFourLayersUpperCenterSelected() { + LayerMenuViewInteraction.onLayerMenuView() + .performOpen() + .performAddLayer() + .performAddLayer() + .performAddLayer() + .performSelectLayer(1) + + val backgroundTop = getActualBackground(0) + val backgroundUpperCenter = getActualBackground(1) + val backgroundLowerCenter = getActualBackground(2) + val backgroundBottom = getActualBackground(3) + + Assert.assertEquals(backgroundTop, getTopBackground(false)?.constantState) + Assert.assertEquals(backgroundUpperCenter, getCenterBackground(true)?.constantState) + Assert.assertEquals(backgroundLowerCenter, getCenterBackground(false)?.constantState) + Assert.assertEquals(backgroundBottom, getBottomBackground(false)?.constantState) + } + + @Test + fun testFourLayersLowerCenterSelected() { + LayerMenuViewInteraction.onLayerMenuView() + .performOpen() + .performAddLayer() + .performAddLayer() + .performAddLayer() + .performScrollToPositionInLayerNavigation(2) + .performSelectLayer(2) + + val backgroundTop = getActualBackground(0) + val backgroundUpperCenter = getActualBackground(1) + val backgroundLowerCenter = getActualBackground(2) + val backgroundBottom = getActualBackground(3) + + Assert.assertEquals(backgroundTop, getTopBackground(false)?.constantState) + Assert.assertEquals(backgroundUpperCenter, getCenterBackground(false)?.constantState) + Assert.assertEquals(backgroundLowerCenter, getCenterBackground(true)?.constantState) + Assert.assertEquals(backgroundBottom, getBottomBackground(false)?.constantState) + } + + @Test + fun testFourLayersBottomSelected() { + LayerMenuViewInteraction.onLayerMenuView() + .performOpen() + .performAddLayer() + .performAddLayer() + .performAddLayer() + .performScrollToPositionInLayerNavigation(3) + .performSelectLayer(3) + + val backgroundTop = getActualBackground(0) + val backgroundUpperCenter = getActualBackground(1) + val backgroundLowerCenter = getActualBackground(2) + val backgroundBottom = getActualBackground(3) + + Assert.assertEquals(backgroundTop, getTopBackground(false)?.constantState) + Assert.assertEquals(backgroundUpperCenter, getCenterBackground(false)?.constantState) + Assert.assertEquals(backgroundLowerCenter, getCenterBackground(false)?.constantState) + Assert.assertEquals(backgroundBottom, getBottomBackground(true)?.constantState) + } + + private fun getActualBackground(position: Int): Drawable.ConstantState? { + val layout = layerAdapter?.getViewHolderAt(position)?.getViewLayout() + return layout?.background?.constantState + } + + private fun setLanguage(language: String) { + val locale = Locale(language) + Locale.setDefault(locale) + val config: Configuration = mainActivity.resources.configuration + config.setLocale(locale) + mainActivity.resources.updateConfiguration(config, mainActivity.resources.displayMetrics) + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/LayerIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/LayerIntegrationTest.kt index b810e2c571..937b5a21ad 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/LayerIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/LayerIntegrationTest.kt @@ -57,8 +57,8 @@ import org.catrobat.paintroid.test.espresso.util.UiMatcher import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction import org.catrobat.paintroid.test.espresso.util.wrappers.LayerMenuViewInteraction import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction -import org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction import org.catrobat.paintroid.test.espresso.util.wrappers.TransformToolOptionsViewInteraction import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule import org.catrobat.paintroid.test.utils.ToastMatcher diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MainActivityIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MainActivityIntegrationTest.kt index d06ff41309..b30e20c97a 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MainActivityIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MainActivityIntegrationTest.kt @@ -43,7 +43,7 @@ import org.catrobat.paintroid.contract.MainActivityContracts.Interactor import org.catrobat.paintroid.contract.MainActivityContracts.MainView import org.catrobat.paintroid.controller.ToolController import org.catrobat.paintroid.presenter.MainActivityPresenter -import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.Companion.onTopBarView import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule import org.catrobat.paintroid.tools.Workspace import org.junit.Before diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MainActivityIntentTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MainActivityIntentTest.java deleted file mode 100644 index 666b389024..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MainActivityIntentTest.java +++ /dev/null @@ -1,123 +0,0 @@ -package org.catrobat.paintroid.test.espresso; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.graphics.Bitmap; -import android.graphics.Color; -import android.net.Uri; -import android.os.Build; -import android.os.Environment; -import android.provider.MediaStore; - -import org.catrobat.paintroid.MainActivity; -import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider; -import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; -import org.catrobat.paintroid.tools.ToolType; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.MockitoAnnotations; - -import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt; -import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Objects; - -import androidx.test.espresso.intent.Intents; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.ActivityTestRule; - -@RunWith(AndroidJUnit4.class) -public class MainActivityIntentTest { - - private static ArrayList deletionFileList = null; - @Rule - public ActivityTestRule launchActivityTestRule = new ActivityTestRule<>(MainActivity.class); - private ContentResolver contentResolver; - - @Before - public void setUp() { - deletionFileList = new ArrayList<>(); - contentResolver = launchActivityTestRule.getActivity().getContentResolver(); - } - - @After - public void tearDown() { - for (File file : deletionFileList) { - if (file != null && file.exists()) { - assertTrue(file.delete()); - } - } - } - - @Test - public void testAppliedChangesAfterOrientationChangePersist() { - MockitoAnnotations.initMocks(this); - Intents.init(); - - Uri testUri = createTestImageFile(); - - Intent intent = new Intent(Intent.ACTION_SEND) - .setType("image/*") - .putExtra(Intent.EXTRA_STREAM, testUri); - - launchActivityTestRule.launchActivity(intent); - - assertNull(launchActivityTestRule.getActivity().model.getSavedPictureUri()); - assertNull(launchActivityTestRule.getActivity().model.getCameraImageUri()); - - onDrawingSurfaceView() - .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); - - onToolBarView().performSelectTool(ToolType.FILL); - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); - - launchActivityTestRule.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); - - Intents.release(); - } - - private Uri createTestImageFile() { - Bitmap bitmap = Bitmap.createBitmap(400, 400, Bitmap.Config.ARGB_8888); - - ContentValues contentValues = new ContentValues(); - contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, "testfile.jpeg"); - contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES); - } - - Uri imageUri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues); - try { - OutputStream fos = contentResolver.openOutputStream(Objects.requireNonNull(imageUri)); - assertTrue(bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)); - assert fos != null; - fos.close(); - } catch (IOException e) { - throw new AssertionError("Picture file could not be created.", e); - } - - File imageFile = new File(imageUri.getPath(), "testfile.jpeg"); - deletionFileList.add(imageFile); - return imageUri; - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MainActivityIntentTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MainActivityIntentTest.kt new file mode 100644 index 0000000000..00216f9ec8 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MainActivityIntentTest.kt @@ -0,0 +1,108 @@ +@file:Suppress("DEPRECATION") + +package org.catrobat.paintroid.test.espresso + +import android.content.ContentResolver +import android.content.ContentValues +import android.content.Intent +import android.content.pm.ActivityInfo +import android.graphics.Bitmap +import android.graphics.Color +import android.net.Uri +import android.os.Build +import android.os.Environment +import android.provider.MediaStore +import androidx.test.espresso.intent.Intents +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.ActivityTestRule +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider +import org.catrobat.paintroid.test.espresso.util.UiInteractions +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction +import org.catrobat.paintroid.tools.ToolType +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.MockitoAnnotations +import java.io.File +import java.io.IOException +import java.util.Objects + +@RunWith(AndroidJUnit4::class) +class MainActivityIntentTest { + @get:Rule + var launchActivityTestRule = ActivityTestRule(MainActivity::class.java) + private var contentResolver: ContentResolver? = null + @Before + fun setUp() { + deletionFileList = ArrayList() + contentResolver = launchActivityTestRule.activity.contentResolver + } + + @After + fun tearDown() { + for (file in deletionFileList) { + if (file.exists()) { + Assert.assertTrue(file.delete()) + } + } + } + + @Test + fun testAppliedChangesAfterOrientationChangePersist() { + MockitoAnnotations.initMocks(this) + Intents.init() + val testUri = createTestImageFile() + val intent = Intent(Intent.ACTION_SEND) + .setType("image/*") + .putExtra(Intent.EXTRA_STREAM, testUri) + launchActivityTestRule.launchActivity(intent) + Assert.assertNull(launchActivityTestRule.activity.model.savedPictureUri) + Assert.assertNull(launchActivityTestRule.activity.model.cameraImageUri) + DrawingSurfaceInteraction.onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + ToolBarViewInteraction.onToolBarView().performSelectTool(ToolType.FILL) + DrawingSurfaceInteraction.onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + DrawingSurfaceInteraction.onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + launchActivityTestRule.activity.requestedOrientation = + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + DrawingSurfaceInteraction.onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + Intents.release() + } + + private fun createTestImageFile(): Uri? { + val bitmap = Bitmap.createBitmap(400, 400, Bitmap.Config.ARGB_8888) + val contentValues = ContentValues() + contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, "testfile.jpeg") + contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg") + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES) + } + val imageUri = + contentResolver?.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues) + try { + val fos = Objects.requireNonNull(imageUri) + ?.let { contentResolver?.openOutputStream(it) } + Assert.assertTrue(bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)) + assert(fos != null) + fos?.close() + } catch (e: IOException) { + throw AssertionError("Picture file could not be created.", e) + } + val imageFile = File(imageUri?.path, "testfile.jpeg") + deletionFileList.add(imageFile) + return imageUri + } + + companion object { + private lateinit var deletionFileList: ArrayList + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MenuFileActivityIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MenuFileActivityIntegrationTest.kt index 2b9948f7e8..b618d4e31a 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MenuFileActivityIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MenuFileActivityIntegrationTest.kt @@ -62,9 +62,9 @@ import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider. import org.catrobat.paintroid.test.espresso.util.EspressoUtils.grantPermissionRulesVersionCheck import org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt import org.catrobat.paintroid.test.espresso.util.UiInteractions.waitFor -import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.Companion.onDrawingSurfaceView import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction -import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.Companion.onTopBarView import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule import org.catrobat.paintroid.tools.ToolType import org.hamcrest.Matchers.`is` @@ -381,7 +381,7 @@ class MenuFileActivityIntegrationTest { onView(withId(R.id.pocketpaint_image_name_save_text)).check(matches(isDisplayed())) onView(withId(R.id.pocketpaint_save_dialog_spinner)).check(matches(isDisplayed())) onView(withId(R.id.pocketpaint_save_dialog_spinner)).perform(click()) - onData(allOf(`is`(instanceOf(String::class.java)), `is`("png"))) + onData(allOf(`is`(instanceOf(String::class.java)), `is`("png"))) .inRoot(RootMatchers.isPlatformPopup()).perform(click()) onView(withId(R.id.pocketpaint_image_name_save_text)) .perform(replaceText(defaultFileName)) @@ -393,7 +393,7 @@ class MenuFileActivityIntegrationTest { onTopBarView().performOpenMoreOptions() onView(withText(R.string.menu_save_image)).perform(click()) onView(withId(R.id.pocketpaint_save_dialog_spinner)).perform(click()) - onData(allOf(`is`(instanceOf(String::class.java)), `is`("jpg"))) + onData(allOf(`is`(instanceOf(String::class.java)), `is`("jpg"))) .inRoot(RootMatchers.isPlatformPopup()).perform(click()) onView(withId(R.id.pocketpaint_image_name_save_text)) .perform(replaceText(defaultFileName)) @@ -447,7 +447,7 @@ class MenuFileActivityIntegrationTest { val imageName = "test12345" onView(withId(R.id.pocketpaint_image_name_save_text)).perform(replaceText(imageName)) onView(withId(R.id.pocketpaint_save_dialog_spinner)).perform(click()) - onData(allOf(`is`(instanceOf(String::class.java)), `is`("png"))) + onData(allOf(`is`(instanceOf(String::class.java)), `is`("png"))) .inRoot(RootMatchers.isPlatformPopup()).perform(click()) onView(withText(R.string.save_button_text)).perform(click()) onView(isRoot()).perform(waitFor(100)) @@ -468,7 +468,7 @@ class MenuFileActivityIntegrationTest { onView(withText("png")).check(matches(isDisplayed())) onView(withText("image$imageNumber")).check(matches(isDisplayed())) onView(withId(R.id.pocketpaint_save_dialog_spinner)).perform(click()) - onData(allOf(`is`(instanceOf(String::class.java)), `is`("png"))) + onData(allOf(`is`(instanceOf(String::class.java)), `is`("png"))) .inRoot(RootMatchers.isPlatformPopup()).perform(click()) onView(withText(R.string.save_button_text)).perform(click()) onView(isRoot()).perform(waitFor(100)) diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MoreOptionsIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MoreOptionsIntegrationTest.kt index 970e94de06..7beb1c5c94 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MoreOptionsIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MoreOptionsIntegrationTest.kt @@ -199,7 +199,7 @@ class MoreOptionsIntegrationTest { String::class.java ) ), - Matchers.`is`("png") + Matchers.`is`("png") ) ).inRoot(RootMatchers.isPlatformPopup()).perform(ViewActions.click()) Espresso.onView(withId(R.id.pocketpaint_image_name_save_text)) @@ -210,9 +210,9 @@ class MoreOptionsIntegrationTest { TopBarViewInteraction.onTopBarView() .performOpenMoreOptions() Espresso.onView(withText(R.string.menu_save_image)).perform(ViewActions.click()) - Espresso.onView(ViewMatchers.withText("png")) + Espresso.onView(withText("png")) .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) - Espresso.onView(ViewMatchers.withText(defaultPictureName)) + Espresso.onView(withText(defaultPictureName)) .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) } diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/OraFileFormatIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/OraFileFormatIntegrationTest.kt index 53562e1eec..9283381a84 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/OraFileFormatIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/OraFileFormatIntegrationTest.kt @@ -44,9 +44,9 @@ import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider import org.catrobat.paintroid.test.espresso.util.EspressoUtils.grantPermissionRulesVersionCheck import org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt import org.catrobat.paintroid.test.espresso.util.UiInteractions.waitFor -import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.Companion.onDrawingSurfaceView import org.catrobat.paintroid.test.espresso.util.wrappers.LayerMenuViewInteraction -import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.Companion.onTopBarView import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule import org.hamcrest.Matchers.instanceOf import org.hamcrest.Matchers.`is` @@ -98,7 +98,7 @@ class OraFileFormatIntegrationTest { onTopBarView().performOpenMoreOptions() onView(withText(R.string.menu_save_image)).perform(ViewActions.click()) onView(withId(R.id.pocketpaint_save_dialog_spinner)).perform(ViewActions.click()) - onData(AllOf.allOf(`is`(instanceOf(String::class.java)), `is`("ora"))) + onData(AllOf.allOf(`is`(instanceOf(String::class.java)), `is`("ora"))) .inRoot(RootMatchers.isPlatformPopup()).perform(ViewActions.click()) onView(withId(R.id.pocketpaint_image_name_save_text)) .perform(ViewActions.replaceText("test1337")) @@ -116,7 +116,7 @@ class OraFileFormatIntegrationTest { onTopBarView().performOpenMoreOptions() onView(withText(R.string.menu_save_image)).perform(ViewActions.click()) onView(withId(R.id.pocketpaint_save_dialog_spinner)).perform(ViewActions.click()) - onData(AllOf.allOf(`is`(instanceOf(String::class.java)), `is`("ora"))) + onData(AllOf.allOf(`is`(instanceOf(String::class.java)), `is`("ora"))) .inRoot(RootMatchers.isPlatformPopup()).perform(ViewActions.click()) onView(withId(R.id.pocketpaint_image_name_save_text)) .perform(ViewActions.replaceText("OraOverride")) @@ -160,7 +160,7 @@ class OraFileFormatIntegrationTest { onTopBarView().performOpenMoreOptions() onView(withText(R.string.menu_save_image)).perform(ViewActions.click()) onView(withId(R.id.pocketpaint_save_dialog_spinner)).perform(ViewActions.click()) - onData(AllOf.allOf(`is`(instanceOf(String::class.java)), `is`("ora"))) + onData(AllOf.allOf(`is`(instanceOf(String::class.java)), `is`("ora"))) .inRoot(RootMatchers.isPlatformPopup()).perform(ViewActions.click()) onView(withId(R.id.pocketpaint_image_name_save_text)) .perform(ViewActions.replaceText("MoreLayersOraTest")) diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/OraFileIntentTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/OraFileIntentTest.java deleted file mode 100644 index eb1cb39365..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/OraFileIntentTest.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2021 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Intent; -import android.graphics.Bitmap; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.Environment; -import android.provider.MediaStore; -import android.util.Log; - -import org.catrobat.paintroid.FileIO; -import org.catrobat.paintroid.MainActivity; -import org.catrobat.paintroid.tools.ToolType; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Objects; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.ActivityTestRule; - -import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -@RunWith(AndroidJUnit4.class) -public class OraFileIntentTest { - private static ArrayList deletionFileList = null; - @Rule - public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); - private ContentResolver resolver; - - @Before - public void setUp() { - onToolBarView().performSelectTool(ToolType.BRUSH); - deletionFileList = new ArrayList<>(); - resolver = launchActivityRule.getActivity().getContentResolver(); - } - - @After - public void tearDown() { - for (File file : deletionFileList) { - if (file != null && file.exists()) { - assertTrue(file.delete()); - } - } - } - - @Test - public void testCheckIntentForOraFile() { - Intent intent = new Intent(); - Uri receivedUri = createTestImageFile(); - Bitmap receivedBitmap = null; - - try { - receivedBitmap = FileIO.getBitmapFromUri(resolver, receivedUri, launchActivityRule.getActivity().getBaseContext()); - } catch (Exception e) { - Log.e("Can't Read", "Can't get Bitmap from File"); - } - - Objects.requireNonNull(receivedBitmap); - intent.setAction(Intent.ACTION_EDIT); - intent.setData(receivedUri); - intent.setType("image/*"); - intent.putExtra(Intent.EXTRA_STREAM, receivedUri); - - launchActivityRule.launchActivity(intent); - Intent mainActivityIntent = launchActivityRule.getActivity().getIntent(); - - String intentAction = intent.getAction(); - String intentType = intent.getType(); - Bundle intentBundle = intent.getExtras(); - Objects.requireNonNull(intentBundle); - Uri intentUri = (Uri) intentBundle.get(Intent.EXTRA_STREAM); - - String mainActivityIntentAction = mainActivityIntent.getAction(); - String mainActivityIntentType = mainActivityIntent.getType(); - Bundle mainActivityIntentBundle = mainActivityIntent.getExtras(); - Objects.requireNonNull(mainActivityIntentBundle); - Uri mainActivityIntentUri = (Uri) mainActivityIntentBundle.get(Intent.EXTRA_STREAM); - Bitmap mainActivityIntentBitmap = null; - Objects.requireNonNull(mainActivityIntentUri); - - try { - mainActivityIntentBitmap = FileIO.getBitmapFromUri(resolver, mainActivityIntentUri, launchActivityRule.getActivity().getBaseContext()); - } catch (Exception e) { - Log.e("Can't read", "Can't get Bitmap From File"); - } - - Objects.requireNonNull(mainActivityIntentBitmap); - - assertEquals(intentAction, mainActivityIntentAction); - assertEquals(intentType, mainActivityIntentType); - assertEquals(intentUri, mainActivityIntentUri); - assertEquals(receivedBitmap.getWidth(), mainActivityIntentBitmap.getWidth()); - assertEquals(receivedBitmap.getHeight(), mainActivityIntentBitmap.getHeight()); - } - - private Uri createTestImageFile() { - Bitmap bitmap = Bitmap.createBitmap(400, 400, Bitmap.Config.ARGB_8888); - - ContentValues contentValues = new ContentValues(); - contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, "testfile.ora"); - contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/*"); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES); - } - - Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues); - try { - OutputStream fos = resolver.openOutputStream(Objects.requireNonNull(imageUri)); - assertTrue(bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)); - assert fos != null; - fos.close(); - } catch (IOException e) { - throw new AssertionError("Picture file could not be created.", e); - } - - File imageFile = new File(imageUri.getPath(), "testfile.ora"); - deletionFileList.add(imageFile); - return imageUri; - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/OraFileIntentTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/OraFileIntentTest.kt new file mode 100644 index 0000000000..1afecdfc16 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/OraFileIntentTest.kt @@ -0,0 +1,136 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2021 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso + +import android.content.ContentResolver +import android.content.ContentValues +import android.content.Intent +import android.graphics.Bitmap +import android.net.Uri +import android.os.Build +import android.os.Environment +import android.provider.MediaStore +import android.util.Log +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.ActivityTestRule +import org.catrobat.paintroid.FileIO.getBitmapFromUri +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction +import org.catrobat.paintroid.tools.ToolType +import org.junit.Rule +import org.junit.Before +import org.junit.After +import org.junit.Assert +import org.junit.Test +import org.junit.runner.RunWith +import java.io.File +import java.io.IOException +import java.util.Objects +import kotlin.collections.ArrayList + +@RunWith(AndroidJUnit4::class) +class OraFileIntentTest { + @JvmField + @Rule + var launchActivityRule = ActivityTestRule(MainActivity::class.java) + private var resolver: ContentResolver? = null + @Before + fun setUp() { + ToolBarViewInteraction.onToolBarView().performSelectTool(ToolType.BRUSH) + deletionFileList = ArrayList() + resolver = launchActivityRule.activity.contentResolver + } + + @After + fun tearDown() { + for (file in deletionFileList!!) { + if (file.exists()) { + Assert.assertTrue(file.delete()) + } + } + } + + @Test + fun testCheckIntentForOraFile() { + val intent = Intent() + val receivedUri = createTestImageFile() + var receivedBitmap: Bitmap? = null + try { + receivedBitmap = getBitmapFromUri(resolver!!, receivedUri, launchActivityRule.activity.baseContext) + } catch (e: Exception) { + Log.e("Can't Read", "Can't get Bitmap from File") + } + Objects.requireNonNull(receivedBitmap) + intent.action = Intent.ACTION_EDIT + intent.data = receivedUri + intent.type = "image/*" + intent.putExtra(Intent.EXTRA_STREAM, receivedUri) + launchActivityRule.launchActivity(intent) + val mainActivityIntent = launchActivityRule.activity.intent + val intentAction = intent.action + val intentType = intent.type + val intentBundle = intent.extras + Objects.requireNonNull(intentBundle) + val intentUri = intentBundle!![Intent.EXTRA_STREAM] as Uri? + val mainActivityIntentAction = mainActivityIntent.action + val mainActivityIntentType = mainActivityIntent.type + val mainActivityIntentBundle = mainActivityIntent.extras + Objects.requireNonNull(mainActivityIntentBundle) + val mainActivityIntentUri = mainActivityIntentBundle!![Intent.EXTRA_STREAM] as Uri? + var mainActivityIntentBitmap: Bitmap? = null + Objects.requireNonNull(mainActivityIntentUri) + try { + mainActivityIntentBitmap = getBitmapFromUri(resolver!!, mainActivityIntentUri!!, launchActivityRule.activity.baseContext) + } catch (e: Exception) { + Log.e("Can't read", "Can't get Bitmap From File") + } + Objects.requireNonNull(mainActivityIntentBitmap) + Assert.assertEquals(intentAction, mainActivityIntentAction) + Assert.assertEquals(intentType, mainActivityIntentType) + Assert.assertEquals(intentUri, mainActivityIntentUri) + Assert.assertEquals(receivedBitmap!!.width.toLong(), mainActivityIntentBitmap!!.width.toLong()) + Assert.assertEquals(receivedBitmap.height.toLong(), mainActivityIntentBitmap.height.toLong()) + } + + private fun createTestImageFile(): Uri { + val bitmap = Bitmap.createBitmap(400, 400, Bitmap.Config.ARGB_8888) + val contentValues = ContentValues() + contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, "testfile.ora") + contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/*") + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES) + } + val imageUri = resolver!!.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues) + try { + val fos = Objects.requireNonNull(imageUri)?.let { resolver!!.openOutputStream(it) } + Assert.assertTrue(bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)) + assert(fos != null) + fos!!.close() + } catch (e: IOException) { + throw AssertionError("Picture file could not be created.", e) + } + val imageFile = File(imageUri!!.path, "testfile.ora") + deletionFileList!!.add(imageFile) + return imageUri + } + + companion object { + private var deletionFileList: ArrayList? = null + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/SaveCompressImageIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/SaveCompressImageIntegrationTest.kt index 0f25151fdf..8695392047 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/SaveCompressImageIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/SaveCompressImageIntegrationTest.kt @@ -44,7 +44,7 @@ import org.catrobat.paintroid.FileIO.getScaledBitmapFromUri import org.catrobat.paintroid.MainActivity import org.catrobat.paintroid.R import org.catrobat.paintroid.test.espresso.util.UiInteractions -import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.Companion.onTopBarView import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule import org.hamcrest.Matchers import org.hamcrest.core.AllOf @@ -111,7 +111,7 @@ class SaveCompressImageIntegrationTest { Espresso.onData( AllOf.allOf( Matchers.`is`(Matchers.instanceOf(String::class.java)), - Matchers.`is`("jpg") + Matchers.`is`("jpg") ) ).inRoot(RootMatchers.isPlatformPopup()).perform(ViewActions.click()) onView(ViewMatchers.withText(R.string.save_button_text)).perform(ViewActions.click()) @@ -129,7 +129,8 @@ class SaveCompressImageIntegrationTest { val testBitmap = getBitmapFromFile(testImageFile) Assert.assertThat(compressedBitmap?.bitmap?.width, Matchers.`is`(Matchers.lessThanOrEqualTo(testBitmap!!.width))) - Assert.assertThat(compressedBitmap?.bitmap?.height, Matchers.`is`(Matchers.lessThanOrEqualTo(testBitmap!!.height))) + Assert.assertThat(compressedBitmap?.bitmap?.height, Matchers.`is`(Matchers.lessThanOrEqualTo( + testBitmap.height))) } private fun createTestBitmap(): Bitmap { diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/StatusbarIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/StatusbarIntegrationTest.kt index 73082169fc..5216940bec 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/StatusbarIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/StatusbarIntegrationTest.kt @@ -29,7 +29,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.rule.ActivityTestRule import org.catrobat.paintroid.MainActivity import org.catrobat.paintroid.R -import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.Companion.onToolBarView import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule import org.catrobat.paintroid.tools.ToolType import org.junit.Before diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/TemporaryFileSavingTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/TemporaryFileSavingTest.kt index a364803742..d96adb9a2e 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/TemporaryFileSavingTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/TemporaryFileSavingTest.kt @@ -31,7 +31,7 @@ import org.catrobat.paintroid.common.TEMP_IMAGE_PATH import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider import org.catrobat.paintroid.test.espresso.util.UiInteractions -import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.Companion.onDrawingSurfaceView import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction import org.catrobat.paintroid.tools.ToolReference diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ToolOnBackPressedIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ToolOnBackPressedIntegrationTest.java deleted file mode 100644 index 321cdce4ee..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ToolOnBackPressedIntegrationTest.java +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2022 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso; - -import android.content.ContentResolver; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.net.Uri; -import android.os.Environment; -import android.view.Gravity; - -import org.catrobat.paintroid.FileIO; -import org.catrobat.paintroid.MainActivity; -import org.catrobat.paintroid.R; -import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; -import org.catrobat.paintroid.test.espresso.util.EspressoUtils; -import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction; -import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; -import org.catrobat.paintroid.tools.ToolReference; -import org.catrobat.paintroid.tools.ToolType; -import org.hamcrest.core.AllOf; -import org.junit.After; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; - -import androidx.test.espresso.Espresso; -import androidx.test.espresso.action.ViewActions; -import androidx.test.espresso.intent.rule.IntentsTestRule; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.GrantPermissionRule; - -import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt; -import static org.catrobat.paintroid.test.espresso.util.UiInteractions.waitFor; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerViewInteraction.onColorPickerView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ConfirmQuitDialogInteraction.onConfirmQuitDialog; -import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -import static androidx.test.espresso.Espresso.onData; -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.Espresso.pressBack; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard; -import static androidx.test.espresso.action.ViewActions.replaceText; -import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.contrib.DrawerActions.open; -import static androidx.test.espresso.contrib.DrawerMatchers.isClosed; -import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.isRoot; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; - -@RunWith(AndroidJUnit4.class) -public class ToolOnBackPressedIntegrationTest { - - private static final String FILE_ENDING = ".png"; - - @Rule - public IntentsTestRule launchActivityRule = new IntentsTestRule<>(MainActivity.class, false, true); - - @Rule - public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); - - @ClassRule - public static GrantPermissionRule grantPermissionRule = EspressoUtils.INSTANCE.grantPermissionRulesVersionCheck(); - - private File saveFile = null; - private ToolReference toolReference; - public final String defaultPictureName = "catroidTemp"; - - @Before - public void setUp() { - MainActivity activity = launchActivityRule.getActivity(); - toolReference = activity.toolReference; - - onToolBarView() - .performSelectTool(ToolType.BRUSH); - } - - @After - public void tearDown() { - if (saveFile != null && saveFile.exists()) { - saveFile.delete(); - saveFile = null; - } - - String imagesDirectory = String.valueOf( - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)); - String pathToFile = imagesDirectory + File.separator + defaultPictureName + FILE_ENDING; - File imageFile = new File(pathToFile); - if (imageFile.exists()) { - imageFile.delete(); - } - } - - @Test - public void testBrushToolBackPressed() { - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - Espresso.pressBack(); - - onConfirmQuitDialog() - .checkPositiveButton(matches(isDisplayed())) - .checkNegativeButton(matches(isDisplayed())) - .checkNeutralButton(matches(not(isDisplayed()))) - .checkMessage(matches(isDisplayed())) - .checkTitle(matches(isDisplayed())); - - Espresso.pressBack(); - - onConfirmQuitDialog() - .checkPositiveButton(doesNotExist()) - .checkNegativeButton(doesNotExist()) - .checkNeutralButton(doesNotExist()) - .checkMessage(doesNotExist()) - .checkTitle(doesNotExist()); - - Espresso.pressBack(); - - onConfirmQuitDialog().onNegativeButton() - .perform(click()); - - assertTrue(launchActivityRule.getActivity().isFinishing()); - } - - @Test - public void testBrushToolBackPressedWithSaveAndOverride() throws IOException, InterruptedException { - TopBarViewInteraction.onTopBarView() - .performOpenMoreOptions(); - onView(withText(R.string.menu_save_image)) - .perform(ViewActions.click()); - - onView(withId(R.id.pocketpaint_save_info_title)).check(matches(isDisplayed())); - onView(withId(R.id.pocketpaint_image_name_save_text)).check(matches(isDisplayed())); - onView(withId(R.id.pocketpaint_save_dialog_spinner)).check(matches(isDisplayed())); - - onView(withId(R.id.pocketpaint_save_dialog_spinner)) - .perform(click()); - onData(AllOf.allOf(is(instanceOf(String.class)), - is("png"))).inRoot(isPlatformPopup()).perform(click()); - onView(withId(R.id.pocketpaint_image_name_save_text)) - .perform(replaceText(defaultPictureName)); - - onView(withText(R.string.save_button_text)) - .perform(click()); - - onView(isRoot()).perform(waitFor(3000)); - - String filename = defaultPictureName + FILE_ENDING; - ContentResolver resolver = launchActivityRule.getActivity().getContentResolver(); - Uri uri = FileIO.INSTANCE.getUriForFilenameInPicturesFolder(filename, resolver); - BitmapFactory.Options options = new BitmapFactory.Options(); - assertNotNull(uri); - InputStream inputStream = resolver.openInputStream(uri); - Bitmap oldBitmap = BitmapFactory.decodeStream(inputStream, null, options); - - onView(isRoot()).perform(waitFor(2000)); - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - Espresso.pressBack(); - - onConfirmQuitDialog().onPositiveButton() - .perform(click()); - - onView(withId(R.id.pocketpaint_save_info_title)).check(matches(isDisplayed())); - onView(withId(R.id.pocketpaint_image_name_save_text)).check(matches(isDisplayed())); - onView(withId(R.id.pocketpaint_save_dialog_spinner)).check(matches(isDisplayed())); - - onView(withId(R.id.pocketpaint_save_dialog_spinner)) - .perform(click()); - onData(AllOf.allOf(is(instanceOf(String.class)), - is("png"))).inRoot(isPlatformPopup()).perform(click()); - onView(withId(R.id.pocketpaint_image_name_save_text)) - .perform(replaceText(defaultPictureName)); - - onView(withText(R.string.save_button_text)) - .perform(click()); - - onView(withText(R.string.overwrite_button_text)) - .perform(click()); - - while (!launchActivityRule.getActivity().isFinishing()) { - Thread.sleep(1000); - } - - uri = FileIO.INSTANCE.getUriForFilenameInPicturesFolder(filename, resolver); - assertNotNull(uri); - inputStream = resolver.openInputStream(uri); - Bitmap actualBitmap = BitmapFactory.decodeStream(inputStream, null, options); - - assertNotNull(oldBitmap); - assertNotNull(actualBitmap); - assertFalse("Bitmaps are the same, should be different", oldBitmap.sameAs(actualBitmap)); - } - - @Test - public void testNotBrushToolBackPressed() { - onToolBarView() - .performSelectTool(ToolType.CURSOR); - - Espresso.pressBack(); - - assertEquals(toolReference.getTool().getToolType(), ToolType.BRUSH); - } - - @Test - public void testToolOptionsGoBackWhenBackPressed() { - onToolBarView() - .performSelectTool(ToolType.CURSOR); - - assertEquals(toolReference.getTool().getToolType(), ToolType.CURSOR); - - Espresso.pressBack(); - - assertEquals(toolReference.getTool().getToolType(), ToolType.BRUSH); - } - - @Test - public void testBrushToolBackPressedFromCatroidAndUsePicture() throws SecurityException, IllegalArgumentException, InterruptedException { - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - String pathToFile = - launchActivityRule.getActivity().getExternalFilesDir(Environment.DIRECTORY_PICTURES) - + File.separator - + defaultPictureName - + FILE_ENDING; - - saveFile = new File(pathToFile); - launchActivityRule.getActivity().model.setSavedPictureUri(Uri.fromFile(saveFile)); - launchActivityRule.getActivity().model.setOpenedFromCatroid(true); - - Espresso.pressBackUnconditionally(); - - while (!launchActivityRule.getActivity().isFinishing()) { - Thread.sleep(1000); - } - - assertTrue(launchActivityRule.getActivity().isFinishing()); - assertTrue(saveFile.exists()); - assertThat(saveFile.length(), is(greaterThan(0L))); - } - - @Test - public void testCloseLayerDialogOnBackPressed() { - onView(withId(R.id.pocketpaint_drawer_layout)) - .perform(open(Gravity.END)) - .check(matches(isDisplayed())); - pressBack(); - onView(withId(R.id.pocketpaint_drawer_layout)) - .check(matches(isClosed())); - } - - @Test - public void testCloseColorPickerDialogOnBackPressed() { - onColorPickerView() - .performOpenColorPicker() - .check(matches(isDisplayed())); - - onColorPickerView() - .perform(closeSoftKeyboard()) - .perform(ViewActions.pressBack()) - .check(doesNotExist()); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ToolOnBackPressedIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ToolOnBackPressedIntegrationTest.kt new file mode 100644 index 0000000000..84c7bcc881 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ToolOnBackPressedIntegrationTest.kt @@ -0,0 +1,243 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso + +import android.graphics.BitmapFactory +import android.net.Uri +import android.os.Environment +import android.view.Gravity +import androidx.test.espresso.Espresso +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.contrib.DrawerActions +import androidx.test.espresso.contrib.DrawerMatchers +import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.test.espresso.matcher.RootMatchers +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.catrobat.paintroid.FileIO.getUriForFilenameInPicturesFolder +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.R +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider +import org.catrobat.paintroid.test.espresso.util.EspressoUtils.grantPermissionRulesVersionCheck +import org.catrobat.paintroid.test.espresso.util.UiInteractions +import org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerViewInteraction.Companion.onColorPickerView +import org.catrobat.paintroid.test.espresso.util.wrappers.ConfirmQuitDialogInteraction.Companion.onConfirmQuitDialog +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.Companion.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.Companion.onToolBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.catrobat.paintroid.tools.ToolReference +import org.catrobat.paintroid.tools.ToolType +import org.hamcrest.Matchers +import org.hamcrest.core.AllOf +import org.junit.runner.RunWith +import java.io.File +import java.io.IOException +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.After +import org.junit.ClassRule +import org.junit.Assert + +@RunWith(AndroidJUnit4::class) +class ToolOnBackPressedIntegrationTest { + @JvmField + @Rule + var launchActivityRule = IntentsTestRule(MainActivity::class.java, false, true) + + @JvmField + @Rule + var screenshotOnFailRule = ScreenshotOnFailRule() + private var saveFile: File? = null + private var toolReference: ToolReference? = null + private val defaultPictureName = "catroidTemp" + @Before + fun setUp() { + val activity = launchActivityRule.activity + toolReference = activity.toolReference + onToolBarView() + .performSelectTool(ToolType.BRUSH) + } + + @After + fun tearDown() { + if (saveFile != null && saveFile!!.exists()) { + saveFile!!.delete() + saveFile = null + } + val imagesDirectory = + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString() + val pathToFile = imagesDirectory + File.separator + defaultPictureName + FILE_ENDING + val imageFile = File(pathToFile) + if (imageFile.exists()) { + imageFile.delete() + } + } + + @Test + fun testBrushToolBackPressed() { + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + Espresso.pressBack() + onConfirmQuitDialog() + .checkPositiveButton(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .checkNegativeButton(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .checkNeutralButton(ViewAssertions.matches(Matchers.not(ViewMatchers.isDisplayed()))) + .checkMessage(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .checkTitle(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.pressBack() + onConfirmQuitDialog() + .checkPositiveButton(ViewAssertions.doesNotExist()) + .checkNegativeButton(ViewAssertions.doesNotExist()) + .checkNeutralButton(ViewAssertions.doesNotExist()) + .checkMessage(ViewAssertions.doesNotExist()) + .checkTitle(ViewAssertions.doesNotExist()) + Espresso.pressBack() + onConfirmQuitDialog().onNegativeButton() + .perform(ViewActions.click()) + Assert.assertTrue(launchActivityRule.activity.isFinishing) + } + + @Test + @Throws(IOException::class, InterruptedException::class) + fun testBrushToolBackPressedWithSaveAndOverride() { + TopBarViewInteraction.onTopBarView() + .performOpenMoreOptions() + Espresso.onView(withText(R.string.menu_save_image)) + .perform(ViewActions.click()) + Espresso.onView(withId(R.id.pocketpaint_save_info_title)).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.pocketpaint_image_name_save_text)).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.pocketpaint_save_dialog_spinner)).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.pocketpaint_save_dialog_spinner)) + .perform(ViewActions.click()) + Espresso.onData(AllOf.allOf(Matchers.`is`(Matchers.instanceOf(String::class.java)), + Matchers.`is`("png"))).inRoot(RootMatchers.isPlatformPopup()).perform(ViewActions.click()) + Espresso.onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(ViewActions.replaceText(defaultPictureName)) + Espresso.onView(withText(R.string.save_button_text)) + .perform(ViewActions.click()) + Espresso.onView(ViewMatchers.isRoot()).perform(UiInteractions.waitFor(3000)) + val filename = defaultPictureName + FILE_ENDING + val resolver = launchActivityRule.activity.contentResolver + var uri = getUriForFilenameInPicturesFolder(filename, resolver) + val options = BitmapFactory.Options() + Assert.assertNotNull(uri) + var inputStream = resolver.openInputStream(uri!!) + val oldBitmap = BitmapFactory.decodeStream(inputStream, null, options) + Espresso.onView(ViewMatchers.isRoot()).perform(UiInteractions.waitFor(2000)) + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + Espresso.pressBack() + onConfirmQuitDialog().onPositiveButton() + .perform(ViewActions.click()) + Espresso.onView(withId(R.id.pocketpaint_save_info_title)).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.pocketpaint_image_name_save_text)).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.pocketpaint_save_dialog_spinner)).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.pocketpaint_save_dialog_spinner)) + .perform(ViewActions.click()) + Espresso.onData(AllOf.allOf(Matchers.`is`(Matchers.instanceOf(String::class.java)), + Matchers.`is`("png"))).inRoot(RootMatchers.isPlatformPopup()).perform(ViewActions.click()) + Espresso.onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(ViewActions.replaceText(defaultPictureName)) + Espresso.onView(withText(R.string.save_button_text)) + .perform(ViewActions.click()) + Espresso.onView(withText(R.string.overwrite_button_text)) + .perform(ViewActions.click()) + while (!launchActivityRule.activity.isFinishing) { + Thread.sleep(1000) + } + uri = getUriForFilenameInPicturesFolder(filename, resolver) + Assert.assertNotNull(uri) + inputStream = resolver.openInputStream(uri!!) + val actualBitmap = BitmapFactory.decodeStream(inputStream, null, options) + Assert.assertNotNull(oldBitmap) + Assert.assertNotNull(actualBitmap) + Assert.assertFalse("Bitmaps are the same, should be different", oldBitmap!!.sameAs(actualBitmap)) + } + + @Test + fun testNotBrushToolBackPressed() { + onToolBarView() + .performSelectTool(ToolType.CURSOR) + Espresso.pressBack() + Assert.assertEquals(toolReference!!.tool!!.toolType, ToolType.BRUSH) + } + + @Test + fun testToolOptionsGoBackWhenBackPressed() { + onToolBarView() + .performSelectTool(ToolType.CURSOR) + Assert.assertEquals(toolReference!!.tool!!.toolType, ToolType.CURSOR) + Espresso.pressBack() + Assert.assertEquals(toolReference!!.tool!!.toolType, ToolType.BRUSH) + } + + @Test + @Throws(SecurityException::class, IllegalArgumentException::class, InterruptedException::class) + fun testBrushToolBackPressedFromCatroidAndUsePicture() { + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + val pathToFile = launchActivityRule.activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES) + .toString() + File.separator + + defaultPictureName + + FILE_ENDING + saveFile = File(pathToFile) + launchActivityRule.activity.model.savedPictureUri = Uri.fromFile(saveFile) + launchActivityRule.activity.model.isOpenedFromCatroid = true + Espresso.pressBackUnconditionally() + while (!launchActivityRule.activity.isFinishing) { + Thread.sleep(1000) + } + Assert.assertTrue(launchActivityRule.activity.isFinishing) + Assert.assertTrue(saveFile!!.exists()) + Assert.assertThat(saveFile!!.length(), Matchers.`is`(Matchers.greaterThan(0L))) + } + + @Test + fun testCloseLayerDialogOnBackPressed() { + Espresso.onView(withId(R.id.pocketpaint_drawer_layout)) + .perform(DrawerActions.open(Gravity.END)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.pressBack() + Espresso.onView(withId(R.id.pocketpaint_drawer_layout)) + .check(ViewAssertions.matches(DrawerMatchers.isClosed())) + } + + @Test + fun testCloseColorPickerDialogOnBackPressed() { + onColorPickerView() + .performOpenColorPicker() + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + onColorPickerView() + .perform(ViewActions.closeSoftKeyboard()) + .perform(ViewActions.pressBack()) + .check(ViewAssertions.doesNotExist()) + } + + companion object { + private const val FILE_ENDING = ".png" + @JvmField + @ClassRule + var grantPermissionRule = grantPermissionRulesVersionCheck() + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ToolOptionsIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ToolOptionsIntegrationTest.kt index 6b64fbe9ad..ed847cdd67 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ToolOptionsIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ToolOptionsIntegrationTest.kt @@ -34,7 +34,7 @@ import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.rule.ActivityTestRule import org.catrobat.paintroid.MainActivity import org.catrobat.paintroid.test.espresso.util.wrappers.BrushToolOptionsViewInteraction.Companion.onBrushToolOptionsView -import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule import org.catrobat.paintroid.tools.ToolType import org.hamcrest.Matchers.not @@ -52,10 +52,10 @@ import java.io.OutputStream @RunWith(value = Parameterized::class) class ToolOptionsIntegrationTest( - private var toolType: ToolType?, - private var toolOptionsShownInitially: Boolean, - private var hasToolOptionsView: Boolean, - private val hasStrokeCapOptions: Boolean + private var toolType: ToolType?, + private var toolOptionsShownInitially: Boolean, + private var hasToolOptionsView: Boolean, + private val hasStrokeCapOptions: Boolean ) { @get:Rule var activityTestRule: ActivityTestRule = IntentsTestRule(MainActivity::class.java) @@ -69,21 +69,21 @@ class ToolOptionsIntegrationTest( @Parameterized.Parameters(name = "{index}: ToolType={0}, ToolOptionsShownInitially={1}, HasToolOptionsView={2}, HasStrokeCapOptions{3}") fun data(): Iterable> { return listOf( - arrayOf(ToolType.BRUSH, true, true, true), - arrayOf(ToolType.HAND, false, false, false), - arrayOf(ToolType.ERASER, true, true, true), - arrayOf(ToolType.LINE, true, true, true), - arrayOf(ToolType.SHAPE, true, true, false), - arrayOf(ToolType.FILL, true, true, false), - arrayOf(ToolType.SPRAY, true, true, false), - arrayOf(ToolType.CURSOR, true, true, true), - arrayOf(ToolType.TEXT, true, true, false), - arrayOf(ToolType.TRANSFORM, true, true, false), - arrayOf(ToolType.CLIPBOARD, true, true, false), - arrayOf(ToolType.PIPETTE, false, false, false), - arrayOf(ToolType.WATERCOLOR, true, true, true), - arrayOf(ToolType.SMUDGE, true, true, true), - arrayOf(ToolType.CLIP, true, true, false) + arrayOf(ToolType.BRUSH, true, true, true), + arrayOf(ToolType.HAND, false, false, false), + arrayOf(ToolType.ERASER, true, true, true), + arrayOf(ToolType.LINE, true, true, true), + arrayOf(ToolType.SHAPE, true, true, false), + arrayOf(ToolType.FILL, true, true, false), + arrayOf(ToolType.SPRAY, true, true, false), + arrayOf(ToolType.CURSOR, true, true, true), + arrayOf(ToolType.TEXT, true, true, false), + arrayOf(ToolType.TRANSFORM, true, true, false), + arrayOf(ToolType.CLIPBOARD, true, true, false), + arrayOf(ToolType.PIPETTE, false, false, false), + arrayOf(ToolType.WATERCOLOR, true, true, true), + arrayOf(ToolType.SMUDGE, true, true, true), + arrayOf(ToolType.CLIP, true, true, false) ) } } @@ -108,20 +108,22 @@ class ToolOptionsIntegrationTest( @After fun tearDown() { testImageFile?.let { assertTrue(it.delete()) } } + fun onToolBarView(): ToolBarViewInteraction = ToolBarViewInteraction() + @Test fun testToolOptions() { onToolBarView() - .performSelectTool(toolType) + .performSelectTool(toolType!!) if (!toolOptionsShownInitially) { onToolBarView() - .performOpenToolOptionsView() + .performOpenToolOptionsView() } if (hasToolOptionsView) { onToolBarView().onToolOptionsView() - .check(matches(isDisplayed())) + .check(matches(isDisplayed())) } else { onToolBarView().onToolOptionsView() - .check(matches(not(isDisplayed()))) + .check(matches(not(isDisplayed()))) } } @@ -129,10 +131,10 @@ class ToolOptionsIntegrationTest( fun testShouldNotUnselectAlreadySelectedStrokeTypeWhenSelectedAgain() { if (hasStrokeCapOptions) { onToolBarView() - .performSelectTool(toolType) + .performSelectTool(toolType!!) if (!toolOptionsShownInitially) { onToolBarView() - .performOpenToolOptionsView() + .performOpenToolOptionsView() } repeat(2) { diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ToolSelectionIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ToolSelectionIntegrationTest.kt index c81eb46439..e60a785fdb 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ToolSelectionIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ToolSelectionIntegrationTest.kt @@ -24,8 +24,8 @@ package org.catrobat.paintroid.test.espresso import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.rule.ActivityTestRule import org.catrobat.paintroid.MainActivity -import org.catrobat.paintroid.test.espresso.util.wrappers.BottomNavigationViewInteraction.onBottomNavigationView -import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.BottomNavigationViewInteraction.Companion.onBottomNavigationView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.Companion.onToolBarView import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule import org.catrobat.paintroid.tools.ToolType import org.junit.Before diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/UndoRedoIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/UndoRedoIntegrationTest.kt index d83ab0a520..6ba9e5f472 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/UndoRedoIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/UndoRedoIntegrationTest.kt @@ -36,11 +36,11 @@ import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider import org.catrobat.paintroid.test.espresso.util.MainActivityHelper import org.catrobat.paintroid.test.espresso.util.UiInteractions import org.catrobat.paintroid.test.espresso.util.UiMatcher -import org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerViewInteraction.onColorPickerView -import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView -import org.catrobat.paintroid.test.espresso.util.wrappers.LayerMenuViewInteraction.onLayerMenuView -import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView -import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerViewInteraction.Companion.onColorPickerView +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.Companion.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.LayerMenuViewInteraction.Companion.onLayerMenuView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.Companion.onToolBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.Companion.onTopBarView import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule import org.catrobat.paintroid.test.utils.TestUtils.Companion.selectColorInDialog import org.catrobat.paintroid.tools.ToolType diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ZoomWindowIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ZoomWindowIntegrationTest.kt index cb06320a36..4e846bfa0c 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ZoomWindowIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ZoomWindowIntegrationTest.kt @@ -6,7 +6,6 @@ import androidx.test.espresso.Espresso.onView import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.rule.ActivityTestRule import org.catrobat.paintroid.MainActivity -import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction import org.catrobat.paintroid.tools.ToolType import org.junit.Before @@ -22,7 +21,8 @@ import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider import org.catrobat.paintroid.test.espresso.util.UiInteractions.PressAndReleaseActions.tearDownPressAndRelease import org.catrobat.paintroid.test.espresso.util.UiInteractions.PressAndReleaseActions.pressAction import org.catrobat.paintroid.test.espresso.util.UiInteractions.PressAndReleaseActions.releaseAction -import org.catrobat.paintroid.test.espresso.util.wrappers.ZoomWindowInteraction.onZoomWindow +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.Companion.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.ZoomWindowInteraction.Companion.onZoomWindow import org.junit.After @RunWith(AndroidJUnit4::class) diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/BrushPickerIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/BrushPickerIntegrationTest.java deleted file mode 100644 index b1b14e33ab..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/BrushPickerIntegrationTest.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2022 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.dialog; - -import android.graphics.Paint; -import android.graphics.Paint.Cap; - -import org.catrobat.paintroid.MainActivity; -import org.catrobat.paintroid.R; -import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; -import org.catrobat.paintroid.tools.ToolType; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import androidx.test.espresso.Espresso; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.ActivityTestRule; - -import static org.catrobat.paintroid.test.espresso.util.EspressoUtils.DEFAULT_STROKE_WIDTH; -import static org.catrobat.paintroid.test.espresso.util.UiInteractions.setProgress; -import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchCenterLeft; -import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withProgress; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; -import static org.hamcrest.core.IsNot.not; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.action.ViewActions.replaceText; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.isChecked; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; - -@RunWith(AndroidJUnit4.class) -public class BrushPickerIntegrationTest { - private static final int MIN_STROKE_WIDTH = 1; - private static final int MIDDLE_STROKE_WIDTH = 50; - private static final int MAX_STROKE_WIDTH = 100; - - @Rule - public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); - - @Rule - public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); - - @Before - public void setUp() { - onToolBarView() - .performSelectTool(ToolType.BRUSH); - } - - private Paint getCurrentToolBitmapPaint() { - return launchActivityRule.getActivity().toolPaint.getPaint(); - } - - private Paint getCurrentToolCanvasPaint() { - return launchActivityRule.getActivity().toolPaint.getPreviewPaint(); - } - - private void assertStrokePaint(Paint strokePaint, int expectedStrokeWidth, Cap expectedCap) { - int paintStrokeWidth = (int) strokePaint.getStrokeWidth(); - Cap paintCap = strokePaint.getStrokeCap(); - - assertEquals("Stroke did not change", expectedStrokeWidth, paintStrokeWidth); - assertEquals("Stroke cap not " + expectedCap.toString(), expectedCap, paintCap); - } - - private void setStrokeWidth(int strokeWidth, int expectedStrokeWidth) { - onView(withId(R.id.pocketpaint_stroke_width_seek_bar)) - .perform(setProgress(strokeWidth)) - .check(matches(withProgress(expectedStrokeWidth))); - } - - private void setStrokeWidth(int strokeWidth) { - setStrokeWidth(strokeWidth, strokeWidth); - } - - @Test - public void brushPickerDialogDefaultLayoutAndToolChanges() { - onView(withId(R.id.pocketpaint_brush_tool_preview)) - .check(matches(isDisplayed())); - onView(withId(R.id.pocketpaint_stroke_width_seek_bar)) - .check(matches(isDisplayed())) - .check(matches(withProgress(DEFAULT_STROKE_WIDTH))); - onView(withId(R.id.pocketpaint_stroke_width_width_text)) - .check(matches(isDisplayed())) - .check(matches(withText(Integer.toString(DEFAULT_STROKE_WIDTH)))); - onView(withId(R.id.pocketpaint_stroke_ibtn_rect)) - .check(matches(isDisplayed())) - .check(matches(not(isChecked()))); - onView(withId(R.id.pocketpaint_stroke_ibtn_circle)) - .check(matches(isDisplayed())) - .check(matches(isChecked())); - - setStrokeWidth(MIN_STROKE_WIDTH); - setStrokeWidth(MIDDLE_STROKE_WIDTH); - setStrokeWidth(MAX_STROKE_WIDTH); - - assertStrokePaint(getCurrentToolCanvasPaint(), MAX_STROKE_WIDTH, Cap.ROUND); - - onView(withId(R.id.pocketpaint_stroke_ibtn_rect)) - .perform(click()) - .check(matches(isChecked())); - onView(withId(R.id.pocketpaint_stroke_ibtn_circle)) - .check(matches(not(isChecked()))); - - assertStrokePaint(getCurrentToolCanvasPaint(), MAX_STROKE_WIDTH, Cap.SQUARE); - - onToolBarView() - .performCloseToolOptionsView(); - - assertStrokePaint(getCurrentToolCanvasPaint(), MAX_STROKE_WIDTH, Cap.SQUARE); - } - - @Test - public void brushPickerDialogKeepStrokeOnToolChange() { - final int newStrokeWidth = 80; - - setStrokeWidth(newStrokeWidth); - onView(withId(R.id.pocketpaint_stroke_ibtn_rect)) - .perform(click()); - - assertStrokePaint(getCurrentToolCanvasPaint(), newStrokeWidth, Cap.SQUARE); - - onToolBarView() - .performCloseToolOptionsView() - .performSelectTool(ToolType.CURSOR); - - onView(withId(R.id.pocketpaint_stroke_width_seek_bar)) - .check(matches(withProgress(newStrokeWidth))); - assertStrokePaint(getCurrentToolCanvasPaint(), newStrokeWidth, Cap.SQUARE); - - onToolBarView() - .performCloseToolOptionsView(); - } - - @Test - public void brushPickerDialogMinimumBrushWidth() { - setStrokeWidth(0, MIN_STROKE_WIDTH); - setStrokeWidth(MIN_STROKE_WIDTH); - - onToolBarView() - .performCloseToolOptionsView(); - } - - @Test - public void brushPickerAntiAliasingOffAtMinimumBrushSize() { - onView(withId(R.id.pocketpaint_stroke_width_seek_bar)) - .perform(touchCenterLeft()); - - onToolBarView() - .performCloseToolOptionsView(); - - Paint bitmapPaint = getCurrentToolBitmapPaint(); - Paint canvasPaint = getCurrentToolCanvasPaint(); - - assertFalse("BITMAP_PAINT antialiasing should be off", bitmapPaint.isAntiAlias()); - assertFalse("CANVAS_PAINT antialiasing should be off", canvasPaint.isAntiAlias()); - } - - @Test - public void setAntiAliasingNotOnWhenCancelPressed() { - onTopBarView() - .performOpenMoreOptions(); - - onView(withText(R.string.menu_advanced)) - .perform(click()); - - onView(withId(R.id.pocketpaint_antialiasing)) - .perform(click()); - - onView(withText(R.string.cancel_button_text)) - .perform(click()); - - Paint bitmapPaint = getCurrentToolBitmapPaint(); - Paint canvasPaint = getCurrentToolCanvasPaint(); - - assertTrue("BITMAP_PAINT antialiasing should be on", bitmapPaint.isAntiAlias()); - assertTrue("CANVAS_PAINT antialiasing should be on", canvasPaint.isAntiAlias()); - } - - @Test - public void setAntiAliasingOffWhenAdvancedSettingsTurnOffAndOn() { - onTopBarView() - .performOpenMoreOptions(); - - onView(withText(R.string.menu_advanced)) - .check(matches(isDisplayed())); - - onView(withText(R.string.menu_advanced)) - .perform(click()); - - onView(withId(R.id.pocketpaint_antialiasing)) - .perform(click()); - - onView(withText(R.string.pocketpaint_ok)) - .perform(click()); - - Paint bitmapPaint = getCurrentToolBitmapPaint(); - Paint canvasPaint = getCurrentToolCanvasPaint(); - - assertFalse("BITMAP_PAINT antialiasing should be off", bitmapPaint.isAntiAlias()); - assertFalse("CANVAS_PAINT antialiasing should be off", canvasPaint.isAntiAlias()); - - onTopBarView() - .performOpenMoreOptions(); - - onView(withText(R.string.menu_advanced)) - .perform(click()); - - onView(withId(R.id.pocketpaint_antialiasing)) - .perform(click()); - - onView(withText(R.string.pocketpaint_ok)) - .perform(click()); - - bitmapPaint = getCurrentToolBitmapPaint(); - canvasPaint = getCurrentToolCanvasPaint(); - - assertTrue("BITMAP_PAINT antialiasing should be on", bitmapPaint.isAntiAlias()); - assertTrue("CANVAS_PAINT antialiasing should be on", canvasPaint.isAntiAlias()); - } - - @Test - public void brushPickerDialogRadioButtonsBehaviour() { - onView(withId(R.id.pocketpaint_stroke_ibtn_rect)) - .check(matches(not(isChecked()))); - onView(withId(R.id.pocketpaint_stroke_ibtn_circle)) - .check(matches(isChecked())); - - onView(withId(R.id.pocketpaint_stroke_ibtn_rect)) - .perform(click()) - .check(matches(isChecked())); - - onView(withId(R.id.pocketpaint_stroke_ibtn_circle)) - .check(matches(not(isChecked()))); - - onToolBarView() - .performCloseToolOptionsView(); - - assertStrokePaint(getCurrentToolCanvasPaint(), DEFAULT_STROKE_WIDTH, Cap.SQUARE); - - onToolBarView() - .performOpenToolOptionsView(); - - onView(withId(R.id.pocketpaint_stroke_ibtn_circle)) - .perform(click()) - .check(matches(isChecked())); - - onView(withId(R.id.pocketpaint_stroke_ibtn_rect)) - .check(matches(not(isChecked()))); - - assertStrokePaint(getCurrentToolCanvasPaint(), DEFAULT_STROKE_WIDTH, Cap.ROUND); - - onToolBarView() - .performCloseToolOptionsView(); - - assertStrokePaint(getCurrentToolCanvasPaint(), DEFAULT_STROKE_WIDTH, Cap.ROUND); - } - - @Test - public void brushPickerDialogEditTextBehaviour() { - onView(withId(R.id.pocketpaint_stroke_width_width_text)) - .perform(replaceText(String.valueOf(MIDDLE_STROKE_WIDTH))); - - Espresso.closeSoftKeyboard(); - - onView(withId(R.id.pocketpaint_stroke_width_width_text)) - .check(matches(withText(String.valueOf(MIDDLE_STROKE_WIDTH)))); - - onView(withId(R.id.pocketpaint_stroke_width_seek_bar)) - .check(matches(withProgress(MIDDLE_STROKE_WIDTH))); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/BrushPickerIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/BrushPickerIntegrationTest.kt new file mode 100644 index 0000000000..1dc199792f --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/BrushPickerIntegrationTest.kt @@ -0,0 +1,232 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +@file:Suppress("DEPRECATION") + +package org.catrobat.paintroid.test.espresso.dialog + +import android.graphics.Paint +import android.graphics.Paint.Cap +import androidx.test.espresso.Espresso.closeSoftKeyboard +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.ActivityTestRule +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.R +import org.catrobat.paintroid.test.espresso.util.EspressoUtils.DEFAULT_STROKE_WIDTH +import org.catrobat.paintroid.test.espresso.util.UiInteractions +import org.catrobat.paintroid.test.espresso.util.UiMatcher +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.Companion.onToolBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.Companion.onTopBarView +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.catrobat.paintroid.tools.ToolType +import org.hamcrest.core.IsNot +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class BrushPickerIntegrationTest { + @get:Rule + var launchActivityRule = ActivityTestRule(MainActivity::class.java) + + @get:Rule + var screenshotOnFailRule = ScreenshotOnFailRule() + + @Before + fun setUp() { onToolBarView().performSelectTool(ToolType.BRUSH) } + + private fun getCurrentToolBitmapPaint(): Paint = launchActivityRule.activity.toolPaint.paint + + private fun getCurrentToolCanvasPaint(): Paint = launchActivityRule.activity.toolPaint.previewPaint + + private fun assertStrokePaint(strokePaint: Paint, expectedStrokeWidth: Int, expectedCap: Cap) { + val paintStrokeWidth = strokePaint.strokeWidth.toInt() + val paintCap = strokePaint.strokeCap + + Assert.assertEquals("Stroke did not change", expectedStrokeWidth.toLong(), paintStrokeWidth.toLong()) + Assert.assertEquals("Stroke cap not $expectedCap", expectedCap, paintCap) + } + + private fun setStrokeWidth(strokeWidth: Int, expectedStrokeWidth: Int) { + onView(withId(R.id.pocketpaint_stroke_width_seek_bar)) + .perform(UiInteractions.setProgress(strokeWidth)) + .check(ViewAssertions.matches(UiMatcher.withProgress(expectedStrokeWidth))) + } + + private fun setStrokeWidth(strokeWidth: Int) = setStrokeWidth(strokeWidth, strokeWidth) + + @Test + fun brushPickerDialogDefaultLayoutAndToolChanges() { + onView(withId(R.id.pocketpaint_brush_tool_preview)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + onView(withId(R.id.pocketpaint_stroke_width_seek_bar)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(ViewAssertions.matches(UiMatcher.withProgress(DEFAULT_STROKE_WIDTH))) + onView(withId(R.id.pocketpaint_stroke_width_width_text)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check( + ViewAssertions.matches( + withText( + DEFAULT_STROKE_WIDTH.toString() + ) + ) + ) + onView(withId(R.id.pocketpaint_stroke_ibtn_rect)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(ViewAssertions.matches(IsNot.not(ViewMatchers.isSelected()))) + onView(withId(R.id.pocketpaint_stroke_ibtn_circle)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(ViewAssertions.matches(ViewMatchers.isSelected())) + setStrokeWidth(MIN_STROKE_WIDTH) + setStrokeWidth(MIDDLE_STROKE_WIDTH) + setStrokeWidth(MAX_STROKE_WIDTH) + assertStrokePaint(getCurrentToolCanvasPaint(), MAX_STROKE_WIDTH, Cap.ROUND) + onView(withId(R.id.pocketpaint_stroke_ibtn_rect)) + .perform(ViewActions.click()) + .check(ViewAssertions.matches(ViewMatchers.isChecked())) + onView(withId(R.id.pocketpaint_stroke_ibtn_circle)) + .check(ViewAssertions.matches(ViewMatchers.isNotChecked())) + assertStrokePaint(getCurrentToolCanvasPaint(), MAX_STROKE_WIDTH, Cap.SQUARE) + onToolBarView().performCloseToolOptionsView() + assertStrokePaint(getCurrentToolCanvasPaint(), MAX_STROKE_WIDTH, Cap.SQUARE) + } + + @Test + fun brushPickerDialogKeepStrokeOnToolChange() { + val newStrokeWidth = 80 + setStrokeWidth(newStrokeWidth) + onView(withId(R.id.pocketpaint_stroke_ibtn_rect)).perform(ViewActions.click()) + assertStrokePaint(getCurrentToolCanvasPaint(), newStrokeWidth, Cap.SQUARE) + onToolBarView() + .performCloseToolOptionsView() + .performSelectTool(ToolType.CURSOR) + onView(withId(R.id.pocketpaint_stroke_width_seek_bar)) + .check(ViewAssertions.matches(UiMatcher.withProgress(newStrokeWidth))) + assertStrokePaint(getCurrentToolCanvasPaint(), newStrokeWidth, Cap.SQUARE) + onToolBarView().performCloseToolOptionsView() + } + + @Test + fun brushPickerDialogMinimumBrushWidth() { + setStrokeWidth(0, MIN_STROKE_WIDTH) + setStrokeWidth(MIN_STROKE_WIDTH) + onToolBarView().performCloseToolOptionsView() + } + + @Test + fun brushPickerAntiAliasingOffAtMinimumBrushSize() { + onView(withId(R.id.pocketpaint_stroke_width_seek_bar)).perform(UiInteractions.touchCenterLeft()) + onToolBarView().performCloseToolOptionsView() + + val bitmapPaint = getCurrentToolBitmapPaint() + val canvasPaint = getCurrentToolCanvasPaint() + + Assert.assertFalse("BITMAP_PAINT antialiasing should be off", bitmapPaint.isAntiAlias) + Assert.assertFalse("CANVAS_PAINT antialiasing should be off", canvasPaint.isAntiAlias) + } + + @Test + fun setAntiAliasingNotOnWhenCancelPressed() { + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_advanced)).perform(ViewActions.click()) + onView(withId(R.id.pocketpaint_antialiasing)).perform(ViewActions.click()) + onView(withText(R.string.cancel_button_text)).perform(ViewActions.click()) + + val bitmapPaint = getCurrentToolBitmapPaint() + val canvasPaint = getCurrentToolCanvasPaint() + + Assert.assertTrue("BITMAP_PAINT antialiasing should be on", bitmapPaint.isAntiAlias) + Assert.assertTrue("CANVAS_PAINT antialiasing should be on", canvasPaint.isAntiAlias) + } + + @Test + fun setAntiAliasingOffWhenAdvancedSettingsTurnOffAndOn() { + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_advanced)).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + onView(withText(R.string.menu_advanced)).perform(ViewActions.click()) + onView(withId(R.id.pocketpaint_antialiasing)).perform(ViewActions.click()) + onView(withText(R.string.pocketpaint_ok)).perform(ViewActions.click()) + + var bitmapPaint = getCurrentToolBitmapPaint() + var canvasPaint = getCurrentToolCanvasPaint() + + Assert.assertFalse("BITMAP_PAINT antialiasing should be off", bitmapPaint.isAntiAlias) + Assert.assertFalse("CANVAS_PAINT antialiasing should be off", canvasPaint.isAntiAlias) + + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_advanced)).perform(ViewActions.click()) + onView(withId(R.id.pocketpaint_antialiasing)).perform(ViewActions.click()) + onView(withText(R.string.pocketpaint_ok)).perform(ViewActions.click()) + + bitmapPaint = getCurrentToolBitmapPaint() + canvasPaint = getCurrentToolCanvasPaint() + + Assert.assertTrue("BITMAP_PAINT antialiasing should be on", bitmapPaint.isAntiAlias) + Assert.assertTrue("CANVAS_PAINT antialiasing should be on", canvasPaint.isAntiAlias) + } + + @Test + fun brushPickerDialogRadioButtonsBehaviour() { + onView(withId(R.id.pocketpaint_stroke_ibtn_rect)) + .check(ViewAssertions.matches(IsNot.not(ViewMatchers.isChecked()))) + onView(withId(R.id.pocketpaint_stroke_ibtn_circle)) + .check(ViewAssertions.matches(ViewMatchers.isChecked())) + onView(withId(R.id.pocketpaint_stroke_ibtn_rect)) + .perform(ViewActions.click()) + .check(ViewAssertions.matches(ViewMatchers.isChecked())) + onView(withId(R.id.pocketpaint_stroke_ibtn_circle)) + .check(ViewAssertions.matches(IsNot.not(ViewMatchers.isChecked()))) + onToolBarView().performCloseToolOptionsView() + assertStrokePaint(getCurrentToolCanvasPaint(), DEFAULT_STROKE_WIDTH, Cap.SQUARE) + onToolBarView().performOpenToolOptionsView() + onView(withId(R.id.pocketpaint_stroke_ibtn_circle)) + .perform(ViewActions.click()) + .check(ViewAssertions.matches(ViewMatchers.isChecked())) + onView(withId(R.id.pocketpaint_stroke_ibtn_rect)) + .check(ViewAssertions.matches(IsNot.not(ViewMatchers.isChecked()))) + assertStrokePaint(getCurrentToolCanvasPaint(), DEFAULT_STROKE_WIDTH, Cap.ROUND) + onToolBarView().performCloseToolOptionsView() + assertStrokePaint(getCurrentToolCanvasPaint(), DEFAULT_STROKE_WIDTH, Cap.ROUND) + } + + @Test + fun brushPickerDialogEditTextBehaviour() { + onView(withId(R.id.pocketpaint_stroke_width_width_text)) + .perform(ViewActions.replaceText(MIDDLE_STROKE_WIDTH.toString())) + closeSoftKeyboard() + onView(withId(R.id.pocketpaint_stroke_width_width_text)) + .check(ViewAssertions.matches(withText(MIDDLE_STROKE_WIDTH.toString()))) + onView(withId(R.id.pocketpaint_stroke_width_seek_bar)) + .check(ViewAssertions.matches(UiMatcher.withProgress(MIDDLE_STROKE_WIDTH))) + } + + companion object { + private const val MIN_STROKE_WIDTH = 1 + private const val MIDDLE_STROKE_WIDTH = 50 + private const val MAX_STROKE_WIDTH = 100 + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/ColorDialogIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/ColorDialogIntegrationTest.java deleted file mode 100644 index 384468a354..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/ColorDialogIntegrationTest.java +++ /dev/null @@ -1,1027 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2022 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.dialog; - -import android.app.Activity; -import android.app.Instrumentation; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.graphics.Paint; -import android.net.Uri; -import android.view.View; - -import org.catrobat.paintroid.MainActivity; -import org.catrobat.paintroid.R; -import org.catrobat.paintroid.colorpicker.HSVColorPickerView; -import org.catrobat.paintroid.colorpicker.PresetSelectorView; -import org.catrobat.paintroid.colorpicker.RgbSelectorView; -import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; -import org.catrobat.paintroid.test.espresso.util.UiInteractions; -import org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerViewInteraction; -import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; -import org.catrobat.paintroid.tools.ToolReference; -import org.catrobat.paintroid.ui.Perspective; -import org.hamcrest.core.AllOf; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -import androidx.test.espresso.IdlingRegistry; -import androidx.test.espresso.action.ViewActions; -import androidx.test.espresso.idling.CountingIdlingResource; -import androidx.test.espresso.intent.rule.IntentsTestRule; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.ActivityTestRule; - -import static org.catrobat.paintroid.common.ConstantsKt.CATROBAT_IMAGE_ENDING; -import static org.catrobat.paintroid.common.ConstantsKt.PAINTROID_PICTURE_NAME; -import static org.catrobat.paintroid.common.ConstantsKt.PAINTROID_PICTURE_PATH; -import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt; -import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchCenterLeft; -import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchCenterMiddle; -import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchCenterRight; -import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withBackground; -import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withBackgroundColor; -import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withTextColor; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerViewInteraction.onColorPickerView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.onToolProperties; -import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; - -import static androidx.test.espresso.Espresso.onData; -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.action.ViewActions.pressBack; -import static androidx.test.espresso.action.ViewActions.replaceText; -import static androidx.test.espresso.action.ViewActions.scrollTo; -import static androidx.test.espresso.action.ViewActions.swipeUp; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.intent.Intents.intending; -import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction; -import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup; -import static androidx.test.espresso.matcher.ViewMatchers.hasSibling; -import static androidx.test.espresso.matcher.ViewMatchers.hasTextColor; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.isRoot; -import static androidx.test.espresso.matcher.ViewMatchers.withClassName; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; - -@RunWith(AndroidJUnit4.class) -public class ColorDialogIntegrationTest { - - private static final String TAB_VIEW_PRESET_SELECTOR_CLASS = PresetSelectorView.class.getSimpleName(); - private static final String TAB_VIEW_HSV_SELECTOR_CLASS = HSVColorPickerView.class.getSimpleName(); - private static final String TAB_VIEW_RGBA_SELECTOR_CLASS = RgbSelectorView.class.getSimpleName(); - private static final String IMAGE_NAME = "colorDialogTestCatrobatImage"; - - private static final String TEXT_RGB_MIN = "0"; - private static final String TEXT_RGB_MAX = "255"; - - private static final String TEXT_ALPHA_MIN = "0"; - private static final String TEXT_ALPHA_MAX = "100"; - - private static final String TEXT_PERCENT_SIGN = "%"; - - @Rule - public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); - - @Rule - public IntentsTestRule launchActivityRuleWithIntent = new IntentsTestRule<>(MainActivity.class, false, false); - - @Rule - public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); - - private ToolReference toolReference; - - private List deletionFileList = new ArrayList<>(); - - @Before - public void setUp() { - toolReference = launchActivityRule.getActivity().toolReference; - } - - private int getColorById(int colorId) { - return launchActivityRule.getActivity().getResources().getColor(colorId); - } - - @Test - public void testStandardTabSelected() { - onColorPickerView() - .performOpenColorPicker(); - - onView(withClassName(containsString(TAB_VIEW_PRESET_SELECTOR_CLASS))).check(matches(isDisplayed())); - } - - @Test - public void testCorrectColorAfterApplyWithoutNewColorSelected() { - Paint initialPaint = toolReference.getTool().getDrawPaint(); - - onColorPickerView() - .performOpenColorPicker() - .onPositiveButton() - .perform(click()); - - assertEquals(initialPaint.getColor(), toolReference.getTool().getDrawPaint().getColor()); - } - - @Test - public void testTabsAreSelectable() { - onColorPickerView() - .performOpenColorPicker(); - - onView(withClassName(containsString(TAB_VIEW_PRESET_SELECTOR_CLASS))).check(matches(isDisplayed())); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_hsv))).perform(click()); - onView(withClassName(containsString(TAB_VIEW_HSV_SELECTOR_CLASS))).check(matches(isDisplayed())); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); - onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))).check(matches(isDisplayed())); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_preset))).perform(scrollTo(), click()); - onView(withClassName(containsString(TAB_VIEW_PRESET_SELECTOR_CLASS))).check(matches(isDisplayed())); - } - - @Test - public void dontShowAlphaRelatedStuffFromCatroidFormulaEditor() { - launchActivityRule.getActivity().model.setOpenedFromCatroid(true); - launchActivityRule.getActivity().model.setOpenedFromFormulaEditorInCatroid(true); - - onColorPickerView() - .performOpenColorPicker(); - - onView(withId(R.id.color_picker_base_layout)) - .perform(swipeUp()); - - onView(withId(R.id.color_alpha_slider)) - .check(matches(not(isDisplayed()))); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_hsv))).perform(click()); - - onView(withId(R.id.color_picker_base_layout)) - .perform(swipeUp()); - - onView(withId(R.id.color_alpha_slider)) - .check(matches(not(isDisplayed()))); - - onColorPickerView() - .onPositiveButton() - .perform(click()); - - int currentSelectColor = toolReference.getTool().getDrawPaint().getColor(); - - onColorPickerView() - .performOpenColorPicker(); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); - - onView(withId(R.id.color_picker_base_layout)) - .perform(swipeUp()); - - onView(withId(R.id.color_picker_alpha_row)) - .check(matches(not(isDisplayed()))); - - onView(withId(R.id.color_picker_color_rgb_hex)) - .check(matches(withText(String.format("#%02X%02X%02X", Color.red(currentSelectColor), Color.green(currentSelectColor), Color.blue(currentSelectColor))))); - } - - @Test - public void showAlphaSliderFromCatroid() { - launchActivityRule.getActivity().model.setOpenedFromCatroid(true); - - onColorPickerView() - .performOpenColorPicker(); - - onView(withId(R.id.color_picker_base_layout)) - .perform(swipeUp()); - - onView(withId(R.id.color_alpha_slider)) - .check(matches(isDisplayed())); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_hsv))).perform(click()); - - onView(withId(R.id.color_picker_base_layout)) - .perform(swipeUp()); - - onView(withId(R.id.color_alpha_slider)) - .check(matches(isDisplayed())); - } - - @Test - public void showAlphaSliderIfNotCatroidFlagSet() { - onColorPickerView() - .performOpenColorPicker(); - - onView(withId(R.id.color_picker_base_layout)) - .perform(swipeUp()); - - onView(withId(R.id.color_alpha_slider)) - .check(matches(isDisplayed())); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_hsv))).perform(click()); - - onView(withId(R.id.color_picker_base_layout)) - .perform(swipeUp()); - - onView(withId(R.id.color_alpha_slider)) - .check(matches(isDisplayed())); - } - - @Test - public void dontShowAlphaSliderInRgb() { - onColorPickerView() - .performOpenColorPicker(); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); - - onView(withId(R.id.color_picker_base_layout)) - .perform(swipeUp()); - - onView(withId(R.id.color_alpha_slider)) - .check(matches(not(isDisplayed()))); - } - - @Test - public void testColorSelectionChangesNewColorViewColor() { - - onColorPickerView() - .performOpenColorPicker(); - - final Resources resources = launchActivityRule.getActivity().getResources(); - final TypedArray presetColors = resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors); - for (int counterColors = 0; counterColors < presetColors.length(); counterColors++) { - onColorPickerView() - .performClickColorPickerPresetSelectorButton(counterColors); - - int arrayColor = presetColors.getColor(counterColors, Color.BLACK); - - onView(allOf(withId(R.id.color_picker_new_color_view), instanceOf(View.class))) - .check(matches(withBackgroundColor(arrayColor))); - } - presetColors.recycle(); - } - - @Test - public void testColorNewColorViewChangesStandard() { - final Resources resources = launchActivityRule.getActivity().getResources(); - final TypedArray presetColors = resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors); - for (int counterColors = 0; counterColors < presetColors.length(); counterColors++) { - onColorPickerView() - .performOpenColorPicker(); - - onColorPickerView() - .performClickColorPickerPresetSelectorButton(counterColors); - - onColorPickerView() - .onPositiveButton() - .perform(click()); - - int arrayColor = presetColors.getColor(counterColors, Color.BLACK); - int selectedColor = toolReference.getTool().getDrawPaint().getColor(); - - assertEquals("Color in array and selected color not the same", arrayColor, selectedColor); - } - presetColors.recycle(); - } - - @Test - public void testCurrentColorViewHasInitialColor() { - int selectedColor = toolReference.getTool().getDrawPaint().getColor(); - onColorPickerView() - .performOpenColorPicker(); - - onColorPickerView() - .checkCurrentViewColor(selectedColor); - } - - @Test - public void testCurrentColorViewDoesNotChangeColor() { - onColorPickerView() - .performOpenColorPicker(); - - int initialColor = toolReference.getTool().getDrawPaint().getColor(); - final Resources resources = launchActivityRule.getActivity().getResources(); - final TypedArray presetColors = resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors); - for (int counterColors = 0; counterColors < presetColors.length(); counterColors++) { - - onColorPickerView() - .performClickColorPickerPresetSelectorButton(counterColors); - - onView(allOf(withId(R.id.color_picker_current_color_view), instanceOf(View.class))) - .check(matches(withBackgroundColor(initialColor))); - } - presetColors.recycle(); - } - - @Test - public void testColorPickerDialogOnBackPressedSelectedColorShouldChange() { - int initialColor = toolReference.getTool().getDrawPaint().getColor(); - - onColorPickerView() - .performOpenColorPicker(); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_preset))).perform(click()); - onView(withClassName(containsString(TAB_VIEW_PRESET_SELECTOR_CLASS))).check(matches(isDisplayed())); - - TypedArray presetColors = launchActivityRule.getActivity().getResources().obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors); - int colorToSelectIndex = presetColors.length() / 2; - int colorToSelect = presetColors.getColor(colorToSelectIndex, Color.WHITE); - presetColors.recycle(); - - assertNotEquals("Selected color should not be the same as the initial color", colorToSelect, initialColor); - - onColorPickerView() - .performClickColorPickerPresetSelectorButton(colorToSelectIndex); - - // Close color picker dialog - onView(isRoot()).perform(pressBack()); - - int currentSelectedColor = toolReference.getTool().getDrawPaint().getColor(); - assertEquals("Selected color has not changed", colorToSelect, currentSelectedColor); - } - - @Test - public void testIfRGBSeekBarsDoChangeColor() { - final Resources resources = launchActivityRule.getActivity().getResources(); - final TypedArray presetColors = resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors); - - onColorPickerView() - .performOpenColorPicker(); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_preset))).perform(click()); - onView(withClassName(containsString(TAB_VIEW_PRESET_SELECTOR_CLASS))).check(matches(isDisplayed())); - - onColorPickerView() - .performClickColorPickerPresetSelectorButton(0); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); - onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))).check(matches(isDisplayed())); - - onView(withId(R.id.color_picker_color_rgb_textview_red)).perform(scrollTo()); - onView(withId(R.id.color_picker_color_rgb_textview_red)).check(matches(allOf(isDisplayed(), withText(R.string.color_red), withTextColor(getColorById(R.color.pocketpaint_color_picker_rgb_red))))); - onView(withId(R.id.color_picker_color_rgb_textview_green)).check(matches(allOf(isDisplayed(), withText(R.string.color_green), withTextColor(getColorById(R.color.pocketpaint_color_picker_rgb_green))))); - onView(withId(R.id.color_picker_color_rgb_textview_blue)).check(matches(allOf(isDisplayed(), withText(R.string.color_blue), withTextColor(getColorById(R.color.pocketpaint_color_picker_rgb_blue))))); - onView(withId(R.id.color_picker_color_rgb_textview_alpha)).perform(scrollTo()); - onView(withId(R.id.color_picker_color_rgb_textview_alpha)).check(matches(allOf(isDisplayed(), withText(R.string.color_alpha), withTextColor(getColorById(R.color.pocketpaint_color_picker_rgb_alpha))))); - - onView(withId(R.id.color_picker_color_rgb_textview_red)).perform(scrollTo()); - onView(withId(R.id.color_picker_color_rgb_seekbar_red)).check(matches(isDisplayed())); - onView(withId(R.id.color_picker_color_rgb_seekbar_green)).check(matches(isDisplayed())); - onView(withId(R.id.color_picker_color_rgb_seekbar_blue)).check(matches(isDisplayed())); - onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)).perform(scrollTo()); - onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)).check(matches(isDisplayed())); - onView(withId(R.id.color_picker_color_rgb_hex)).perform(scrollTo()); - onView(withId(R.id.color_picker_color_rgb_hex)).check(matches(isDisplayed())); - - onView(withId(R.id.color_picker_rgb_red_value)).perform(scrollTo()); - onView(withId(R.id.color_picker_rgb_red_value)).check(matches(isDisplayed())); - onView(withId(R.id.color_picker_rgb_green_value)).check(matches(isDisplayed())); - onView(withId(R.id.color_picker_rgb_blue_value)).check(matches(isDisplayed())); - onView(withId(R.id.color_picker_rgb_alpha_value)).perform(scrollTo()); - onView(withId(R.id.color_picker_rgb_alpha_value)).check(matches(isDisplayed())); - onView(allOf(withText(TEXT_PERCENT_SIGN), hasSibling(withId(R.id.color_picker_rgb_alpha_value)))).check(matches(isDisplayed())); - - int currentSelectedColor = presetColors.getColor(0, Color.BLACK); - - onView(withId(R.id.color_picker_rgb_red_value)).perform(scrollTo()); - onView(withId(R.id.color_picker_rgb_red_value)).check(matches(withText(Integer.toString(Color.red(currentSelectedColor))))); - onView(withId(R.id.color_picker_rgb_green_value)).check(matches(withText(Integer.toString(Color.green(currentSelectedColor))))); - onView(withId(R.id.color_picker_rgb_blue_value)).check(matches(withText(Integer.toString(Color.blue(currentSelectedColor))))); - onView(withId(R.id.color_picker_rgb_alpha_value)).perform(scrollTo()); - onView(withId(R.id.color_picker_rgb_alpha_value)).check(matches( - withText( - Integer.toString( - (int) (Color.alpha(currentSelectedColor) / 2.55f) - ) - ) - )); - onView(withId(R.id.color_picker_color_rgb_seekbar_red)).perform(scrollTo(), touchCenterLeft()); - onView(withId(R.id.color_picker_rgb_red_value)).check(matches(withText(TEXT_RGB_MIN))); - onView(withId(R.id.color_picker_color_rgb_seekbar_red)).perform(scrollTo(), touchCenterRight()); - onView(withId(R.id.color_picker_rgb_red_value)).check(matches(withText(TEXT_RGB_MAX))); - - onView(withId(R.id.color_picker_color_rgb_seekbar_green)).perform(scrollTo(), touchCenterLeft()); - onView(withId(R.id.color_picker_rgb_green_value)).check(matches(withText(TEXT_RGB_MIN))); - onView(withId(R.id.color_picker_color_rgb_seekbar_green)).perform(scrollTo(), touchCenterRight()); - onView(withId(R.id.color_picker_rgb_green_value)).check(matches(withText(TEXT_RGB_MAX))); - - onView(withId(R.id.color_picker_color_rgb_seekbar_blue)).perform(scrollTo(), touchCenterLeft()); - onView(withId(R.id.color_picker_rgb_blue_value)).check(matches(withText(TEXT_RGB_MIN))); - onView(withId(R.id.color_picker_color_rgb_seekbar_blue)).perform(scrollTo(), touchCenterRight()); - onView(withId(R.id.color_picker_rgb_blue_value)).check(matches(withText(TEXT_RGB_MAX))); - - onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)).perform(scrollTo(), touchCenterLeft()); - onView(withId(R.id.color_picker_rgb_alpha_value)).check(matches(withText(TEXT_ALPHA_MIN))); - onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)).perform(scrollTo(), touchCenterRight()); - onView(withId(R.id.color_picker_rgb_alpha_value)).check(matches(withText(TEXT_ALPHA_MAX))); - - // Select color red #FFFF0000 by using hex input - onView(withId(R.id.color_picker_color_rgb_hex)).perform(replaceText("#FFFF0000")); - - onColorPickerView() - .checkNewColorViewColor(Color.RED); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(scrollTo(), click()); - - // Select color blue #FF0000FF by using seekbars - onView(withId(R.id.color_picker_color_rgb_seekbar_red)).perform(scrollTo(), touchCenterLeft()); - onView(withId(R.id.color_picker_color_rgb_seekbar_green)).perform(scrollTo(), touchCenterLeft()); - onView(withId(R.id.color_picker_color_rgb_seekbar_blue)).perform(scrollTo(), touchCenterRight()); - onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)).perform(scrollTo(), touchCenterRight()); - - onColorPickerView() - .onPositiveButton() - .perform(scrollTo(), click()); - - assertNotEquals("Selected color changed to blue from black", toolReference.getTool().getDrawPaint().getColor(), Color.BLACK); - assertEquals("Selected color is not blue", toolReference.getTool().getDrawPaint().getColor(), Color.BLUE); - } - - @Test - public void testHEXEditTextMaxInputLength() { - onColorPickerView() - .performOpenColorPicker(); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); - onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))).check(matches(isDisplayed())); - - onView(withId(R.id.color_picker_color_rgb_hex)).perform(replaceText("#0123456789ABCDEF01234")); - - onView(withId(R.id.color_picker_color_rgb_hex)).check(matches( - withText( - String.format("#0123456789ABCDEF0")))); - } - - @Test - public void testHEXUpdatingOnColorChange() { - onColorPickerView() - .performOpenColorPicker(); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_preset))).perform(click()); - onView(withClassName(containsString(TAB_VIEW_PRESET_SELECTOR_CLASS))).check(matches(isDisplayed())); - - onColorPickerView() - .performClickColorPickerPresetSelectorButton(10); - - onColorPickerView() - .onPositiveButton() - .perform(click()); - - int currentSelectColor = toolReference.getTool().getDrawPaint().getColor(); - - onColorPickerView() - .performOpenColorPicker(); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); - onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))).check(matches(isDisplayed())); - - onView(withId(R.id.color_picker_color_rgb_hex)).check(matches( - withText( - String.format("#FF%06X", 0xFFFFFF & currentSelectColor)))); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_hsv))).perform(scrollTo(), click()); - onView(withClassName(containsString(TAB_VIEW_HSV_SELECTOR_CLASS))).check(matches(isDisplayed())); - - onColorPickerView().perform(scrollTo(), touchCenterMiddle()); - - onColorPickerView() - .onPositiveButton() - .perform(click()); - - currentSelectColor = toolReference.getTool().getDrawPaint().getColor(); - - onColorPickerView() - .performOpenColorPicker(); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); - onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))).check(matches(isDisplayed())); - - onView(withId(R.id.color_picker_color_rgb_hex)).check(matches( - withText( - String.format("#FF%06X", 0xFFFFFF & currentSelectColor)))); - - onView(withId(R.id.color_picker_color_rgb_seekbar_red)).perform(scrollTo(), touchCenterLeft()); - onView(withId(R.id.color_picker_color_rgb_seekbar_blue)).perform(scrollTo(), touchCenterLeft()); - onView(withId(R.id.color_picker_color_rgb_seekbar_green)).perform(scrollTo(), touchCenterLeft()); - onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)).perform(scrollTo(), touchCenterRight()); - - onColorPickerView() - .onPositiveButton() - .perform(click()); - - currentSelectColor = toolReference.getTool().getDrawPaint().getColor(); - - onColorPickerView() - .performOpenColorPicker(); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); - onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))).check(matches(isDisplayed())); - - onView(withId(R.id.color_picker_color_rgb_hex)).check(matches( - withText( - String.format("#FF%06X", 0xFFFFFF & currentSelectColor)))); - } - - @Test - public void testHEXEditTextMarkingWrongInput() { - onColorPickerView() - .performOpenColorPicker(); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); - onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))).check(matches(isDisplayed())); - - //set to invalid length of 6 (alpha missing) - onView(withId(R.id.color_picker_color_rgb_hex)).perform(replaceText("#FF0000")); - onView(withId(R.id.color_picker_color_rgb_hex)).check(matches(hasTextColor(R.color.pocketpaint_color_picker_hex_wrong_value_red))); - - //set to invalid value - onView(withId(R.id.color_picker_color_rgb_hex)).perform(replaceText("#FFXXYYZZ")); - onView(withId(R.id.color_picker_color_rgb_hex)).check(matches(hasTextColor(R.color.pocketpaint_color_picker_hex_wrong_value_red))); - - //set to invalid value (# missing) - onView(withId(R.id.color_picker_color_rgb_hex)).perform(replaceText("FF000000")); - onView(withId(R.id.color_picker_color_rgb_hex)).check(matches(hasTextColor(R.color.pocketpaint_color_picker_hex_wrong_value_red))); - } - - @Test - public void testHEXEditTextMarkingCorrectInputAfterWrongInput() { - onColorPickerView() - .performOpenColorPicker(); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); - onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))).check(matches(isDisplayed())); - - //set to invalid length of 6 (alpha missing) - onView(withId(R.id.color_picker_color_rgb_hex)).perform(replaceText("#FF0000")); - - //set correct HEX value - onView(withId(R.id.color_picker_color_rgb_hex)).perform(replaceText("#FF000000")); - onView(withId(R.id.color_picker_color_rgb_hex)).check(matches(hasTextColor(R.color.pocketpaint_color_picker_hex_correct_black))); - } - - @Test - public void testHEXEditTextInitialColorIsSetCorrectly() { - onColorPickerView() - .performOpenColorPicker(); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); - onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))).check(matches(isDisplayed())); - - //Inital text color should be black - onView(withId(R.id.color_picker_color_rgb_hex)).check(matches(hasTextColor(R.color.pocketpaint_color_picker_hex_correct_black))); - } - - @Test - public void testOpenColorPickerOnClickOnColorButton() { - onColorPickerView() - .performOpenColorPicker(); - - onView(withId(R.id.color_picker_base_layout)) - .check(matches(isDisplayed())); - onColorPickerView() - .check(matches(isDisplayed())); - } - - @Test - public void testStandardColorDoesNotChangeOnCancelButtonPress() { - int initialColor = toolReference.getTool().getDrawPaint().getColor(); - - onColorPickerView() - .performOpenColorPicker(); - - onColorPickerView() - .performClickColorPickerPresetSelectorButton(0); - - onColorPickerView() - .onNegativeButton() - .perform(click()); - - onToolProperties() - .checkMatchesColor(initialColor); - } - - @Test - public void testStandardColorDoesChangeOnCancel() { - int initialColor = toolReference.getTool().getDrawPaint().getColor(); - - onColorPickerView() - .performOpenColorPicker(); - onColorPickerView() - .performClickColorPickerPresetSelectorButton(0); - onColorPickerView() - .perform(ViewActions.pressBack()); - onToolProperties() - .checkDoesNotMatchColor(initialColor); - } - - @Test - public void testColorOnlyUpdatesOncePerColorPickerIntent() { - int initialColor = toolReference.getTool().getDrawPaint().getColor(); - - onColorPickerView() - .performOpenColorPicker(); - onColorPickerView() - .performClickColorPickerPresetSelectorButton(0); - onToolProperties() - .checkMatchesColor(initialColor); - onColorPickerView() - .performClickColorPickerPresetSelectorButton(1); - onToolProperties() - .checkMatchesColor(initialColor); - onColorPickerView() - .performClickColorPickerPresetSelectorButton(2); - onToolProperties() - .checkMatchesColor(initialColor); - - onColorPickerView() - .perform(ViewActions.pressBack()); - onToolProperties() - .checkDoesNotMatchColor(initialColor); - } - - @Test - public void testColorPickerRemainsOpenOnOrientationChange() { - onColorPickerView() - .performOpenColorPicker(); - - launchActivityRule.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - - onColorPickerView().check(matches(isDisplayed())); - } - - @Test - public void testColorPickerTabRestoredOnOrientationChange() { - onColorPickerView() - .performOpenColorPicker(); - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))) - .perform(click()); - - launchActivityRule.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - - onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))) - .check(matches(isDisplayed())); - } - - @Test - public void testColorPickerInitializesRgbTabTransparentColor() { - TypedArray presetColors = launchActivityRule.getActivity().getResources().obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors); - onColorPickerView() - .performOpenColorPicker(); - onColorPickerView() - .performClickColorPickerPresetSelectorButton(presetColors.length() - 1); - onColorPickerView() - .onPositiveButton() - .perform(click()); - onColorPickerView() - .performOpenColorPicker(); - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); - onView(withId(R.id.color_picker_rgb_red_value)).check(matches(withText(Integer.toString(Color.red(Color.TRANSPARENT))))); - onView(withId(R.id.color_picker_rgb_green_value)).check(matches(withText(Integer.toString(Color.green(Color.TRANSPARENT))))); - onView(withId(R.id.color_picker_rgb_blue_value)).check(matches(withText(Integer.toString(Color.blue(Color.TRANSPARENT))))); - onView(withId(R.id.color_picker_rgb_alpha_value)).check(matches( - withText( - Integer.toString( - (int) (Color.alpha(Color.TRANSPARENT) / 2.55f) - ) - ) - )); - presetColors.recycle(); - } - - @Test - public void testInsertInvalidHexInputAndSlideSeekbar() { - onColorPickerView() - .performOpenColorPicker(); - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); - onView(withId(R.id.color_picker_color_rgb_hex)).perform(replaceText("#FFFF0000xxxx")); - - onView(withId(R.id.color_picker_color_rgb_seekbar_blue)).perform(scrollTo(), touchCenterRight()); - onView(withId(R.id.color_picker_color_rgb_hex)).perform(scrollTo()); - onView(withId(R.id.color_picker_color_rgb_hex)).check(matches( - withText( - String.format("#FF%06X", 0xFFFFFF & 0xFF0000FF)))); - } - - @Test - public void testPipetteButtonIsDisplayed() { - onColorPickerView() - .performOpenColorPicker(); - - onView(withId(R.id.color_picker_pipette_btn)) - .check(matches(isDisplayed())) - .check(matches(withText(R.string.color_picker_pipette))); - } - - @Test - public void testColorViewsAreDisplayed() { - onColorPickerView() - .performOpenColorPicker(); - - onView(withId(R.id.color_picker_new_color_view)) - .check(matches(isDisplayed())) - .check(matches(withBackgroundColor(Color.BLACK))); - - onView(withId(R.id.color_picker_current_color_view)) - .check(matches(isDisplayed())) - .check(matches(withBackgroundColor(Color.BLACK))); - } - - @Test - public void alphaValueIsSetInSliderWhenChangedInSeekBar() { - CountingIdlingResource idlingResource = launchActivityRule.getActivity().getIdlingResource(); - IdlingRegistry.getInstance().register(idlingResource); - onColorPickerView() - .performOpenColorPicker(); - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); - - onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)).perform(touchCenterMiddle()); - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_preset))).perform(scrollTo(), click()); - onColorPickerView() - .onPositiveButton() - .perform(click()); - onToolProperties() - .checkMatchesColor(Color.parseColor("#7F000000")); - IdlingRegistry.getInstance().unregister(idlingResource); - } - - @Test - public void alphaValueIsSetInSeekBarWhenChangedInSlider() { - onColorPickerView() - .performOpenColorPicker(); - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_preset))).perform(click()); - - onView(withId(R.id.color_alpha_slider)).perform(scrollTo(), touchCenterMiddle()); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); - onView(withId(R.id.color_picker_rgb_alpha_value)).check(matches( - withText("50") - )); - } - - @Test - public void testPreserveZoomAfterPipetteUsage() { - Perspective perspective = launchActivityRule.getActivity().getPerspective(); - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - float scale = 4f; - - perspective.setScale(scale); - - onColorPickerView() - .performOpenColorPicker(); - - onView(withId(R.id.color_picker_pipette_btn)).perform(click()); - onView(withId(R.id.doneAction)).perform(click()); - - onColorPickerView() - .performCloseColorPickerWithDialogButton(); - - assertEquals(scale, perspective.getScale(), Float.MIN_VALUE); - } - - @Test - public void testColorHistoryShowsPresetSelectorColors() { - final Resources resources = launchActivityRule.getActivity().getResources(); - final TypedArray presetColors = resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors); - - for (int counterColors = 0; counterColors < presetColors.length() && counterColors < ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY; counterColors++) { - onColorPickerView() - .performOpenColorPicker(); - onColorPickerView() - .performClickColorPickerPresetSelectorButton(counterColors); - onColorPickerView() - .onPositiveButton() - .perform(click()); - } - - for (int counterColors = 0; counterColors < presetColors.length() && counterColors < ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY; counterColors++) { - onColorPickerView() - .performOpenColorPicker(); - onColorPickerView() - .performClickOnHistoryColor(MAXIMUM_COLORS_IN_HISTORY - 1); - - onColorPickerView() - .onPositiveButton() - .perform(click()); - - int arrayColor = presetColors.getColor(counterColors, Color.BLACK); - int selectedColor = Objects.requireNonNull(toolReference.getTool()).getDrawPaint().getColor(); - - assertEquals("Color in history doesn't match selection", arrayColor, selectedColor); - } - presetColors.recycle(); - } - - @Test - public void testColorHistorySelectMoreThanMaxHistoryColors() { - final Resources resources = launchActivityRule.getActivity().getResources(); - final TypedArray presetColors = resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors); - - for (int counterColors = 0; counterColors < presetColors.length() && counterColors <= ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY; counterColors++) { - onColorPickerView() - .performOpenColorPicker(); - onColorPickerView() - .performClickColorPickerPresetSelectorButton(counterColors); - onColorPickerView() - .onPositiveButton() - .perform(click()); - } - onColorPickerView() - .performOpenColorPicker(); - - for (int historyCounter = 0, colorCounter = MAXIMUM_COLORS_IN_HISTORY; historyCounter < ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY; historyCounter++, colorCounter--) { - onColorPickerView() - .checkHistoryColor(historyCounter, presetColors.getColor(colorCounter, Color.BLACK)); - } - presetColors.recycle(); - } - - @Test - public void testColorHistoryShowsRGBSelectorColors() { - launchActivityRule.getActivity(); - - onColorPickerView() - .performOpenColorPicker(); - - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); - onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))).check(matches(isDisplayed())); - - onView(withId(R.id.color_picker_color_rgb_seekbar_red)).perform(scrollTo(), touchCenterRight()); - onView(withId(R.id.color_picker_color_rgb_seekbar_green)).perform(scrollTo(), touchCenterRight()); - onView(withId(R.id.color_picker_color_rgb_seekbar_blue)).perform(scrollTo(), touchCenterRight()); - onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)).perform(scrollTo(), touchCenterRight()); - onColorPickerView() - .onPositiveButton() - .perform(click()); - - onColorPickerView() - .performOpenColorPicker(); - onColorPickerView().checkHistoryColor(0, 0xFFFFFFFF); - onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); - onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))).check(matches(isDisplayed())); - onView(withId(R.id.color_picker_color_rgb_seekbar_red)).perform(scrollTo(), touchCenterLeft()); - onView(withId(R.id.color_picker_color_rgb_seekbar_green)).perform(scrollTo(), touchCenterLeft()); - onView(withId(R.id.color_picker_color_rgb_seekbar_blue)).perform(scrollTo(), touchCenterLeft()); - onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)).perform(scrollTo(), touchCenterLeft()); - onColorPickerView() - .onPositiveButton() - .perform(click()); - - onColorPickerView() - .performOpenColorPicker(); - onColorPickerView().checkHistoryColor(0, 0x00000000); - } - - @Test - public void testColorHistoryPreservedWhenClickingNewImage() { - final Resources resources = launchActivityRule.getActivity().getResources(); - final TypedArray presetColors = resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors); - - for (int counterColors = 0; counterColors < presetColors.length() && counterColors < ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY; counterColors++) { - onColorPickerView() - .performOpenColorPicker(); - onColorPickerView() - .performClickColorPickerPresetSelectorButton(counterColors); - onColorPickerView() - .onPositiveButton() - .perform(click()); - } - - onTopBarView().performOpenMoreOptions(); - onView(withText(R.string.menu_new_image)).perform(click()); - onView(withText(R.string.discard_button_text)).perform(click()); - - onColorPickerView() - .performOpenColorPicker(); - onColorPickerView().checkHistoryColor(3, presetColors.getColor(0, Color.BLACK)); - presetColors.recycle(); - } - - @Test - public void testColorHistoryDeletedWhenRestartingApp() { - final Resources resources = launchActivityRule.getActivity().getResources(); - final TypedArray presetColors = resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors); - onColorPickerView() - .performOpenColorPicker(); - for (int counterColors = 0; counterColors < presetColors.length() && counterColors < ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY; counterColors++) { - onColorPickerView() - .performClickColorPickerPresetSelectorButton(counterColors); - } - launchActivityRule.finishActivity(); - launchActivityRule.launchActivity(new Intent()); - - onColorPickerView() - .performOpenColorPicker(); - onView(withId(R.id.color_history_text_view)).check(matches(not(isDisplayed()))); - presetColors.recycle(); - } - - @Test - public void testSaveColorHistoryInCatrobatFile() { - final MainActivity activity = launchActivityRule.getActivity(); - final Resources resources = activity.getResources(); - final TypedArray presetColors = resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors); - - for (int counterColors = 0; counterColors < presetColors.length() && counterColors < ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY; counterColors++) { - onColorPickerView() - .performOpenColorPicker(); - onColorPickerView() - .performClickColorPickerPresetSelectorButton(counterColors); - onColorPickerView() - .onPositiveButton() - .perform(click()); - } - - saveCatrobatImage(); - Uri uri = activity.model.getSavedPictureUri(); - launchActivityRule.finishActivity(); - - Intent intent = new Intent(); - intent.putExtra(PAINTROID_PICTURE_PATH, ""); - intent.putExtra(PAINTROID_PICTURE_NAME, IMAGE_NAME); - launchActivityRuleWithIntent.launchActivity(intent); - intent = new Intent(); - intent.setData(uri); - Instrumentation.ActivityResult resultOK = new Instrumentation.ActivityResult(Activity.RESULT_OK, intent); - intending(hasAction(Intent.ACTION_GET_CONTENT)).respondWith(resultOK); - - for (int counterColors = 0; counterColors + MAXIMUM_COLORS_IN_HISTORY < presetColors.length() && counterColors < ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY; counterColors++) { - onColorPickerView() - .performOpenColorPicker(); - onColorPickerView() - .performClickColorPickerPresetSelectorButton(counterColors + MAXIMUM_COLORS_IN_HISTORY); - onColorPickerView() - .onPositiveButton() - .perform(click()); - } - - onDrawingSurfaceView() - .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onTopBarView().performOpenMoreOptions(); - onView(withText(R.string.menu_load_image)).perform(click()); - onView(withText(R.string.menu_replace_image)).perform(click()); - onView(withText(R.string.discard_button_text)).perform(click()); - - onColorPickerView() - .performOpenColorPicker(); - onColorPickerView().checkHistoryColor(3, presetColors.getColor(0, Color.BLACK)); - presetColors.recycle(); - if (!deletionFileList.isEmpty() && deletionFileList.get(0) != null && deletionFileList.get(0).exists()) { - assertTrue(deletionFileList.get(0).delete()); - } - } - - private void saveCatrobatImage() { - onTopBarView() - .performOpenMoreOptions(); - onView(withText(R.string.menu_save_image)) - .perform(scrollTo(), click()); - onView(withId(R.id.pocketpaint_save_dialog_spinner)) - .perform(click()); - onData(AllOf.allOf(is(instanceOf(String.class)), - is(CATROBAT_IMAGE_ENDING))).inRoot(isPlatformPopup()).perform(click()); - - onView(withId(R.id.pocketpaint_image_name_save_text)) - .perform(replaceText(IMAGE_NAME)); - onView(withText(R.string.save_button_text)) - .perform(ViewActions.click()); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/ColorDialogIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/ColorDialogIntegrationTest.kt new file mode 100644 index 0000000000..883ea0c755 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/ColorDialogIntegrationTest.kt @@ -0,0 +1,1409 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.dialog + +import android.app.Activity +import android.app.Instrumentation.ActivityResult +import android.content.Intent +import android.content.pm.ActivityInfo +import android.graphics.Color +import android.os.Environment +import android.view.View +import androidx.test.espresso.Espresso +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.intent.Intents +import androidx.test.espresso.intent.matcher.IntentMatchers +import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.test.espresso.matcher.RootMatchers +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.ActivityTestRule +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.R +import org.catrobat.paintroid.colorpicker.HSVColorPickerView +import org.catrobat.paintroid.colorpicker.PresetSelectorView +import org.catrobat.paintroid.colorpicker.RgbSelectorView +import org.catrobat.paintroid.common.CATROBAT_IMAGE_ENDING +import org.catrobat.paintroid.common.PAINTROID_PICTURE_NAME +import org.catrobat.paintroid.common.PAINTROID_PICTURE_PATH +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider +import org.catrobat.paintroid.test.espresso.util.UiInteractions +import org.catrobat.paintroid.test.espresso.util.UiMatcher +import org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerViewInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.catrobat.paintroid.tools.ToolReference +import org.hamcrest.Matchers +import org.hamcrest.core.AllOf.allOf +import org.junit.Assert +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import java.io.File +import java.util.Objects + +@RunWith(AndroidJUnit4::class) +class ColorDialogIntegrationTest { + @get:Rule + var launchActivityRule = ActivityTestRule( + MainActivity::class.java + ) + + @get:Rule + var launchActivityRuleWithIntent = IntentsTestRule( + MainActivity::class.java, false, false + ) + + @get:Rule + var screenshotOnFailRule = ScreenshotOnFailRule() + private var toolReference: ToolReference? = null + + @Before + fun setUp() { + toolReference = launchActivityRule.activity.toolReference + } + + @After + fun tearDown() { + val imagesDirectory = + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString() + val pathToFile = imagesDirectory + File.separator + IMAGE_NAME + "." + CATROBAT_IMAGE_ENDING + val imageFile = File(pathToFile) + if (imageFile.exists()) { + imageFile.delete() + } + } + + private fun getColorById(colorId: Int) = + launchActivityRule.activity.resources.getColor(colorId) + + @Test + fun testStandardTabSelected() { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + ViewMatchers.withClassName( + Matchers.containsString( + TAB_VIEW_PRESET_SELECTOR_CLASS + ) + ) + ).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + + @Test + fun testCorrectColorAfterApplyWithoutNewColorSelected() { + val initialPaint = toolReference!!.tool!!.drawPaint + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + .onPositiveButton() + .perform(ViewActions.click()) + Assert.assertEquals( + initialPaint.color.toLong(), + toolReference!!.tool!!.drawPaint.color.toLong() + ) + } + + @Test + fun testTabsAreSelectable() { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + ViewMatchers.withClassName( + Matchers.containsString( + TAB_VIEW_PRESET_SELECTOR_CLASS + ) + ) + ).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_hsv) + ) + ).perform(ViewActions.click()) + Espresso.onView( + ViewMatchers.withClassName( + Matchers.containsString( + TAB_VIEW_HSV_SELECTOR_CLASS + ) + ) + ).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(ViewActions.click()) + Espresso.onView( + ViewMatchers.withClassName( + Matchers.containsString( + TAB_VIEW_RGBA_SELECTOR_CLASS + ) + ) + ).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_preset) + ) + ).perform(ViewActions.scrollTo(), ViewActions.click()) + Espresso.onView( + ViewMatchers.withClassName( + Matchers.containsString( + TAB_VIEW_PRESET_SELECTOR_CLASS + ) + ) + ).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + + @Test + fun dontShowAlphaRelatedStuffFromCatroidFormulaEditor() { + launchActivityRule.activity.model.isOpenedFromCatroid = true + launchActivityRule.activity.model.isOpenedFromFormulaEditorInCatroid = true + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView(withId(R.id.color_picker_base_layout)) + .perform(ViewActions.swipeUp()) + Espresso.onView(withId(R.id.color_alpha_slider)) + .check(ViewAssertions.matches(Matchers.not(ViewMatchers.isDisplayed()))) + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_hsv) + ) + ).perform(ViewActions.click()) + Espresso.onView(withId(R.id.color_picker_base_layout)) + .perform(ViewActions.swipeUp()) + Espresso.onView(withId(R.id.color_alpha_slider)) + .check(ViewAssertions.matches(Matchers.not(ViewMatchers.isDisplayed()))) + ColorPickerViewInteraction.onColorPickerView() + .onPositiveButton() + .perform(ViewActions.click()) + val currentSelectColor = toolReference!!.tool!!.drawPaint.color + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(ViewActions.click()) + Espresso.onView(withId(R.id.color_picker_base_layout)) + .perform(ViewActions.swipeUp()) + Espresso.onView(withId(R.id.color_picker_alpha_row)) + .check(ViewAssertions.matches(Matchers.not(ViewMatchers.isDisplayed()))) + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)) + .check( + ViewAssertions.matches( + withText( + String.format( + "#%02X%02X%02X", + Color.red( + currentSelectColor + ), + Color.green( + currentSelectColor + ), + Color.blue( + currentSelectColor + ) + ) + ) + ) + ) + } + + @Test + fun showAlphaSliderFromCatroid() { + launchActivityRule.activity.model.isOpenedFromCatroid = true + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView(withId(R.id.color_picker_base_layout)) + .perform(ViewActions.swipeUp()) + Espresso.onView(withId(R.id.color_alpha_slider)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_hsv) + ) + ).perform(ViewActions.click()) + Espresso.onView(withId(R.id.color_picker_base_layout)) + .perform(ViewActions.swipeUp()) + Espresso.onView(withId(R.id.color_alpha_slider)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + + @Test + fun showAlphaSliderIfNotCatroidFlagSet() { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView(withId(R.id.color_picker_base_layout)) + .perform(ViewActions.swipeUp()) + Espresso.onView(withId(R.id.color_alpha_slider)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_hsv) + ) + ).perform(ViewActions.click()) + Espresso.onView(withId(R.id.color_picker_base_layout)) + .perform(ViewActions.swipeUp()) + Espresso.onView(withId(R.id.color_alpha_slider)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + + @Test + fun dontShowAlphaSliderInRgb() { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(ViewActions.click()) + Espresso.onView(withId(R.id.color_picker_base_layout)) + .perform(ViewActions.swipeUp()) + Espresso.onView(withId(R.id.color_alpha_slider)) + .check(ViewAssertions.matches(Matchers.not(ViewMatchers.isDisplayed()))) + } + + @Test + fun testColorSelectionChangesNewColorViewColor() { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + val resources = launchActivityRule.activity.resources + val presetColors = + resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors) + for (counterColors in 0 until presetColors.length()) { + ColorPickerViewInteraction.onColorPickerView() + .performClickColorPickerPresetSelectorButton(counterColors) + val arrayColor = presetColors.getColor(counterColors, Color.BLACK) + Espresso.onView( + allOf( + withId(R.id.color_picker_new_color_view), Matchers.instanceOf( + View::class.java + ) + ) + ) + .check(ViewAssertions.matches(UiMatcher.withBackgroundColor(arrayColor))) + } + presetColors.recycle() + } + + @Test + fun testColorNewColorViewChangesStandard() { + val resources = launchActivityRule.activity.resources + val presetColors = + resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors) + for (counterColors in 0 until presetColors.length()) { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + ColorPickerViewInteraction.onColorPickerView() + .performClickColorPickerPresetSelectorButton(counterColors) + ColorPickerViewInteraction.onColorPickerView() + .onPositiveButton() + .perform(ViewActions.click()) + val arrayColor = presetColors.getColor(counterColors, Color.BLACK) + val selectedColor = toolReference!!.tool!!.drawPaint.color + Assert.assertEquals( + "Color in array and selected color not the same", + arrayColor.toLong(), + selectedColor.toLong() + ) + } + presetColors.recycle() + } + + @Test + fun testCurrentColorViewHasInitialColor() { + val selectedColor = toolReference!!.tool!!.drawPaint.color + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + ColorPickerViewInteraction.onColorPickerView() + .checkCurrentViewColor(selectedColor) + } + + @Test + fun testCurrentColorViewDoesNotChangeColor() { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + val initialColor = toolReference!!.tool!!.drawPaint.color + val resources = launchActivityRule.activity.resources + val presetColors = + resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors) + for (counterColors in 0 until presetColors.length()) { + ColorPickerViewInteraction.onColorPickerView() + .performClickColorPickerPresetSelectorButton(counterColors) + Espresso.onView( + allOf( + withId(R.id.color_picker_current_color_view), Matchers.instanceOf( + View::class.java + ) + ) + ) + .check(ViewAssertions.matches(UiMatcher.withBackgroundColor(initialColor))) + } + presetColors.recycle() + } + + @Test + fun testColorPickerDialogOnBackPressedSelectedColorShouldChange() { + val initialColor = toolReference!!.tool!!.drawPaint.color + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_preset) + ) + ).perform(ViewActions.click()) + Espresso.onView( + ViewMatchers.withClassName( + Matchers.containsString( + TAB_VIEW_PRESET_SELECTOR_CLASS + ) + ) + ).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + val presetColors = + launchActivityRule.activity.resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors) + val colorToSelectIndex = presetColors.length() / 2 + val colorToSelect = presetColors.getColor(colorToSelectIndex, Color.WHITE) + presetColors.recycle() + Assert.assertNotEquals( + "Selected color should not be the same as the initial color", + colorToSelect.toLong(), + initialColor.toLong() + ) + ColorPickerViewInteraction.onColorPickerView() + .performClickColorPickerPresetSelectorButton(colorToSelectIndex) + + Espresso.onView(ViewMatchers.isRoot()).perform(ViewActions.pressBack()) + val currentSelectedColor = toolReference!!.tool!!.drawPaint.color + Assert.assertEquals( + "Selected color has not changed", + colorToSelect.toLong(), + currentSelectedColor.toLong() + ) + } + + @Suppress("LongMethod") + @Test + fun testIfRGBSeekBarsDoChangeColor() { + val resources = launchActivityRule.activity.resources + val presetColors = + resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors) + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_preset) + ) + ).perform(ViewActions.click()) + Espresso.onView( + ViewMatchers.withClassName( + Matchers.containsString( + TAB_VIEW_PRESET_SELECTOR_CLASS + ) + ) + ).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + ColorPickerViewInteraction.onColorPickerView() + .performClickColorPickerPresetSelectorButton(0) + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(ViewActions.click()) + Espresso.onView( + ViewMatchers.withClassName( + Matchers.containsString( + TAB_VIEW_RGBA_SELECTOR_CLASS + ) + ) + ).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.color_picker_color_rgb_textview_red)) + .perform(ViewActions.scrollTo()) + Espresso.onView(withId(R.id.color_picker_color_rgb_textview_red)).check( + ViewAssertions.matches( + allOf( + ViewMatchers.isDisplayed(), + withText(R.string.color_red), + UiMatcher.withTextColor(getColorById(R.color.pocketpaint_color_picker_rgb_red)) + ) + ) + ) + Espresso.onView(withId(R.id.color_picker_color_rgb_textview_green)).check( + ViewAssertions.matches( + allOf( + ViewMatchers.isDisplayed(), + withText(R.string.color_green), + UiMatcher.withTextColor(getColorById(R.color.pocketpaint_color_picker_rgb_green)) + ) + ) + ) + Espresso.onView(withId(R.id.color_picker_color_rgb_textview_blue)).check( + ViewAssertions.matches( + allOf( + ViewMatchers.isDisplayed(), + withText(R.string.color_blue), + UiMatcher.withTextColor(getColorById(R.color.pocketpaint_color_picker_rgb_blue)) + ) + ) + ) + Espresso.onView(withId(R.id.color_picker_color_rgb_textview_alpha)) + .perform(ViewActions.scrollTo()) + Espresso.onView(withId(R.id.color_picker_color_rgb_textview_alpha)).check( + ViewAssertions.matches( + allOf( + ViewMatchers.isDisplayed(), + withText(R.string.color_alpha), + UiMatcher.withTextColor(getColorById(R.color.pocketpaint_color_picker_hex_black)) + ) + ) + ) + Espresso.onView(withId(R.id.color_picker_color_rgb_textview_red)) + .perform(ViewActions.scrollTo()) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_red)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_green)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_blue)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)) + .perform(ViewActions.scrollTo()) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)).perform(ViewActions.scrollTo()) + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.color_picker_rgb_red_value)).perform(ViewActions.scrollTo()) + Espresso.onView(withId(R.id.color_picker_rgb_red_value)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.color_picker_rgb_green_value)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.color_picker_rgb_blue_value)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.color_picker_rgb_alpha_value)).perform(ViewActions.scrollTo()) + Espresso.onView(withId(R.id.color_picker_rgb_alpha_value)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView( + Matchers.allOf( + withText(TEXT_PERCENT_SIGN), + ViewMatchers.hasSibling(withId(R.id.color_picker_rgb_alpha_value)) + ) + ).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + val currentSelectedColor = presetColors.getColor(0, Color.BLACK) + Espresso.onView(withId(R.id.color_picker_rgb_red_value)).perform(ViewActions.scrollTo()) + Espresso.onView(withId(R.id.color_picker_rgb_red_value)).check( + ViewAssertions.matches( + withText( + Color.red(currentSelectedColor).toString() + ) + ) + ) + Espresso.onView(withId(R.id.color_picker_rgb_green_value)).check( + ViewAssertions.matches( + withText( + Color.green(currentSelectedColor).toString() + ) + ) + ) + Espresso.onView(withId(R.id.color_picker_rgb_blue_value)).check( + ViewAssertions.matches( + withText( + Color.blue(currentSelectedColor).toString() + ) + ) + ) + Espresso.onView(withId(R.id.color_picker_rgb_alpha_value)).perform(ViewActions.scrollTo()) + Espresso.onView(withId(R.id.color_picker_rgb_alpha_value)).check( + ViewAssertions.matches( + withText( + (Color.alpha(currentSelectedColor) / 2.55f).toInt().toString() + ) + ) + ) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_red)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterLeft()) + Espresso.onView(withId(R.id.color_picker_rgb_red_value)).check( + ViewAssertions.matches( + withText( + TEXT_RGB_MIN + ) + ) + ) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_red)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterRight()) + Espresso.onView(withId(R.id.color_picker_rgb_red_value)).check( + ViewAssertions.matches( + withText( + TEXT_RGB_MAX + ) + ) + ) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_green)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterLeft()) + Espresso.onView(withId(R.id.color_picker_rgb_green_value)).check( + ViewAssertions.matches( + withText( + TEXT_RGB_MIN + ) + ) + ) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_green)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterRight()) + Espresso.onView(withId(R.id.color_picker_rgb_green_value)).check( + ViewAssertions.matches( + withText( + TEXT_RGB_MAX + ) + ) + ) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_blue)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterLeft()) + Espresso.onView(withId(R.id.color_picker_rgb_blue_value)).check( + ViewAssertions.matches( + withText( + TEXT_RGB_MIN + ) + ) + ) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_blue)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterRight()) + Espresso.onView(withId(R.id.color_picker_rgb_blue_value)).check( + ViewAssertions.matches( + withText( + TEXT_RGB_MAX + ) + ) + ) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterLeft()) + Espresso.onView(withId(R.id.color_picker_rgb_alpha_value)).check( + ViewAssertions.matches( + withText( + TEXT_ALPHA_MIN + ) + ) + ) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterRight()) + Espresso.onView(withId(R.id.color_picker_rgb_alpha_value)).check( + ViewAssertions.matches( + withText( + TEXT_ALPHA_MAX + ) + ) + ) + + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)) + .perform(ViewActions.replaceText("#FFFF0000")) + ColorPickerViewInteraction.onColorPickerView() + .checkNewColorViewColor(Color.RED) + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(ViewActions.scrollTo(), ViewActions.click()) + + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_red)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterLeft()) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_green)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterLeft()) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_blue)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterRight()) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterRight()) + ColorPickerViewInteraction.onColorPickerView() + .onPositiveButton() + .perform(ViewActions.scrollTo(), ViewActions.click()) + Assert.assertNotEquals( + "Selected color changed to blue from black", + toolReference!!.tool!!.drawPaint.color.toLong(), + Color.BLACK.toLong() + ) + Assert.assertEquals( + "Selected color is not blue", + toolReference!!.tool!!.drawPaint.color.toLong(), + Color.BLUE.toLong() + ) + } + + @Test + fun testHEXEditTextMaxInputLength() { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(ViewActions.click()) + Espresso.onView( + ViewMatchers.withClassName( + Matchers.containsString( + TAB_VIEW_RGBA_SELECTOR_CLASS + ) + ) + ).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)) + .perform(ViewActions.replaceText("#0123456789ABCDEF01234")) + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)).check( + ViewAssertions.matches( + withText(String.format("#0123456789ABCDEF0")) + ) + ) + } + + @Suppress("LongMethod") + @Test + fun testHEXUpdatingOnColorChange() { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_preset) + ) + ).perform(ViewActions.click()) + Espresso.onView( + ViewMatchers.withClassName( + Matchers.containsString( + TAB_VIEW_PRESET_SELECTOR_CLASS + ) + ) + ).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + ColorPickerViewInteraction.onColorPickerView() + .performClickColorPickerPresetSelectorButton(10) + ColorPickerViewInteraction.onColorPickerView() + .onPositiveButton() + .perform(ViewActions.click()) + var currentSelectColor = toolReference!!.tool!!.drawPaint.color + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(ViewActions.click()) + Espresso.onView( + ViewMatchers.withClassName( + Matchers.containsString( + TAB_VIEW_RGBA_SELECTOR_CLASS + ) + ) + ).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)).check( + ViewAssertions.matches( + withText(String.format("#FF%06X", 0xFFFFFF and currentSelectColor)) + ) + ) + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_hsv) + ) + ).perform(ViewActions.scrollTo(), ViewActions.click()) + Espresso.onView( + ViewMatchers.withClassName( + Matchers.containsString( + TAB_VIEW_HSV_SELECTOR_CLASS + ) + ) + ).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + ColorPickerViewInteraction.onColorPickerView() + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterMiddle()) + ColorPickerViewInteraction.onColorPickerView() + .onPositiveButton() + .perform(ViewActions.click()) + currentSelectColor = toolReference!!.tool!!.drawPaint.color + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(ViewActions.click()) + Espresso.onView( + ViewMatchers.withClassName( + Matchers.containsString( + TAB_VIEW_RGBA_SELECTOR_CLASS + ) + ) + ).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)).check( + ViewAssertions.matches( + withText(String.format("#FF%06X", 0xFFFFFF and currentSelectColor)) + ) + ) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_red)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterLeft()) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_blue)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterLeft()) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_green)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterLeft()) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterRight()) + ColorPickerViewInteraction.onColorPickerView() + .onPositiveButton() + .perform(ViewActions.click()) + currentSelectColor = toolReference!!.tool!!.drawPaint.color + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(ViewActions.click()) + Espresso.onView( + ViewMatchers.withClassName( + Matchers.containsString( + TAB_VIEW_RGBA_SELECTOR_CLASS + ) + ) + ).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)).check( + ViewAssertions.matches( + withText(String.format("#FF%06X", 0xFFFFFF and currentSelectColor)) + ) + ) + } + + @Test + fun testHEXEditTextMarkingWrongInput() { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(ViewActions.click()) + Espresso.onView( + ViewMatchers.withClassName( + Matchers.containsString( + TAB_VIEW_RGBA_SELECTOR_CLASS + ) + ) + ).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)) + .perform(ViewActions.replaceText("#FF0000")) + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)) + .check(ViewAssertions.matches(ViewMatchers.hasTextColor(R.color.pocketpaint_color_picker_hex_wrong_value_red))) + + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)) + .perform(ViewActions.replaceText("#FFXXYYZZ")) + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)) + .check(ViewAssertions.matches(ViewMatchers.hasTextColor(R.color.pocketpaint_color_picker_hex_wrong_value_red))) + + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)) + .perform(ViewActions.replaceText("FF000000")) + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)) + .check(ViewAssertions.matches(ViewMatchers.hasTextColor(R.color.pocketpaint_color_picker_hex_wrong_value_red))) + } + + @Test + fun testHEXEditTextMarkingCorrectInputAfterWrongInput() { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(ViewActions.click()) + Espresso.onView( + ViewMatchers.withClassName( + Matchers.containsString( + TAB_VIEW_RGBA_SELECTOR_CLASS + ) + ) + ).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)) + .perform(ViewActions.replaceText("#FF0000")) + + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)) + .perform(ViewActions.replaceText("#FF000000")) + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)) + .check(ViewAssertions.matches(ViewMatchers.hasTextColor(R.color.pocketpaint_color_picker_hex_correct_black))) + } + + @Test + fun testHEXEditTextInitialColorIsSetCorrectly() { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(ViewActions.click()) + Espresso.onView( + ViewMatchers.withClassName( + Matchers.containsString( + TAB_VIEW_RGBA_SELECTOR_CLASS + ) + ) + ).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)) + .check(ViewAssertions.matches(ViewMatchers.hasTextColor(R.color.pocketpaint_color_picker_hex_correct_black))) + } + + @Test + fun testOpenColorPickerOnClickOnColorButton() { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView(withId(R.id.color_picker_base_layout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + ColorPickerViewInteraction.onColorPickerView() + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + + @Test + fun testStandardColorDoesNotChangeOnCancelButtonPress() { + val initialColor = toolReference!!.tool!!.drawPaint.color + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + ColorPickerViewInteraction.onColorPickerView() + .performClickColorPickerPresetSelectorButton(0) + ColorPickerViewInteraction.onColorPickerView() + .onNegativeButton() + .perform(ViewActions.click()) + ToolPropertiesInteraction.onToolProperties() + .checkMatchesColor(initialColor) + } + + @Test + fun testStandardColorDoesChangeOnCancel() { + val initialColor = toolReference!!.tool!!.drawPaint.color + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + ColorPickerViewInteraction.onColorPickerView() + .performClickColorPickerPresetSelectorButton(0) + ColorPickerViewInteraction.onColorPickerView() + .perform(ViewActions.pressBack()) + ToolPropertiesInteraction.onToolProperties() + .checkDoesNotMatchColor(initialColor) + } + + @Test + fun testColorOnlyUpdatesOncePerColorPickerIntent() { + val initialColor = toolReference!!.tool!!.drawPaint.color + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + ColorPickerViewInteraction.onColorPickerView() + .performClickColorPickerPresetSelectorButton(0) + ToolPropertiesInteraction.onToolProperties() + .checkMatchesColor(initialColor) + ColorPickerViewInteraction.onColorPickerView() + .performClickColorPickerPresetSelectorButton(1) + ToolPropertiesInteraction.onToolProperties() + .checkMatchesColor(initialColor) + ColorPickerViewInteraction.onColorPickerView() + .performClickColorPickerPresetSelectorButton(2) + ToolPropertiesInteraction.onToolProperties() + .checkMatchesColor(initialColor) + ColorPickerViewInteraction.onColorPickerView() + .perform(ViewActions.pressBack()) + ToolPropertiesInteraction.onToolProperties() + .checkDoesNotMatchColor(initialColor) + } + + @Test + fun testColorPickerRemainsOpenOnOrientationChange() { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + launchActivityRule.activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + ColorPickerViewInteraction.onColorPickerView() + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + + @Test + fun testColorPickerTabRestoredOnOrientationChange() { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ) + .perform(ViewActions.click()) + launchActivityRule.activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + Espresso.onView( + ViewMatchers.withClassName( + Matchers.containsString( + TAB_VIEW_RGBA_SELECTOR_CLASS + ) + ) + ) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + + @Test + fun testColorPickerInitializesRgbTabTransparentColor() { + val presetColors = + launchActivityRule.activity.resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors) + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + ColorPickerViewInteraction.onColorPickerView() + .performClickColorPickerPresetSelectorButton(presetColors.length() - 1) + ColorPickerViewInteraction.onColorPickerView() + .onPositiveButton() + .perform(ViewActions.click()) + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(ViewActions.click()) + Espresso.onView(withId(R.id.color_picker_rgb_red_value)).check( + ViewAssertions.matches( + withText( + Color.red(Color.TRANSPARENT).toString() + ) + ) + ) + Espresso.onView(withId(R.id.color_picker_rgb_green_value)).check( + ViewAssertions.matches( + withText( + Color.green(Color.TRANSPARENT).toString() + ) + ) + ) + Espresso.onView(withId(R.id.color_picker_rgb_blue_value)).check( + ViewAssertions.matches( + withText( + Color.blue(Color.TRANSPARENT).toString() + ) + ) + ) + Espresso.onView(withId(R.id.color_picker_rgb_alpha_value)).check( + ViewAssertions.matches( + withText( + (Color.alpha(Color.TRANSPARENT) / 2.55f).toInt().toString() + ) + ) + ) + presetColors.recycle() + } + + @Test + fun testInsertInvalidHexInputAndSlideSeekbar() { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(ViewActions.click()) + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)) + .perform(ViewActions.replaceText("#FFFF0000xxxx")) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_blue)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterRight()) + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)).perform(ViewActions.scrollTo()) + Espresso.onView(withId(R.id.color_picker_color_rgb_hex)).check( + ViewAssertions.matches( + withText(String.format("#FF%06X", 0xFFFFFF and -0xffff01)) + ) + ) + } + + @Test + fun testPipetteButtonIsDisplayed() { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView(withId(R.id.color_picker_pipette_btn)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(ViewAssertions.matches(withText(R.string.color_picker_pipette))) + } + + @Test + fun testColorViewsAreDisplayed() { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView(withId(R.id.color_picker_new_color_view)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(ViewAssertions.matches(UiMatcher.withBackgroundColor(Color.BLACK))) + Espresso.onView(withId(R.id.color_picker_current_color_view)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(ViewAssertions.matches(UiMatcher.withBackgroundColor(Color.BLACK))) + } + + @Test + fun alphaValueIsSetInSliderWhenChangedInSeekBar() { + val idlingResource = launchActivityRule.activity.idlingResource + IdlingRegistry.getInstance().register(idlingResource) + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(ViewActions.click()) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)) + .perform(UiInteractions.touchCenterMiddle()) + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_preset) + ) + ).perform(ViewActions.scrollTo(), ViewActions.click()) + ColorPickerViewInteraction.onColorPickerView() + .onPositiveButton() + .perform(ViewActions.click()) + ToolPropertiesInteraction.onToolProperties() + .checkMatchesColor(Color.parseColor("#80000000")) + IdlingRegistry.getInstance().unregister(idlingResource) + } + + @Test + fun alphaValueIsSetInSeekBarWhenChangedInSlider() { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_preset) + ) + ).perform(ViewActions.click()) + Espresso.onView(withId(R.id.color_alpha_slider)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterMiddle()) + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(ViewActions.click()) + Espresso.onView(withId(R.id.color_picker_rgb_alpha_value)).check( + ViewAssertions.matches( + withText("50") + ) + ) + } + + @Test + fun testPreserveZoomAfterPipetteUsage() { + val perspective = launchActivityRule.activity.perspective + DrawingSurfaceInteraction.onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + val scale = 4f + perspective.scale = scale + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView(withId(R.id.color_picker_pipette_btn)).perform(ViewActions.click()) + Espresso.onView(withId(R.id.doneAction)).perform(ViewActions.click()) + ColorPickerViewInteraction.onColorPickerView() + .performCloseColorPickerWithDialogButton() + Assert.assertEquals(scale, perspective.scale, Float.MIN_VALUE) + } + + @Test + fun testColorHistoryShowsPresetSelectorColors() { + val resources = launchActivityRule.activity.resources + val presetColors = + resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors) + run { + var counterColors = 0 + while (counterColors < presetColors.length() && counterColors < ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY) { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + ColorPickerViewInteraction.onColorPickerView() + .performClickColorPickerPresetSelectorButton(counterColors) + ColorPickerViewInteraction.onColorPickerView() + .onPositiveButton() + .perform(ViewActions.click()) + counterColors++ + } + } + var counterColors = 0 + while (counterColors < presetColors.length() && counterColors < ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY) { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + ColorPickerViewInteraction.onColorPickerView() + .performClickOnHistoryColor(ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY - 1) + ColorPickerViewInteraction.onColorPickerView() + .onPositiveButton() + .perform(ViewActions.click()) + val arrayColor = presetColors.getColor(counterColors, Color.BLACK) + val selectedColor = Objects.requireNonNull( + toolReference!!.tool + )?.drawPaint?.color + Assert.assertEquals( + "Color in history doesn't match selection", + arrayColor.toLong(), + selectedColor?.toLong() + ) + counterColors++ + } + presetColors.recycle() + } + + @Test + fun testColorHistorySelectMoreThanMaxHistoryColors() { + val resources = launchActivityRule.activity.resources + val presetColors = + resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors) + var counterColors = 0 + while (counterColors < presetColors.length() && counterColors <= ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY) { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + ColorPickerViewInteraction.onColorPickerView() + .performClickColorPickerPresetSelectorButton(counterColors) + ColorPickerViewInteraction.onColorPickerView() + .onPositiveButton() + .perform(ViewActions.click()) + counterColors++ + } + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + var historyCounter = 0 + var colorCounter = ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY + while (historyCounter < ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY) { + ColorPickerViewInteraction.onColorPickerView() + .checkHistoryColor(historyCounter, presetColors.getColor(colorCounter, Color.BLACK)) + historyCounter++ + colorCounter-- + } + presetColors.recycle() + } + + @Test + fun testColorHistoryShowsRGBSelectorColors() { + launchActivityRule.activity + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(ViewActions.click()) + Espresso.onView( + ViewMatchers.withClassName( + Matchers.containsString( + TAB_VIEW_RGBA_SELECTOR_CLASS + ) + ) + ).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_red)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterRight()) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_green)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterRight()) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_blue)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterRight()) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterRight()) + ColorPickerViewInteraction.onColorPickerView() + .onPositiveButton() + .perform(ViewActions.click()) + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + ColorPickerViewInteraction.onColorPickerView().checkHistoryColor(0, -0x1) + Espresso.onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(ViewActions.click()) + Espresso.onView( + ViewMatchers.withClassName( + Matchers.containsString( + TAB_VIEW_RGBA_SELECTOR_CLASS + ) + ) + ).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_red)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterLeft()) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_green)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterLeft()) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_blue)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterLeft()) + Espresso.onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)) + .perform(ViewActions.scrollTo(), UiInteractions.touchCenterLeft()) + ColorPickerViewInteraction.onColorPickerView() + .onPositiveButton() + .perform(ViewActions.click()) + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + ColorPickerViewInteraction.onColorPickerView().checkHistoryColor(0, 0x00000000) + } + + @Test + fun testColorHistoryPreservedWhenClickingNewImage() { + val resources = launchActivityRule.activity.resources + val presetColors = + resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors) + var counterColors = 0 + while (counterColors < presetColors.length() && counterColors < ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY) { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + ColorPickerViewInteraction.onColorPickerView() + .performClickColorPickerPresetSelectorButton(counterColors) + ColorPickerViewInteraction.onColorPickerView() + .onPositiveButton() + .perform(ViewActions.click()) + counterColors++ + } + TopBarViewInteraction.onTopBarView().performOpenMoreOptions() + Espresso.onView(withText(R.string.menu_new_image)).perform(ViewActions.click()) + Espresso.onView(withText(R.string.discard_button_text)).perform(ViewActions.click()) + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + ColorPickerViewInteraction.onColorPickerView() + .checkHistoryColor(3, presetColors.getColor(0, Color.BLACK)) + presetColors.recycle() + } + + @Test + fun testColorHistoryDeletedWhenRestartingApp() { + val resources = launchActivityRule.activity.resources + val presetColors = + resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors) + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + var counterColors = 0 + while (counterColors < presetColors.length() && counterColors < ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY) { + ColorPickerViewInteraction.onColorPickerView() + .performClickColorPickerPresetSelectorButton(counterColors) + counterColors++ + } + launchActivityRule.finishActivity() + launchActivityRule.launchActivity(Intent()) + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + Espresso.onView(withId(R.id.color_history_text_view)) + .check(ViewAssertions.matches(Matchers.not(ViewMatchers.isDisplayed()))) + presetColors.recycle() + } + + @Test + fun testSaveColorHistoryInCatrobatFile() { + val idlingResource = launchActivityRule.activity.idlingResource + IdlingRegistry.getInstance().register(idlingResource) + val activity = launchActivityRule.activity + val resources = activity.resources + val presetColors = + resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors) + run { + var counterColors = 0 + while (counterColors < presetColors.length() && counterColors < ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY) { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + ColorPickerViewInteraction.onColorPickerView() + .performClickColorPickerPresetSelectorButton(counterColors) + ColorPickerViewInteraction.onColorPickerView() + .onPositiveButton() + .perform(ViewActions.click()) + counterColors++ + } + } + saveCatrobatImage() + val uri = activity.model.savedPictureUri + launchActivityRule.finishActivity() + var intent = Intent() + intent.putExtra(PAINTROID_PICTURE_PATH, "") + intent.putExtra(PAINTROID_PICTURE_NAME, IMAGE_NAME) + launchActivityRuleWithIntent.launchActivity(intent) + intent = Intent() + intent.data = uri + val resultOK = ActivityResult(Activity.RESULT_OK, intent) + Intents.intending(IntentMatchers.hasAction(Intent.ACTION_GET_CONTENT)).respondWith(resultOK) + var counterColors = 0 + while (counterColors + ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY < presetColors.length() && counterColors < ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY) { + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + ColorPickerViewInteraction.onColorPickerView() + .performClickColorPickerPresetSelectorButton(counterColors + ColorPickerViewInteraction.MAXIMUM_COLORS_IN_HISTORY) + ColorPickerViewInteraction.onColorPickerView() + .onPositiveButton() + .perform(ViewActions.click()) + counterColors++ + } + DrawingSurfaceInteraction.onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + TopBarViewInteraction.onTopBarView().performOpenMoreOptions() + Espresso.onView(withText(R.string.menu_load_image)).perform(ViewActions.click()) + Espresso.onView(withText(R.string.menu_replace_image)).perform(ViewActions.click()) + Espresso.onView(withText(R.string.discard_button_text)).perform(ViewActions.click()) + ColorPickerViewInteraction.onColorPickerView() + .performOpenColorPicker() + ColorPickerViewInteraction.onColorPickerView() + .checkHistoryColor(3, presetColors.getColor(0, Color.BLACK)) + presetColors.recycle() + IdlingRegistry.getInstance().unregister(idlingResource) + } + + private fun saveCatrobatImage() { + TopBarViewInteraction.onTopBarView() + .performOpenMoreOptions() + Espresso.onView(withText(R.string.menu_save_image)) + .perform(ViewActions.scrollTo(), ViewActions.click()) + Espresso.onView(withId(R.id.pocketpaint_save_dialog_spinner)) + .perform(ViewActions.click()) + Espresso.onData( + allOf( + Matchers.`is`( + Matchers.instanceOf( + String::class.java + ) + ), + Matchers.`is`(CATROBAT_IMAGE_ENDING) + ) + ).inRoot(RootMatchers.isPlatformPopup()).perform(ViewActions.click()) + Espresso.onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(ViewActions.replaceText(IMAGE_NAME)) + Espresso.onView(withText(R.string.save_button_text)) + .perform(ViewActions.click()) + } + + companion object { + private val TAB_VIEW_PRESET_SELECTOR_CLASS = PresetSelectorView::class.java.simpleName + private val TAB_VIEW_HSV_SELECTOR_CLASS = HSVColorPickerView::class.java.simpleName + private val TAB_VIEW_RGBA_SELECTOR_CLASS = RgbSelectorView::class.java.simpleName + private const val IMAGE_NAME = "colorDialogTestCatrobatImage" + private const val TEXT_RGB_MIN = "0" + private const val TEXT_RGB_MAX = "255" + private const val TEXT_ALPHA_MIN = "0" + private const val TEXT_ALPHA_MAX = "100" + private const val TEXT_PERCENT_SIGN = "%" + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/util/RtlActivityTestRule.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/util/RtlActivityTestRule.java deleted file mode 100644 index f158afb772..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/util/RtlActivityTestRule.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2015 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.rtl.util; - -import android.app.Activity; -import android.content.Context; - -import org.catrobat.paintroid.test.espresso.util.LanguageSupport; - -import java.util.Locale; - -import androidx.test.platform.app.InstrumentationRegistry; -import androidx.test.rule.ActivityTestRule; - -public class RtlActivityTestRule extends ActivityTestRule { - private final String language; - - public RtlActivityTestRule(Class activityClass, String language) { - super(activityClass); - this.language = language; - } - - @Override - protected void beforeActivityLaunched() { - super.beforeActivityLaunched(); - - Locale locale = new Locale(language); - Context targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - LanguageSupport.setLocale(targetContext, locale); - } - - @Override - protected void afterActivityFinished() { - super.afterActivityFinished(); - - Context targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - LanguageSupport.setLocale(targetContext, new Locale("en")); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/util/RtlActivityTestRule.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/util/RtlActivityTestRule.kt new file mode 100644 index 0000000000..5ebcaeb0d6 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/util/RtlActivityTestRule.kt @@ -0,0 +1,40 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.rtl.util + +import android.app.Activity +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.rule.ActivityTestRule +import org.catrobat.paintroid.test.espresso.util.LanguageSupport +import java.util.* + +class RtlActivityTestRule(activityClass: Class?, private val language: String) : ActivityTestRule(activityClass) { + override fun beforeActivityLaunched() { + super.beforeActivityLaunched() + val locale = Locale(language) + val targetContext = InstrumentationRegistry.getInstrumentation().targetContext + LanguageSupport.setLocale(targetContext, locale) + } + + override fun afterActivityFinished() { + super.afterActivityFinished() + val targetContext = InstrumentationRegistry.getInstrumentation().targetContext + LanguageSupport.setLocale(targetContext, Locale("en")) + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/util/RtlUiTestUtils.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/util/RtlUiTestUtils.kt new file mode 100644 index 0000000000..4650b269b1 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/util/RtlUiTestUtils.kt @@ -0,0 +1,28 @@ +/** + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * (//developer.catrobat.org/credits>) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see //www.gnu.org/licenses/>. + */ +package org.catrobat.paintroid.test.espresso.rtl.util + +class RtlUiTestUtils private constructor() { + companion object { + fun checkTextDirection(string: String): Boolean { + return Character.getDirectionality(string[0]) == Character.DIRECTIONALITY_RIGHT_TO_LEFT || + Character.getDirectionality(string[0]) == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC + } + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/screenshots/CreateMarketingScreenshots.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/screenshots/CreateMarketingScreenshots.kt index e951268d27..39137d47b3 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/screenshots/CreateMarketingScreenshots.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/screenshots/CreateMarketingScreenshots.kt @@ -110,7 +110,7 @@ class CreateMarketingScreenshots { Espresso.onData( AllOf.allOf( Matchers.`is`(Matchers.instanceOf(String::class.java)), - Matchers.`is`("png") + Matchers.`is`("png") ) ).inRoot(RootMatchers.isPlatformPopup()).perform(click()) onView(withText(R.string.cancel_button_text)) diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/BrushToolIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/BrushToolIntegrationTest.kt new file mode 100644 index 0000000000..58dfd3f1ad --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/BrushToolIntegrationTest.kt @@ -0,0 +1,181 @@ +package org.catrobat.paintroid.test.espresso.tools + +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2023 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import android.graphics.Color +import android.os.Build +import androidx.annotation.RequiresApi +import androidx.test.espresso.Espresso +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.R +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider +import org.catrobat.paintroid.test.espresso.util.UiInteractions +import org.catrobat.paintroid.test.espresso.util.UiInteractions.swipeAccurate +import org.catrobat.paintroid.test.espresso.util.UiMatcher +import org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerViewInteraction.Companion.onColorPickerView +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.catrobat.paintroid.tools.ToolReference +import org.catrobat.paintroid.tools.ToolType +import org.hamcrest.Matchers +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RequiresApi(api = Build.VERSION_CODES.P) +@RunWith(AndroidJUnit4::class) +class BrushToolIntegrationTest { + + private lateinit var activity: MainActivity + private var toolReference: ToolReference? = null + + @get:Rule + var activityScenarioRule: ActivityScenarioRule = ActivityScenarioRule(MainActivity::class.java) + + @get:Rule + var screenshotOnFailRule = ScreenshotOnFailRule() + + private fun getActivity(): MainActivity { + lateinit var activity: MainActivity + activityScenarioRule.scenario.onActivity { + activity = it + } + return activity + } + + @Before + fun setUp() { + activity = getActivity() + toolReference = activity.toolReference + ToolBarViewInteraction.onToolBarView().performSelectTool(ToolType.BRUSH) + } + + @Test + fun drawOnlyOneLineWithSmoothingAlgorithm() { + val commandManager = activity.commandManager + val previousCommandCount = commandManager.getUndoCommandCount() + DrawingSurfaceInteraction.onDrawingSurfaceView() + .perform(swipeAccurate(DrawingSurfaceLocationProvider.MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_TOP_LEFT)) + val updatedCommandCount = commandManager.getUndoCommandCount() + assertEquals(previousCommandCount + 1, updatedCommandCount) + } + + @Test + fun testBrushToolColor() { + ToolBarViewInteraction.onToolBarView() + .performSelectTool(ToolType.BRUSH) + DrawingSurfaceInteraction.onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + DrawingSurfaceInteraction.onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testBrushToolTransparentColor() { + ToolBarViewInteraction.onToolBarView() + .performSelectTool(ToolType.BRUSH) + onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + Matchers.allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_preset) + ) + ).perform(ViewActions.click()) + Espresso.onView(withId(R.id.color_alpha_slider)).perform( + ViewActions.scrollTo(), + UiInteractions.touchCenterMiddle() + ) + Espresso.onView( + Matchers.allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(ViewActions.click()) + onColorPickerView() + .onPositiveButton() + .perform(ViewActions.click()) + DrawingSurfaceInteraction.onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + + val selectedColor = toolReference?.tool?.drawPaint!!.color + DrawingSurfaceInteraction.onDrawingSurfaceView() + .checkPixelColor(selectedColor, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testBrushToolWithHandleMoveColor() { + ToolBarViewInteraction.onToolBarView() + .performSelectTool(ToolType.BRUSH) + ToolPropertiesInteraction.onToolProperties() + .setColor(Color.BLACK) + DrawingSurfaceInteraction.onDrawingSurfaceView() + .perform(UiInteractions.swipe(DrawingSurfaceLocationProvider.MIDDLE, DrawingSurfaceLocationProvider.BOTTOM_MIDDLE)) + DrawingSurfaceInteraction.onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testBrushToolWithHandleMoveTransparentColor() { + ToolBarViewInteraction.onToolBarView() + .performSelectTool(ToolType.BRUSH) + onColorPickerView() + .performOpenColorPicker() + Espresso.onView( + Matchers.allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_preset) + ) + ).perform(ViewActions.click()) + Espresso.onView(withId(R.id.color_alpha_slider)).perform( + ViewActions.scrollTo(), + UiInteractions.touchCenterMiddle() + ) + Espresso.onView( + Matchers.allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(ViewActions.click()) + onColorPickerView() + .onPositiveButton() + .perform(ViewActions.click()) + DrawingSurfaceInteraction.onDrawingSurfaceView() + .perform( + UiInteractions.swipe( + DrawingSurfaceLocationProvider.MIDDLE, + DrawingSurfaceLocationProvider.BOTTOM_MIDDLE + ) + ) + + val selectedColor = toolReference?.tool?.drawPaint!!.color + DrawingSurfaceInteraction.onDrawingSurfaceView() + .checkPixelColor(selectedColor, BitmapLocationProvider.MIDDLE) + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ClipboardToolIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ClipboardToolIntegrationTest.java deleted file mode 100644 index 8ac0c3e0b9..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ClipboardToolIntegrationTest.java +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2015 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.tools; - -import android.content.pm.ActivityInfo; -import android.graphics.Bitmap; -import android.graphics.Color; -import android.graphics.PointF; - -import org.catrobat.paintroid.MainActivity; -import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider; -import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; -import org.catrobat.paintroid.test.espresso.util.wrappers.ClipboardToolViewInteraction; -import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; -import org.catrobat.paintroid.tools.ToolReference; -import org.catrobat.paintroid.tools.ToolType; -import org.catrobat.paintroid.tools.Workspace; -import org.catrobat.paintroid.tools.drawable.DrawableShape; -import org.catrobat.paintroid.tools.drawable.DrawableStyle; -import org.catrobat.paintroid.tools.implementation.BaseToolWithRectangleShape; -import org.catrobat.paintroid.tools.implementation.ClipboardTool; -import org.catrobat.paintroid.ui.Perspective; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.ActivityTestRule; - -import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt; -import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.LayerMenuViewInteraction.onLayerMenuView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ShapeToolOptionsViewInteraction.onShapeToolOptionsView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -@RunWith(AndroidJUnit4.class) -public class ClipboardToolIntegrationTest { - - private static final float SCALE_25 = 0.25f; - private static final float STAMP_RESIZE_FACTOR = 1.5f; - @Rule - public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); - - @Rule - public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); - - private Workspace workspace; - private Perspective perspective; - private ToolReference toolReference; - private MainActivity mainActivity; - - @Before - public void setUp() { - onToolBarView() - .performSelectTool(ToolType.BRUSH); - mainActivity = launchActivityRule.getActivity(); - workspace = mainActivity.workspace; - perspective = mainActivity.perspective; - toolReference = mainActivity.toolReference; - } - - @Test - public void testBorders() { - onToolBarView() - .performSelectTool(ToolType.SHAPE); - - onShapeToolOptionsView() - .performSelectShape(DrawableShape.RECTANGLE) - .performSelectShapeDrawType(DrawableStyle.STROKE); - - onTopBarView() - .performClickCheckmark(); - - onToolBarView() - .performSelectTool(ToolType.CLIPBOARD); - - ClipboardTool clipboardTool = (ClipboardTool) toolReference.getTool(); - clipboardTool.boxHeight -= 25; - clipboardTool.boxWidth -= 25; - - ClipboardToolViewInteraction.Companion.onClipboardToolViewInteraction() - .performCopy(); - - int topLeft = clipboardTool.drawingBitmap.getPixel(0, 0); - int topRight = clipboardTool.drawingBitmap.getPixel(clipboardTool.drawingBitmap.getWidth() - 1, 0); - int bottomLeft = clipboardTool.drawingBitmap.getPixel(0, clipboardTool.drawingBitmap.getHeight() - 1); - int bottomRight = clipboardTool.drawingBitmap.getPixel(clipboardTool.drawingBitmap.getWidth() - 1, clipboardTool.drawingBitmap.getHeight() - 1); - - assertEquals(topLeft, Color.BLACK); - assertEquals(topRight, Color.BLACK); - assertEquals(bottomLeft, Color.BLACK); - assertEquals(bottomRight, Color.BLACK); - } - - @Test - public void testClipboardToolConsidersLayerOpacity() { - onToolBarView() - .performSelectTool(ToolType.SHAPE); - - onShapeToolOptionsView() - .performSelectShape(DrawableShape.RECTANGLE); - - onTopBarView() - .performClickCheckmark(); - - onToolBarView() - .performSelectTool(ToolType.CLIPBOARD); - - org.catrobat.paintroid.test.espresso.util.wrappers.LayerMenuViewInteraction.onLayerMenuView() - .performOpen() - .performSetOpacityTo(50, 0) - .performClose(); - - ClipboardToolViewInteraction.Companion.onClipboardToolViewInteraction() - .performCopy(); - - ClipboardTool clipboardTool = (ClipboardTool) toolReference.getTool(); - int fiftyPercentOpacityBlack = Color.argb(255 / 2, 0, 0, 0); - int centerPixel = clipboardTool.drawingBitmap.getPixel(clipboardTool.drawingBitmap.getWidth() / 2, clipboardTool.drawingBitmap.getHeight() / 2); - assertEquals(centerPixel, Color.BLACK); - - ClipboardToolViewInteraction.Companion.onClipboardToolViewInteraction() - .performPaste(); - - onDrawingSurfaceView() - .checkPixelColor(fiftyPercentOpacityBlack, BitmapLocationProvider.MIDDLE); - - ClipboardToolViewInteraction.Companion.onClipboardToolViewInteraction() - .performCut(); - - onDrawingSurfaceView() - .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); - - ClipboardToolViewInteraction.Companion.onClipboardToolViewInteraction() - .performPaste(); - - onDrawingSurfaceView() - .checkPixelColor(fiftyPercentOpacityBlack, BitmapLocationProvider.MIDDLE); - } - - @Test - public void testCopyPixel() { - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onToolBarView() - .performSelectTool(ToolType.CLIPBOARD); - - ClipboardToolViewInteraction.Companion.onClipboardToolViewInteraction() - .performCopy(); - - ClipboardTool clipboardTool = (ClipboardTool) toolReference.getTool(); - clipboardTool.toolPosition.set(clipboardTool.toolPosition.x, clipboardTool.toolPosition.y * .5f); - - ClipboardToolViewInteraction.Companion.onClipboardToolViewInteraction() - .performPaste(); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, clipboardTool.toolPosition.x, clipboardTool.toolPosition.y); - } - - @Test - public void testCutAndPastePixel() { - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onToolBarView() - .performSelectTool(ToolType.CLIPBOARD); - - ClipboardToolViewInteraction.Companion.onClipboardToolViewInteraction() - .performCut(); - - ClipboardTool clipboardTool = (ClipboardTool) toolReference.getTool(); - - onDrawingSurfaceView() - .checkPixelColor(Color.TRANSPARENT, clipboardTool.toolPosition.x, clipboardTool.toolPosition.y); - - ClipboardToolViewInteraction.Companion.onClipboardToolViewInteraction() - .performPaste(); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, clipboardTool.toolPosition.x, clipboardTool.toolPosition.y); - } - - @Test - public void testClipboardToolNotCapturingOtherLayers() { - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onToolBarView() - .performSelectTool(ToolType.CLIPBOARD); - - onLayerMenuView() - .performOpen() - .performAddLayer(); - - onLayerMenuView() - .performClose(); - - ClipboardToolViewInteraction.Companion.onClipboardToolViewInteraction() - .performCopy(); - - ClipboardTool clipboardTool = (ClipboardTool) toolReference.getTool(); - clipboardTool.toolPosition.set(clipboardTool.toolPosition.x, clipboardTool.toolPosition.y * .5f); - - ClipboardToolViewInteraction.Companion.onClipboardToolViewInteraction() - .performPaste(); - - onDrawingSurfaceView() - .checkPixelColor(Color.TRANSPARENT, clipboardTool.toolPosition.x, clipboardTool.toolPosition.y * .5f); - } - - @Test - public void testClipboardToolOutsideDrawingSurface() { - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - int bitmapWidth = workspace.getWidth(); - int bitmapHeight = workspace.getHeight(); - perspective.setScale(SCALE_25); - - onToolBarView() - .performSelectTool(ToolType.CLIPBOARD); - - ClipboardTool clipboardTool = (ClipboardTool) toolReference.getTool(); - PointF toolPosition = new PointF(perspective.surfaceCenterX, perspective.surfaceCenterY); - clipboardTool.toolPosition.set(toolPosition); - clipboardTool.boxWidth = (int) (bitmapWidth * STAMP_RESIZE_FACTOR); - clipboardTool.boxHeight = (int) (bitmapHeight * STAMP_RESIZE_FACTOR); - - ClipboardToolViewInteraction.Companion.onClipboardToolViewInteraction() - .performPaste(); - - assertNotNull(clipboardTool.drawingBitmap); - } - - @Test - public void testBitmapSavedOnOrientationChange() { - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onToolBarView() - .performSelectTool(ToolType.CLIPBOARD); - - Bitmap emptyBitmap = Bitmap.createBitmap(((BaseToolWithRectangleShape) - toolReference.getTool()).drawingBitmap); - - ClipboardToolViewInteraction.Companion.onClipboardToolViewInteraction() - .performCopy(); - - Bitmap expectedBitmap = Bitmap.createBitmap(((BaseToolWithRectangleShape) - toolReference.getTool()).drawingBitmap); - - assertFalse(expectedBitmap.sameAs(emptyBitmap)); - - mainActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - - Bitmap actualBitmap = Bitmap.createBitmap(((BaseToolWithRectangleShape) - toolReference.getTool()).drawingBitmap); - - assertTrue(expectedBitmap.sameAs(actualBitmap)); - } - - @Test - public void testClipboardToolDoesNotResetPerspectiveScale() { - float scale = 2.0f; - - perspective.setScale(scale); - perspective.surfaceTranslationX = 50; - perspective.surfaceTranslationY = 200; - mainActivity.refreshDrawingSurface(); - - onToolBarView() - .performSelectTool(ToolType.CLIPBOARD); - - assertEquals(scale, perspective.getScale(), 0.0001f); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ClipboardToolIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ClipboardToolIntegrationTest.kt new file mode 100644 index 0000000000..8eed2c5f6e --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ClipboardToolIntegrationTest.kt @@ -0,0 +1,243 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +@file:Suppress("DEPRECATION") + +package org.catrobat.paintroid.test.espresso.tools + +import android.content.pm.ActivityInfo +import android.graphics.Bitmap +import android.graphics.Color +import android.graphics.PointF +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.ActivityTestRule +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider +import org.catrobat.paintroid.test.espresso.util.UiInteractions +import org.catrobat.paintroid.test.espresso.util.wrappers.ClipboardToolViewInteraction.Companion.onClipboardToolViewInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.Companion.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.LayerMenuViewInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.ShapeToolOptionsViewInteraction.Companion.onShapeToolOptionsView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.Companion.onToolBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.Companion.onTopBarView +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.catrobat.paintroid.tools.ToolReference +import org.catrobat.paintroid.tools.ToolType +import org.catrobat.paintroid.tools.Workspace +import org.catrobat.paintroid.tools.drawable.DrawableShape +import org.catrobat.paintroid.tools.drawable.DrawableStyle +import org.catrobat.paintroid.tools.implementation.BaseToolWithRectangleShape +import org.catrobat.paintroid.tools.implementation.ClipboardTool +import org.catrobat.paintroid.ui.Perspective +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class ClipboardToolIntegrationTest { + @get:Rule + var launchActivityRule = ActivityTestRule(MainActivity::class.java) + + @get:Rule + var screenshotOnFailRule = ScreenshotOnFailRule() + private var workspace: Workspace? = null + private var perspective: Perspective? = null + private var toolReference: ToolReference? = null + private var mainActivity: MainActivity? = null + @Before + fun setUp() { + onToolBarView().performSelectTool(ToolType.BRUSH) + mainActivity = launchActivityRule.activity + workspace = mainActivity?.workspace + perspective = mainActivity?.perspective + toolReference = mainActivity?.toolReference + } + + @Test + fun testBorders() { + onToolBarView().performSelectTool(ToolType.SHAPE) + onShapeToolOptionsView() + .performSelectShape(DrawableShape.RECTANGLE) + .performSelectShapeDrawType(DrawableStyle.STROKE) + onTopBarView().performClickCheckmark() + onToolBarView().performSelectTool(ToolType.CLIPBOARD) + val stampTool = toolReference?.tool as ClipboardTool? + if (stampTool != null) { + stampTool.boxHeight = stampTool.boxHeight.minus(25f) + } + if (stampTool != null) { + stampTool.boxWidth = stampTool.boxWidth.minus(25f) + } + onClipboardToolViewInteraction() + .performCopy() + val topLeft = stampTool?.drawingBitmap?.getPixel(0, 0) + val topRight = stampTool?.drawingBitmap?.getPixel(stampTool.drawingBitmap?.width?.minus(1) ?: 0, 0) + val bottomLeft = stampTool?.drawingBitmap?.getPixel( + 0, + stampTool.drawingBitmap?.height?.minus(1) ?: 0 + ) + val bottomRight = stampTool?.drawingBitmap?.getPixel( + stampTool.drawingBitmap?.width?.minus(1) ?: 0, + stampTool.drawingBitmap?.height?.minus(1) ?: 0 + ) + Assert.assertEquals(topLeft?.toLong(), Color.BLACK.toLong()) + Assert.assertEquals(topRight?.toLong(), Color.BLACK.toLong()) + Assert.assertEquals(bottomLeft?.toLong(), Color.BLACK.toLong()) + Assert.assertEquals(bottomRight?.toLong(), Color.BLACK.toLong()) + } + + @Test + fun testCopyPixel() { + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onToolBarView().performSelectTool(ToolType.CLIPBOARD) + onClipboardToolViewInteraction().performCopy() + val stampTool = toolReference?.tool as ClipboardTool? + stampTool?.toolPosition?.set(stampTool.toolPosition.x, stampTool.toolPosition.y * .5f) + onClipboardToolViewInteraction().performPaste() + stampTool?.toolPosition?.let { + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, stampTool.toolPosition.x, it.y) + } + } + + @Test + fun testCutAndPastePixel() { + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onToolBarView() + .performSelectTool(ToolType.CLIPBOARD) + onClipboardToolViewInteraction() + .performCut() + val stampTool = toolReference?.tool as ClipboardTool? + stampTool?.toolPosition?.let { + onDrawingSurfaceView() + .checkPixelColor( + Color.TRANSPARENT, + stampTool.toolPosition.x, + it.y + ) + } + onClipboardToolViewInteraction().performPaste() + stampTool?.toolPosition?.x?.let { + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, it, stampTool.toolPosition.y) + } + } + + @Test + fun testStampToolNotCapturingOtherLayers() { + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onToolBarView() + .performSelectTool(ToolType.CLIPBOARD) + LayerMenuViewInteraction.onLayerMenuView() + .performOpen() + .performAddLayer() + LayerMenuViewInteraction.onLayerMenuView() + .performClose() + onClipboardToolViewInteraction() + .performCopy() + val stampTool = toolReference?.tool as ClipboardTool? + stampTool!!.toolPosition[stampTool.toolPosition.x] = stampTool.toolPosition.y * .5f + onClipboardToolViewInteraction() + .performPaste() + onDrawingSurfaceView() + .checkPixelColor( + Color.TRANSPARENT, + stampTool.toolPosition.x, + stampTool.toolPosition.y * .5f + ) + } + + @Test + fun testStampOutsideDrawingSurface() { + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + val bitmapWidth = workspace?.width + val bitmapHeight = workspace?.height + perspective?.scale = SCALE_25 + onToolBarView() + .performSelectTool(ToolType.CLIPBOARD) + val stampTool = toolReference?.tool as ClipboardTool? + val toolPosition = perspective?.surfaceCenterX?.let { + perspective?.surfaceCenterY?.let { + it1 -> + PointF(it, it1) + } + } + if (toolPosition != null) { + stampTool?.toolPosition?.set(toolPosition) + } + if (bitmapWidth != null) { + stampTool?.boxWidth = bitmapWidth * STAMP_RESIZE_FACTOR + } + if (bitmapHeight != null) { + stampTool?.boxHeight = bitmapHeight * STAMP_RESIZE_FACTOR + } + onClipboardToolViewInteraction().performPaste() + Assert.assertNotNull(stampTool?.drawingBitmap) + } + + @Test + fun testBitmapSavedOnOrientationChange() { + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onToolBarView() + .performSelectTool(ToolType.CLIPBOARD) + val emptyBitmap = + (toolReference?.tool as BaseToolWithRectangleShape?)?.drawingBitmap?.let { + Bitmap.createBitmap(it) + } + onClipboardToolViewInteraction().performCopy() + val expectedBitmap = + (toolReference?.tool as BaseToolWithRectangleShape?)?.drawingBitmap?.let { + Bitmap.createBitmap(it) + } + if (expectedBitmap != null) { + Assert.assertFalse(expectedBitmap.sameAs(emptyBitmap)) + } + mainActivity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + val actualBitmap = + (toolReference?.tool as BaseToolWithRectangleShape?)?.drawingBitmap?.let { + Bitmap.createBitmap(it) + } + if (expectedBitmap != null) { + Assert.assertTrue(expectedBitmap.sameAs(actualBitmap)) + } + } + + @Test + fun testStampToolDoesNotResetPerspectiveScale() { + val scale = 2.0f + perspective?.scale = scale + perspective?.surfaceTranslationX = 50F + perspective?.surfaceTranslationY = 200F + mainActivity?.refreshDrawingSurface() + onToolBarView().performSelectTool(ToolType.CLIPBOARD) + perspective?.scale?.let { Assert.assertEquals(scale, it, 0.0001f) } + } + + companion object { + private const val SCALE_25 = 0.25f + private const val STAMP_RESIZE_FACTOR = 1.5f + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ClippingToolIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ClippingToolIntegrationTest.kt index 55706a866f..f790d925da 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ClippingToolIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ClippingToolIntegrationTest.kt @@ -13,7 +13,7 @@ import org.catrobat.paintroid.test.espresso.util.UiInteractions import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction import org.catrobat.paintroid.test.espresso.util.wrappers.LayerMenuViewInteraction import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction -import org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.onToolProperties +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.Companion.onToolProperties import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule import org.catrobat.paintroid.tools.ToolReference @@ -274,16 +274,16 @@ class ClippingToolIntegrationTest { onToolProperties().setColor(Color.BLACK) DrawingSurfaceInteraction.onDrawingSurfaceView() - .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) DrawingSurfaceInteraction.onDrawingSurfaceView() - .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) DrawingSurfaceInteraction.onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) ToolBarViewInteraction.onToolBarView() - .performSelectTool(ToolType.CLIP) + .performSelectTool(ToolType.CLIP) onToolProperties().setColor(Color.YELLOW) @@ -300,7 +300,7 @@ class ClippingToolIntegrationTest { } TopBarViewInteraction.onTopBarView() - .performClickCheckmark() + .performClickCheckmark() val inAreaX = middle.x - 10 val inAreaY = middle.y - 10 @@ -315,13 +315,13 @@ class ClippingToolIntegrationTest { assertEquals(colorOutOfArea, Color.TRANSPARENT) TopBarViewInteraction.onTopBarView() - .performUndo() + .performUndo() val colorOutOfAreaAfterUndo = workspace.bitmapOfCurrentLayer?.getPixel(outOfAreaX, outOfAreaY) assertEquals(colorOutOfAreaAfterUndo, Color.BLACK) TopBarViewInteraction.onTopBarView() - .performRedo() + .performRedo() val colorOutOfAreaAfterRedo = workspace.bitmapOfCurrentLayer?.getPixel(outOfAreaX, outOfAreaY) assertEquals(colorOutOfAreaAfterRedo, Color.TRANSPARENT) diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/EraserToolIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/EraserToolIntegrationTest.java deleted file mode 100644 index 8c77c5204c..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/EraserToolIntegrationTest.java +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2015 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.tools; - -import android.graphics.Color; -import android.graphics.Paint.Cap; - -import org.catrobat.paintroid.MainActivity; -import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider; -import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; -import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; -import org.catrobat.paintroid.tools.ToolType; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.ActivityTestRule; - -import static org.catrobat.paintroid.test.espresso.util.EspressoUtils.DEFAULT_STROKE_WIDTH; -import static org.catrobat.paintroid.test.espresso.util.UiInteractions.setProgress; -import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt; -import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withProgress; -import static org.catrobat.paintroid.test.espresso.util.wrappers.BrushPickerViewInteraction.onBrushPickerView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.onToolProperties; -import static org.hamcrest.Matchers.allOf; - -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.isSelected; -import static androidx.test.espresso.matcher.ViewMatchers.withText; - -@RunWith(AndroidJUnit4.class) -public class EraserToolIntegrationTest { - - private static final String TEXT_DEFAULT_STROKE_WIDTH = Integer.toString(DEFAULT_STROKE_WIDTH); - - @Rule - public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); - - @Rule - public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); - - @Test - public void testEraseOnEmptyBitmap() { - onDrawingSurfaceView() - .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); - - onToolBarView() - .performSelectTool(ToolType.ERASER); - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onDrawingSurfaceView() - .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); - } - - @Test - public void testEraseSinglePixel() { - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); - - onToolBarView() - .performSelectTool(ToolType.ERASER); - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onDrawingSurfaceView() - .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); - } - - @Test - public void testSwitchingBetweenBrushAndEraser() { - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); - - onToolBarView() - .performSelectTool(ToolType.ERASER); - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onDrawingSurfaceView() - .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); - - onToolBarView() - .performSelectTool(ToolType.BRUSH); - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); - - onToolBarView() - .performSelectTool(ToolType.ERASER); - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onDrawingSurfaceView() - .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); - } - - @Test - public void testChangeEraserBrushSize() { - int newStrokeWidth = 90; - onBrushPickerView().onStrokeWidthSeekBar() - .perform(setProgress(newStrokeWidth)) - .check(matches(withProgress(newStrokeWidth))); - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); - - onToolBarView() - .performSelectTool(ToolType.ERASER); - - onBrushPickerView().onStrokeWidthSeekBar() - .check(matches(allOf(isDisplayed(), withProgress(newStrokeWidth)))); - onBrushPickerView().onStrokeWidthTextView() - .check(matches(allOf(isDisplayed(), withText(Integer.toString(newStrokeWidth))))); - - newStrokeWidth = 80; - onBrushPickerView().onStrokeWidthSeekBar() - .perform(setProgress(newStrokeWidth)) - .check(matches(withProgress(newStrokeWidth))); - - onToolBarView() - .performCloseToolOptionsView(); - - onToolProperties() - .checkStrokeWidth(80); - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onDrawingSurfaceView() - .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); - } - - @Test - public void testChangeEraserBrushForm() { - onBrushPickerView() - .onStrokeWidthSeekBar() - .perform(setProgress(70)); - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); - - onBrushPickerView() - .onStrokeWidthSeekBar() - .perform(setProgress(50)); - - onToolBarView() - .performSelectTool(ToolType.ERASER); - - onBrushPickerView().onStrokeCapSquareView() - .perform(click()); - - onToolBarView() - .performCloseToolOptionsView(); - - onToolProperties() - .checkCap(Cap.SQUARE); - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onDrawingSurfaceView() - .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); - } - - @Test - public void testRestorePreviousToolSettings() { - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); - - onToolBarView() - .performSelectTool(ToolType.ERASER); - - onBrushPickerView().onStrokeWidthTextView() - .check(matches(allOf(isDisplayed(), withText(TEXT_DEFAULT_STROKE_WIDTH)))); - onBrushPickerView().onStrokeWidthSeekBar() - .check(matches(allOf(isDisplayed(), withProgress(DEFAULT_STROKE_WIDTH)))); - - int newStrokeWidth = 80; - onBrushPickerView().onStrokeWidthSeekBar() - .perform(setProgress(newStrokeWidth)) - .check(matches(withProgress(newStrokeWidth))); - - onBrushPickerView().onStrokeCapSquareView() - .perform(click()); - - onToolProperties() - .checkStrokeWidth(newStrokeWidth) - .checkCap(Cap.SQUARE); - - onToolBarView() - .performCloseToolOptionsView() - .performOpenToolOptionsView(); - - onBrushPickerView().onStrokeWidthSeekBar() - .check(matches(withProgress(newStrokeWidth))); - - int eraserStrokeWidth = 60; - onBrushPickerView().onStrokeWidthSeekBar() - .perform(setProgress(eraserStrokeWidth)) - .check(matches(withProgress(eraserStrokeWidth))); - - onBrushPickerView().onStrokeCapRoundView() - .perform(click()); - - onToolProperties() - .checkStrokeWidth(eraserStrokeWidth) - .checkCap(Cap.ROUND); - - onToolBarView() - .performSelectTool(ToolType.BRUSH); - - onBrushPickerView().onStrokeWidthSeekBar() - .check(matches(withProgress(eraserStrokeWidth))); - onBrushPickerView().onStrokeCapRoundView() - .check(matches(isSelected())); - - onToolProperties() - .checkCap(Cap.ROUND) - .checkStrokeWidth(eraserStrokeWidth); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/EraserToolIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/EraserToolIntegrationTest.kt new file mode 100644 index 0000000000..8c7a408606 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/EraserToolIntegrationTest.kt @@ -0,0 +1,335 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +@file:Suppress("DEPRECATION") + +package org.catrobat.paintroid.test.espresso.tools + +import android.graphics.Color +import android.graphics.Paint.Cap +import androidx.test.espresso.Espresso +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.ActivityTestRule +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider +import org.catrobat.paintroid.test.espresso.util.EspressoUtils.DEFAULT_STROKE_WIDTH +import org.catrobat.paintroid.test.espresso.util.UiInteractions.setProgress +import org.catrobat.paintroid.test.espresso.util.UiInteractions.swipeAccurate +import org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt +import org.catrobat.paintroid.test.espresso.util.UiInteractions.waitFor +import org.catrobat.paintroid.test.espresso.util.UiMatcher.withProgress +import org.catrobat.paintroid.test.espresso.util.wrappers.BrushPickerViewInteraction.Companion.onBrushPickerView +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.Companion.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.Companion.onToolBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.Companion.onToolProperties +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.Companion.onTopBarView +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.catrobat.paintroid.tools.ToolType +import org.hamcrest.Matchers.allOf +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class EraserToolIntegrationTest { + @get:Rule + var launchActivityRule = ActivityTestRule(MainActivity::class.java) + + @get:Rule + var screenshotOnFailRule = ScreenshotOnFailRule() + + @Test + fun testEraseOnEmptyBitmap() { + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + onToolBarView() + .performSelectTool(ToolType.ERASER) + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testEraseSinglePixel() { + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + onToolBarView() + .performSelectTool(ToolType.ERASER) + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testSwitchingBetweenBrushAndEraser() { + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + onToolBarView() + .performSelectTool(ToolType.ERASER) + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + onToolBarView() + .performSelectTool(ToolType.BRUSH) + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + onToolBarView() + .performSelectTool(ToolType.ERASER) + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testChangeEraserBrushSize() { + var newStrokeWidth = 90 + onBrushPickerView().onStrokeWidthSeekBar() + .perform(setProgress(newStrokeWidth)) + .check(matches(withProgress(newStrokeWidth))) + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + onToolBarView() + .performSelectTool(ToolType.ERASER) + onBrushPickerView().onStrokeWidthSeekBar() + .check( + matches( + allOf( + isDisplayed(), + withProgress(newStrokeWidth) + ) + ) + ) + onBrushPickerView().onStrokeWidthTextView() + .check( + matches( + allOf( + isDisplayed(), + withText( + newStrokeWidth.toString() + ) + ) + ) + ) + newStrokeWidth = 80 + onBrushPickerView().onStrokeWidthSeekBar() + .perform(setProgress(newStrokeWidth)) + .check(matches(withProgress(newStrokeWidth))) + onToolBarView() + .performCloseToolOptionsView() + onToolProperties() + .checkStrokeWidth(80f) + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testChangeEraserBrushForm() { + onBrushPickerView() + .onStrokeWidthSeekBar() + .perform(setProgress(70)) + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + onBrushPickerView() + .onStrokeWidthSeekBar() + .perform(setProgress(50)) + onToolBarView() + .performSelectTool(ToolType.ERASER) + onBrushPickerView().onStrokeCapSquareView() + .perform(click()) + onToolBarView() + .performCloseToolOptionsView() + onToolProperties() + .checkCap(Cap.SQUARE) + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + } + + @Suppress("LongMethod") + @Test + fun testRestorePreviousToolSettings() { + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + onToolBarView() + .performSelectTool(ToolType.ERASER) + onBrushPickerView().onStrokeWidthTextView() + .check( + matches( + allOf( + isDisplayed(), + withText( + TEXT_DEFAULT_STROKE_WIDTH + ) + ) + ) + ) + onBrushPickerView().onStrokeWidthSeekBar() + .check( + matches( + allOf( + isDisplayed(), + withProgress(DEFAULT_STROKE_WIDTH) + ) + ) + ) + val newStrokeWidth = 80 + onBrushPickerView().onStrokeWidthSeekBar() + .perform(setProgress(newStrokeWidth)) + .check(matches(withProgress(newStrokeWidth))) + onBrushPickerView().onStrokeCapSquareView() + .perform(click()) + onToolProperties() + .checkStrokeWidth(newStrokeWidth.toFloat()) + .checkCap(Cap.SQUARE) + onToolBarView() + .performCloseToolOptionsView() + .performOpenToolOptionsView() + onBrushPickerView().onStrokeWidthSeekBar() + .check(matches(withProgress(newStrokeWidth))) + val eraserStrokeWidth = 60 + onBrushPickerView().onStrokeWidthSeekBar() + .perform(setProgress(eraserStrokeWidth)) + .check(matches(withProgress(eraserStrokeWidth))) + onBrushPickerView().onStrokeCapRoundView() + .perform(click()) + onToolProperties() + .checkStrokeWidth(eraserStrokeWidth.toFloat()) + .checkCap(Cap.ROUND) + onToolBarView() + .performSelectTool(ToolType.BRUSH) + onBrushPickerView().onStrokeWidthSeekBar() + .check(matches(withProgress(eraserStrokeWidth))) + onBrushPickerView().onStrokeCapRoundView() + .check(matches(ViewMatchers.isSelected())) + onToolProperties() + .checkCap(Cap.ROUND) + .checkStrokeWidth(eraserStrokeWidth.toFloat()) + } + + @Test + fun fillBitmapEraseASpotThenFillTheErasedSpotCreatesNoCheckeredPattern() { + onToolBarView() + .performSelectTool(ToolType.FILL) + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + onToolBarView() + .performSelectTool(ToolType.ERASER) + onDrawingSurfaceView().perform( + swipeAccurate( + DrawingSurfaceLocationProvider.HALFWAY_TOP_MIDDLE, + DrawingSurfaceLocationProvider.HALFWAY_BOTTOM_MIDDLE + ) + ) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_TOP_MIDDLE) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_BOTTOM_MIDDLE) + onToolBarView() + .performSelectTool(ToolType.FILL) + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_TOP_MIDDLE) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_BOTTOM_MIDDLE) + } + + @Test + fun fillBitmapEraseASpotThenFillTheErasedSpotUndoRedoNoCheckeredPattern() { + onToolBarView() + .performSelectTool(ToolType.FILL) + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + onToolBarView() + .performSelectTool(ToolType.ERASER) + onDrawingSurfaceView().perform( + swipeAccurate( + DrawingSurfaceLocationProvider.HALFWAY_TOP_MIDDLE, + DrawingSurfaceLocationProvider.HALFWAY_BOTTOM_MIDDLE + ) + ) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_TOP_MIDDLE) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_BOTTOM_MIDDLE) + onToolBarView() + .performSelectTool(ToolType.FILL) + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_TOP_MIDDLE) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_BOTTOM_MIDDLE) + onTopBarView().performUndo() + Espresso.onView(ViewMatchers.isRoot()).perform(waitFor(2000)) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_TOP_MIDDLE) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_BOTTOM_MIDDLE) + onTopBarView().performRedo() + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_TOP_MIDDLE) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_BOTTOM_MIDDLE) + } + + companion object { + private const val TEXT_DEFAULT_STROKE_WIDTH = DEFAULT_STROKE_WIDTH.toString() + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/FillToolIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/FillToolIntegrationTest.java deleted file mode 100644 index 5408613e7e..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/FillToolIntegrationTest.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2015 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.tools; - -import android.graphics.Color; -import android.net.Uri; - -import org.catrobat.paintroid.MainActivity; -import org.catrobat.paintroid.R; -import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider; -import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; -import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; -import org.catrobat.paintroid.tools.ToolReference; -import org.catrobat.paintroid.tools.ToolType; -import org.catrobat.paintroid.tools.implementation.FillTool; -import org.catrobat.paintroid.ui.Perspective; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.File; - -import androidx.test.espresso.IdlingRegistry; -import androidx.test.espresso.ViewInteraction; -import androidx.test.espresso.idling.CountingIdlingResource; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.ActivityTestRule; - -import static org.catrobat.paintroid.test.espresso.util.UiInteractions.swipeAccurate; -import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt; -import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withProgress; -import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.onToolProperties; -import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; -import static org.catrobat.paintroid.tools.implementation.FillToolKt.DEFAULT_TOLERANCE_IN_PERCENT; -import static org.junit.Assert.assertEquals; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard; -import static androidx.test.espresso.action.ViewActions.replaceText; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; - -@RunWith(AndroidJUnit4.class) -public class FillToolIntegrationTest { - - private static final double TOLERANCE_DELTA = 0.05d; - - @Rule - public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); - - @Rule - public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); - - private Perspective perspective; - private ToolReference toolReference; - private MainActivity mainActivity; - private CountingIdlingResource idlingResource; - - @Before - public void setUp() { - mainActivity = launchActivityRule.getActivity(); - perspective = mainActivity.perspective; - toolReference = mainActivity.toolReference; - idlingResource = mainActivity.getIdlingResource(); - IdlingRegistry.getInstance().register(idlingResource); - - onToolBarView() - .performSelectTool(ToolType.FILL); - } - - @After - public void tearDown() { - IdlingRegistry.getInstance().unregister(idlingResource); - } - - @Test - public void testFloodFillIfImageLoaded() { - mainActivity.model.setSavedPictureUri(Uri.fromFile(new File("dummy"))); - - onToolProperties() - .checkMatchesColor(Color.BLACK); - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); - - mainActivity.model.setSavedPictureUri(null); - } - - @Test - public void testBitmapIsFilled() { - onToolProperties() - .checkMatchesColor(Color.BLACK); - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); - } - - @Test - public void testNothingHappensWhenClickedOutsideDrawingArea() { - perspective.multiplyScale(.4f); - onToolProperties() - .checkMatchesColor(Color.BLACK); - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.OUTSIDE_MIDDLE_RIGHT)); - onDrawingSurfaceView() - .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); - } - - @Test - public void testOnlyFillInnerArea() { - onToolBarView() - .performSelectTool(ToolType.BRUSH); - - onToolProperties() - .checkMatchesColor(Color.BLACK); - - onDrawingSurfaceView() - .perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_TOP_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_RIGHT_MIDDLE)) - .perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_RIGHT_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_BOTTOM_MIDDLE)) - .perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_BOTTOM_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_LEFT_MIDDLE)) - .perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_LEFT_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_TOP_MIDDLE)); - - onToolBarView() - .performSelectTool(ToolType.FILL); - - onToolProperties() - .setColorResource(R.color.pocketpaint_color_picker_green1); - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onDrawingSurfaceView() - .checkPixelColorResource(R.color.pocketpaint_color_picker_green1, BitmapLocationProvider.MIDDLE) - .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE_RIGHT) - .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE); - } - - @Test - public void testFillToolOptionsDialog() { - FillTool fillTool = (FillTool) toolReference.getTool(); - assertEquals( - "Wrong fill tool member value for color tolerance", - fillTool.getToleranceAbsoluteValue(DEFAULT_TOLERANCE_IN_PERCENT), - fillTool.colorTolerance, - TOLERANCE_DELTA - ); - - onToolBarView() - .performClickSelectedToolButton(); - - final ViewInteraction colorToleranceInput = onView(withId(R.id.pocketpaint_fill_tool_dialog_color_tolerance_input)); - final ViewInteraction colorToleranceSeekBar = onView(withId(R.id.pocketpaint_color_tolerance_seek_bar)); - - String testToleranceText = "100"; - - colorToleranceInput.check(matches(withText(Integer.toString(DEFAULT_TOLERANCE_IN_PERCENT)))); - - colorToleranceInput.perform(replaceText(testToleranceText), closeSoftKeyboard()); - - colorToleranceInput.check(matches(withText(testToleranceText))); - colorToleranceSeekBar.check(matches(withProgress(Integer.parseInt(testToleranceText)))); - - float expectedAbsoluteTolerance = fillTool.getToleranceAbsoluteValue(100); - assertEquals("Wrong fill tool member value for color tolerance", expectedAbsoluteTolerance, fillTool.colorTolerance, TOLERANCE_DELTA); - - // Close tool options - onToolBarView() - .performClickSelectedToolButton(); - } - - @Test - public void testFillToolDialogAfterToolSwitch() { - FillTool fillTool = (FillTool) toolReference.getTool(); - - onToolBarView() - .performClickSelectedToolButton(); - - final ViewInteraction colorToleranceInput = onView(withId(R.id.pocketpaint_fill_tool_dialog_color_tolerance_input)); - final ViewInteraction colorToleranceSeekBar = onView(withId(R.id.pocketpaint_color_tolerance_seek_bar)); - - int toleranceInPercent = 50; - - colorToleranceInput.perform(replaceText(String.valueOf(toleranceInPercent))); - - float expectedAbsoluteTolerance = fillTool.getToleranceAbsoluteValue(toleranceInPercent); - - assertEquals("Wrong fill tool member value for color tolerance", expectedAbsoluteTolerance, fillTool.colorTolerance, TOLERANCE_DELTA); - - // Close tool options - onToolBarView() - .performClickSelectedToolButton(); - - onToolBarView() - .performSelectTool(ToolType.BRUSH); - - onToolBarView() - .performSelectTool(ToolType.FILL); - onToolBarView() - .performClickSelectedToolButton(); - - colorToleranceInput.check(matches(withText(Integer.toString(DEFAULT_TOLERANCE_IN_PERCENT)))); - colorToleranceSeekBar.check(matches(withProgress(DEFAULT_TOLERANCE_IN_PERCENT))); - } - - @Ignore("Fails on Jenkins, trying out if everything works without this test or if error is due to a bug on Jenkins") - @Test - public void testFillToolUndoRedoWithTolerance() { - onToolBarView() - .performSelectTool(ToolType.BRUSH); - - onDrawingSurfaceView() - .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); - - onToolProperties() - .setColorResource(R.color.pocketpaint_color_picker_brown2) - .checkMatchesColorResource(R.color.pocketpaint_color_picker_brown2); - - onToolBarView() - .performSelectTool(ToolType.FILL) - .performOpenToolOptionsView(); - - onView(withId(R.id.pocketpaint_fill_tool_dialog_color_tolerance_input)) - .perform(replaceText(String.valueOf(100))); - - onToolBarView() - .performCloseToolOptionsView(); - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); - onDrawingSurfaceView() - .checkPixelColorResource(R.color.pocketpaint_color_picker_brown2, BitmapLocationProvider.MIDDLE) - .checkPixelColorResource(R.color.pocketpaint_color_picker_brown2, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE); - - onTopBarView() - .performUndo(); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) - .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE); - - onTopBarView() - .performRedo(); - - onDrawingSurfaceView() - .checkPixelColorResource(R.color.pocketpaint_color_picker_brown2, BitmapLocationProvider.MIDDLE) - .checkPixelColorResource(R.color.pocketpaint_color_picker_brown2, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/FillToolIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/FillToolIntegrationTest.kt new file mode 100644 index 0000000000..ee54bf52f2 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/FillToolIntegrationTest.kt @@ -0,0 +1,271 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.tools + +import android.graphics.Color +import android.net.Uri +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.action.ViewActions.replaceText +import androidx.test.espresso.action.ViewActions.closeSoftKeyboard +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.idling.CountingIdlingResource +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.ActivityTestRule +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.R +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider +import org.catrobat.paintroid.test.espresso.util.UiInteractions.swipeAccurate +import org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt +import org.catrobat.paintroid.test.espresso.util.UiInteractions.waitFor +import org.catrobat.paintroid.test.espresso.util.UiMatcher.withProgress +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.Companion.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.Companion.onToolBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.Companion.onToolProperties +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.Companion.onTopBarView +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.catrobat.paintroid.tools.ToolReference +import org.catrobat.paintroid.tools.ToolType +import org.catrobat.paintroid.tools.implementation.DEFAULT_TOLERANCE_IN_PERCENT +import org.catrobat.paintroid.tools.implementation.FillTool +import org.catrobat.paintroid.ui.Perspective +import org.junit.Rule +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.Assert +import org.junit.runner.RunWith +import java.io.File + +@RunWith(AndroidJUnit4::class) +class FillToolIntegrationTest { + @JvmField + @Rule + var launchActivityRule = ActivityTestRule(MainActivity::class.java) + + @JvmField + @Rule + var screenshotOnFailRule = ScreenshotOnFailRule() + private var perspective: Perspective? = null + private var toolReference: ToolReference? = null + private lateinit var mainActivity: MainActivity + private var idlingResource: CountingIdlingResource? = null + @Before + fun setUp() { + mainActivity = launchActivityRule.activity + perspective = mainActivity.perspective + toolReference = mainActivity.toolReference + idlingResource = mainActivity.idlingResource + IdlingRegistry.getInstance().register(idlingResource) + onToolBarView() + .performSelectTool(ToolType.FILL) + } + + @After + fun tearDown() { + IdlingRegistry.getInstance().unregister(idlingResource) + } + + @Test + fun testFloodFillIfImageLoaded() { + mainActivity.model.savedPictureUri = Uri.fromFile(File("dummy")) + onToolProperties() + .checkMatchesColor(Color.BLACK) + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + mainActivity.model.savedPictureUri = null + } + + @Test + fun testBitmapIsFilled() { + onToolProperties() + .checkMatchesColor(Color.BLACK) + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testNothingHappensWhenClickedOutsideDrawingArea() { + perspective!!.multiplyScale(.4f) + onToolProperties() + .checkMatchesColor(Color.BLACK) + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.OUTSIDE_MIDDLE_RIGHT)) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testOnlyFillInnerArea() { + onToolBarView() + .performSelectTool(ToolType.BRUSH) + onToolProperties() + .checkMatchesColor(Color.BLACK) + onDrawingSurfaceView() + .perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_TOP_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_RIGHT_MIDDLE)) + .perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_RIGHT_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_BOTTOM_MIDDLE)) + .perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_BOTTOM_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_LEFT_MIDDLE)) + .perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_LEFT_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_TOP_MIDDLE)) + onToolBarView() + .performSelectTool(ToolType.FILL) + onToolProperties() + .setColorResource(R.color.pocketpaint_color_picker_green1) + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColorResource(R.color.pocketpaint_color_picker_green1, BitmapLocationProvider.MIDDLE) + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE_RIGHT) + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE) + } + + @Test + fun testFillToolOptionsDialog() { + val fillTool = toolReference!!.tool as FillTool? + Assert.assertEquals( + "Wrong fill tool member value for color tolerance", + fillTool!!.getToleranceAbsoluteValue(DEFAULT_TOLERANCE_IN_PERCENT).toDouble(), + fillTool.colorTolerance.toDouble(), + TOLERANCE_DELTA + ) + val colorToleranceInput = onView(withId(R.id.pocketpaint_fill_tool_dialog_color_tolerance_input)) + val colorToleranceSeekBar = onView(withId(R.id.pocketpaint_color_tolerance_seek_bar)) + val testToleranceText = "100" + colorToleranceInput.check( + matches( + withText( + DEFAULT_TOLERANCE_IN_PERCENT.toString() + ) + ) + ) + colorToleranceInput.perform(replaceText(testToleranceText), closeSoftKeyboard()) + colorToleranceInput.check(matches(withText(testToleranceText))) + colorToleranceSeekBar.check(matches(withProgress(testToleranceText.toInt()))) + val expectedAbsoluteTolerance = fillTool.getToleranceAbsoluteValue(100) + Assert.assertEquals("Wrong fill tool member value for color tolerance", expectedAbsoluteTolerance.toDouble(), fillTool.colorTolerance.toDouble(), TOLERANCE_DELTA) + + // Close tool options + onToolBarView() + .performClickSelectedToolButton() + } + + @Test + fun testFillToolDialogAfterToolSwitch() { + val fillTool = toolReference!!.tool as FillTool? + val colorToleranceInput = onView(withId(R.id.pocketpaint_fill_tool_dialog_color_tolerance_input)) + val colorToleranceSeekBar = onView(withId(R.id.pocketpaint_color_tolerance_seek_bar)) + val toleranceInPercent = 50 + colorToleranceInput.perform(replaceText(toleranceInPercent.toString())) + val expectedAbsoluteTolerance = fillTool!!.getToleranceAbsoluteValue(toleranceInPercent) + Assert.assertEquals("Wrong fill tool member value for color tolerance", expectedAbsoluteTolerance.toDouble(), fillTool.colorTolerance.toDouble(), TOLERANCE_DELTA) + + // Close tool options + onToolBarView() + .performClickSelectedToolButton() + onToolBarView() + .performSelectTool(ToolType.BRUSH) + onToolBarView() + .performSelectTool(ToolType.FILL) + colorToleranceInput.check( + matches( + withText( + DEFAULT_TOLERANCE_IN_PERCENT.toString() + ) + ) + ) + colorToleranceSeekBar.check(matches(withProgress(DEFAULT_TOLERANCE_IN_PERCENT))) + } + + @Test + fun testFillToolUndoRedoWithTolerance() { + onToolBarView() + .performSelectTool(ToolType.BRUSH) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + onToolProperties() + .setColorResource(R.color.pocketpaint_color_picker_brown2) + .checkMatchesColorResource(R.color.pocketpaint_color_picker_brown2) + onToolBarView() + .performSelectTool(ToolType.FILL) + onView(withId(R.id.pocketpaint_fill_tool_dialog_color_tolerance_input)).perform(replaceText("100")) + onToolBarView() + .performCloseToolOptionsView() + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColorResource(R.color.pocketpaint_color_picker_brown2, BitmapLocationProvider.MIDDLE) + .checkPixelColorResource(R.color.pocketpaint_color_picker_brown2, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE) + onTopBarView() + .performUndo() + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE) + onTopBarView() + .performRedo() + onDrawingSurfaceView() + .checkPixelColorResource(R.color.pocketpaint_color_picker_brown2, BitmapLocationProvider.MIDDLE) + .checkPixelColorResource(R.color.pocketpaint_color_picker_brown2, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE) + } + + @Test + fun testFillBitmapAndEraseASpotAndFillTheErasedSpotAgain() { + onToolProperties() + .checkMatchesColor(Color.BLACK) + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + onToolBarView() + .performSelectTool(ToolType.ERASER) + onDrawingSurfaceView() + .perform( + swipeAccurate( + DrawingSurfaceLocationProvider.HALFWAY_TOP_MIDDLE, + DrawingSurfaceLocationProvider.HALFWAY_BOTTOM_MIDDLE + ) + ) + onView(isRoot()).perform(waitFor(1000)) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + onView(isRoot()).perform(waitFor(1000)) + onToolBarView() + .performSelectTool(ToolType.FILL) + onToolProperties() + .checkMatchesColor(Color.BLACK) + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.HALFWAY_BOTTOM_MIDDLE)) + onView(isRoot()).perform(waitFor(1000)) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + } + + companion object { + private const val TOLERANCE_DELTA = 0.05 + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/FlipToolIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/FlipToolIntegrationTest.java deleted file mode 100644 index bbb0c04063..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/FlipToolIntegrationTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2022 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.tools; - -import android.graphics.Color; - -import org.catrobat.paintroid.MainActivity; -import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider; -import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; -import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; -import org.catrobat.paintroid.tools.ToolType; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.ActivityTestRule; - -import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt; -import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.TransformToolOptionsViewInteraction.onTransformToolOptionsView; - -@RunWith(AndroidJUnit4.class) -public class FlipToolIntegrationTest { - - @Rule - public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); - - @Rule - public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); - - @Before - public void setUp() { - onToolBarView() - .performSelectTool(ToolType.BRUSH); - } - - @Test - public void testHorizontalFlip() { - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.HALFWAY_TOP_MIDDLE)); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_TOP_MIDDLE) - .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_BOTTOM_MIDDLE); - - onToolBarView() - .performSelectTool(ToolType.TRANSFORM); - - onTransformToolOptionsView() - .performFlipHorizontal(); - - onDrawingSurfaceView() - .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_TOP_MIDDLE) - .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_BOTTOM_MIDDLE); - } - - @Test - public void testVerticalFlip() { - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.HALFWAY_LEFT_MIDDLE)); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_LEFT_MIDDLE) - .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE); - - onToolBarView() - .performSelectTool(ToolType.TRANSFORM); - - onTransformToolOptionsView() - .performFlipVertical(); - - onDrawingSurfaceView() - .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_LEFT_MIDDLE) - .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/FlipToolIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/FlipToolIntegrationTest.kt new file mode 100644 index 0000000000..3afc9c4c46 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/FlipToolIntegrationTest.kt @@ -0,0 +1,84 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.tools + +import android.graphics.Color +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.ActivityTestRule +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider +import org.catrobat.paintroid.test.espresso.util.UiInteractions +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.Companion.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.Companion.onToolBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.TransformToolOptionsViewInteraction.Companion.onTransformToolOptionsView +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.catrobat.paintroid.tools.ToolType +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class FlipToolIntegrationTest { + @JvmField + @Rule + var launchActivityRule = ActivityTestRule(MainActivity::class.java) + + @JvmField + @Rule + var screenshotOnFailRule = ScreenshotOnFailRule() + @Before + fun setUp() { + onToolBarView() + .performSelectTool(ToolType.BRUSH) + } + + @Test + fun testHorizontalFlip() { + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.HALFWAY_TOP_MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_TOP_MIDDLE) + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_BOTTOM_MIDDLE) + onToolBarView() + .performSelectTool(ToolType.TRANSFORM) + onTransformToolOptionsView() + .performFlipHorizontal() + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_TOP_MIDDLE) + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_BOTTOM_MIDDLE) + } + + @Test + fun testVerticalFlip() { + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.HALFWAY_LEFT_MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_LEFT_MIDDLE) + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE) + onToolBarView() + .performSelectTool(ToolType.TRANSFORM) + onTransformToolOptionsView() + .performFlipVertical() + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_LEFT_MIDDLE) + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE) + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ImportToolIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ImportToolIntegrationTest.java deleted file mode 100644 index 009cbf8e29..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ImportToolIntegrationTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2015 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.tools; - -import org.catrobat.paintroid.MainActivity; -import org.catrobat.paintroid.R; -import org.catrobat.paintroid.test.espresso.rtl.util.RtlActivityTestRule; -import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; -import org.catrobat.paintroid.tools.ToolType; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.ActivityTestRule; - -import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; -import static org.junit.Assert.assertEquals; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; - -@RunWith(AndroidJUnit4.class) -public class ImportToolIntegrationTest { - - @Rule - public ActivityTestRule launchActivityRule = new RtlActivityTestRule<>(MainActivity.class, "ar"); - - @Rule - public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); - - private MainActivity mainActivity; - - @Before - public void setUp() { - mainActivity = launchActivityRule.getActivity(); - onToolBarView() - .performSelectTool(ToolType.IMPORTPNG); - } - - @Test - public void testImportDialogShownOnImportToolSelected() { - onView(withId(R.id.pocketpaint_dialog_import_stickers)).check(matches(isDisplayed())); - onView(withId(R.id.pocketpaint_dialog_import_gallery)).check(matches(isDisplayed())); - } - - @Test - public void testImportDialogDismissedOnCancelClicked() { - onView(withText(R.string.pocketpaint_cancel)).perform(click()); - - onView(withId(R.id.pocketpaint_dialog_import_stickers)).check(doesNotExist()); - onView(withId(R.id.pocketpaint_dialog_import_gallery)).check(doesNotExist()); - } - - @Test - public void testImportDoesNotResetPerspectiveScale() { - onView(withText(R.string.pocketpaint_cancel)).perform(click()); - - onToolBarView() - .performSelectTool(ToolType.BRUSH); - - float scale = 2.0f; - mainActivity.perspective.setScale(scale); - mainActivity.refreshDrawingSurface(); - - onToolBarView() - .performSelectTool(ToolType.IMPORTPNG); - - onView(withText(R.string.pocketpaint_cancel)).perform(click()); - - assertEquals(scale, mainActivity.perspective.getScale(), Float.MIN_VALUE); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ImportToolIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ImportToolIntegrationTest.kt new file mode 100644 index 0000000000..2dc62bf6bf --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ImportToolIntegrationTest.kt @@ -0,0 +1,89 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +@file:Suppress("DEPRECATION") + +package org.catrobat.paintroid.test.espresso.tools + +import androidx.test.espresso.Espresso +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.ActivityTestRule +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.R +import org.catrobat.paintroid.test.espresso.rtl.util.RtlActivityTestRule +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.catrobat.paintroid.tools.ToolType +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class ImportToolIntegrationTest { + + @get:Rule + var launchActivityRule: ActivityTestRule = RtlActivityTestRule(MainActivity::class.java, "ar") + + @get:Rule + var screenshotOnFailRule = ScreenshotOnFailRule() + private var mainActivity: MainActivity? = null + @Before + fun setUp() { + mainActivity = launchActivityRule.activity + ToolBarViewInteraction.onToolBarView().performSelectTool(ToolType.IMPORTPNG) + } + + @Test + fun testImportDialogShownOnImportToolSelected() { + Espresso.onView(ViewMatchers.withId(R.id.pocketpaint_dialog_import_stickers)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.pocketpaint_dialog_import_gallery)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + + @Test + fun testImportDialogDismissedOnCancelClicked() { + Espresso.onView(ViewMatchers.withText(R.string.pocketpaint_cancel)) + .perform(ViewActions.click()) + Espresso.onView(ViewMatchers.withId(R.id.pocketpaint_dialog_import_stickers)) + .check(ViewAssertions.doesNotExist()) + Espresso.onView(ViewMatchers.withId(R.id.pocketpaint_dialog_import_gallery)) + .check(ViewAssertions.doesNotExist()) + } + + @Test + fun testImportDoesNotResetPerspectiveScale() { + Espresso.onView(ViewMatchers.withText(R.string.pocketpaint_cancel)) + .perform(ViewActions.click()) + ToolBarViewInteraction.onToolBarView() + .performSelectTool(ToolType.BRUSH) + val scale = 2.0f + mainActivity?.perspective?.scale = scale + mainActivity?.refreshDrawingSurface() + ToolBarViewInteraction.onToolBarView() + .performSelectTool(ToolType.IMPORTPNG) + Espresso.onView(ViewMatchers.withText(R.string.pocketpaint_cancel)) + .perform(ViewActions.click()) + mainActivity?.perspective?.let { Assert.assertEquals(scale, it.scale, Float.MIN_VALUE) } + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/LineToolIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/LineToolIntegrationTest.kt index 48e509d52a..958f618dbf 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/LineToolIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/LineToolIntegrationTest.kt @@ -38,10 +38,10 @@ import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider import org.catrobat.paintroid.test.espresso.util.EspressoUtils.DEFAULT_STROKE_WIDTH import org.catrobat.paintroid.test.espresso.util.UiInteractions import org.catrobat.paintroid.test.espresso.util.UiMatcher -import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.Companion.onDrawingSurfaceView import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction -import org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.onToolProperties -import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.Companion.onToolProperties +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.Companion.onTopBarView import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule import org.catrobat.paintroid.test.utils.TestUtils.Companion.selectColorInDialog import org.catrobat.paintroid.tools.ToolType diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/PipetteToolIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/PipetteToolIntegrationTest.kt index a159da355f..c5c980c6fe 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/PipetteToolIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/PipetteToolIntegrationTest.kt @@ -28,12 +28,12 @@ import org.catrobat.paintroid.R import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider import org.catrobat.paintroid.test.espresso.util.UiInteractions -import org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerPreviewInteraction.onColorPickerPreview -import org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerViewInteraction.onColorPickerView -import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView -import org.catrobat.paintroid.test.espresso.util.wrappers.LayerMenuViewInteraction.onLayerMenuView -import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView -import org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.onToolProperties +import org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerPreviewInteraction.Companion.onColorPickerPreview +import org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerViewInteraction.Companion.onColorPickerView +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.Companion.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.LayerMenuViewInteraction.Companion.onLayerMenuView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.Companion.onToolBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.Companion.onToolProperties import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule import org.catrobat.paintroid.tools.ToolType diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolEraseIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolEraseIntegrationTest.java deleted file mode 100644 index 8b92a62a65..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolEraseIntegrationTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2022 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.tools; - -import android.graphics.Color; - -import org.catrobat.paintroid.MainActivity; -import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider; -import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; -import org.catrobat.paintroid.tools.ToolType; -import org.catrobat.paintroid.tools.drawable.DrawableShape; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameter; -import org.junit.runners.Parameterized.Parameters; - -import java.util.Arrays; - -import androidx.test.rule.ActivityTestRule; - -import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ShapeToolOptionsViewInteraction.onShapeToolOptionsView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.onToolProperties; -import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; - -@RunWith(Parameterized.class) -public class ShapeToolEraseIntegrationTest { - - @Parameter - public DrawableShape shape; - - @Parameters(name = "{0}") - public static Iterable data() { - return Arrays.asList(new Object[][]{ - {DrawableShape.RECTANGLE}, - {DrawableShape.OVAL}, - {DrawableShape.HEART}, - {DrawableShape.STAR} - }); - } - - @Rule - public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); - - @Rule - public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); - - @Test - public void testEraseWithFilledShape() { - - onToolBarView() - .performSelectTool(ToolType.SHAPE) - .performCloseToolOptionsView(); - - onTopBarView() - .performClickCheckmark(); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); - - onToolProperties() - .setColor(Color.TRANSPARENT); - - onToolBarView() - .performOpenToolOptionsView(); - - onShapeToolOptionsView() - .performSelectShape(shape); - - onToolBarView() - .performCloseToolOptionsView(); - - onTopBarView() - .performClickCheckmark(); - - onDrawingSurfaceView() - .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolEraseIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolEraseIntegrationTest.kt new file mode 100644 index 0000000000..43bc20fb60 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolEraseIntegrationTest.kt @@ -0,0 +1,83 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.tools + +import android.graphics.Color +import androidx.test.rule.ActivityTestRule +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.ShapeToolOptionsViewInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.catrobat.paintroid.tools.ToolType +import org.catrobat.paintroid.tools.drawable.DrawableShape +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +@RunWith(Parameterized::class) +class ShapeToolEraseIntegrationTest { + @Parameterized.Parameter + lateinit var shape: DrawableShape + + @JvmField + @Rule + val launchActivityRule = ActivityTestRule(MainActivity::class.java) + + @JvmField + @Rule + val screenshotOnFailRule = ScreenshotOnFailRule() + + companion object { + @JvmStatic + @Parameterized.Parameters(name = "{0}") + fun data() = listOf( + arrayOf(DrawableShape.RECTANGLE), + arrayOf(DrawableShape.OVAL), + arrayOf(DrawableShape.HEART), + arrayOf(DrawableShape.STAR) + ) + } + @Test + fun testEraseWithFilledShape() { + ToolBarViewInteraction.onToolBarView() + .performSelectTool(ToolType.SHAPE) + .performCloseToolOptionsView() + TopBarViewInteraction.onTopBarView() + .performClickCheckmark() + DrawingSurfaceInteraction.onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + ToolPropertiesInteraction.onToolProperties() + .setColor(Color.TRANSPARENT) + ToolBarViewInteraction.onToolBarView() + .performOpenToolOptionsView() + ShapeToolOptionsViewInteraction.onShapeToolOptionsView() + .performSelectShape(shape) + ToolBarViewInteraction.onToolBarView() + .performCloseToolOptionsView() + TopBarViewInteraction.onTopBarView() + .performClickCheckmark() + DrawingSurfaceInteraction.onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolIntegrationTest.java deleted file mode 100644 index 0e528313cf..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolIntegrationTest.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2022 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.tools; - -import android.graphics.Color; -import android.graphics.Paint; - -import org.catrobat.paintroid.MainActivity; -import org.catrobat.paintroid.R; -import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider; -import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; -import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; -import org.catrobat.paintroid.tools.ToolReference; -import org.catrobat.paintroid.tools.ToolType; -import org.catrobat.paintroid.tools.drawable.DrawableShape; -import org.catrobat.paintroid.tools.drawable.DrawableStyle; -import org.catrobat.paintroid.tools.implementation.BaseToolWithRectangleShape; -import org.catrobat.paintroid.tools.implementation.ShapeTool; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.ActivityTestRule; - -import static org.catrobat.paintroid.test.espresso.util.OffsetLocationProvider.withOffset; -import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchCenterLeft; -import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ShapeToolOptionsViewInteraction.onShapeToolOptionsView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.onToolProperties; -import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; - -@RunWith(AndroidJUnit4.class) -public class ShapeToolIntegrationTest { - - @Rule - public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); - - @Rule - public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); - - private ToolReference toolReference; - private MainActivity mainActivity; - - @Before - public void setUp() { - mainActivity = launchActivityRule.getActivity(); - toolReference = mainActivity.toolReference; - - onToolBarView() - .performSelectTool(ToolType.SHAPE); - } - - private Paint getCurrentToolBitmapPaint() { - return ((ShapeTool) toolReference.getTool()).getShapeBitmapPaint(); - } - - private Paint getToolPaint() { - return mainActivity.toolPaint.getPaint(); - } - - @Test - public void testEllipseIsDrawnOnBitmap() { - onShapeToolOptionsView() - .performSelectShape(DrawableShape.OVAL); - - BaseToolWithRectangleShape ellipseTool = (BaseToolWithRectangleShape) toolReference.getTool(); - float rectHeight = ellipseTool.boxHeight; - - onToolBarView() - .performCloseToolOptionsView(); - - onTopBarView() - .performClickCheckmark(); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) - .checkPixelColor(Color.BLACK, withOffset(BitmapLocationProvider.MIDDLE, (int) (rectHeight / 2.5f), 0)) - .checkPixelColor(Color.TRANSPARENT, withOffset(BitmapLocationProvider.MIDDLE, (int) (rectHeight / 2.5f), (int) (rectHeight / 2.5f))); - } - - @Test - public void testUndoRedo() { - onToolBarView() - .performCloseToolOptionsView(); - - onTopBarView() - .performClickCheckmark(); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); - - onTopBarView() - .performUndo(); - - onDrawingSurfaceView() - .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); - - onTopBarView() - .performRedo(); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); - } - - @Test - public void testFilledRectChangesColor() { - onToolBarView() - .performCloseToolOptionsView(); - - onToolProperties() - .setColorResource(R.color.pocketpaint_color_picker_brown1); - - onTopBarView() - .performClickCheckmark(); - - onDrawingSurfaceView() - .checkPixelColorResource(R.color.pocketpaint_color_picker_brown1, BitmapLocationProvider.MIDDLE); - } - - @Test - public void testDrawWithHeartShape() { - onShapeToolOptionsView() - .performSelectShape(DrawableShape.HEART); - - onToolBarView() - .performCloseToolOptionsView(); - - onTopBarView() - .performClickCheckmark(); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); - } - - @Test - public void testAntiAliasingIsOffIfShapeOutlineWidthIsOne() { - onToolBarView() - .performSelectTool(ToolType.SHAPE); - onShapeToolOptionsView() - .performSelectShapeDrawType(DrawableStyle.STROKE); - onShapeToolOptionsView() - .performSetOutlineWidth(touchCenterLeft()); - - drawShape(); - - Paint bitmapPaint = getCurrentToolBitmapPaint(); - Paint toolPaint = getToolPaint(); - - assertFalse("BITMAP_PAINT antialiasing should be off", bitmapPaint.isAntiAlias()); - assertTrue("TOOL_PAINT antialiasing should be on", toolPaint.isAntiAlias()); - } - - @Test - public void testDoNotUseRegularToolPaintInShapeTool() { - onToolBarView() - .performSelectTool(ToolType.SHAPE); - onShapeToolOptionsView() - .performSelectShapeDrawType(DrawableStyle.FILL); - - drawShape(); - - Paint bitmapPaint = getCurrentToolBitmapPaint(); - Paint toolPaint = getToolPaint(); - - assertNotEquals("bitmapPaint and toolPaint should differ", bitmapPaint, toolPaint); - } - - @Test - public void testShapeWithOutlineAlsoWorksWithTransparentColor() { - onToolBarView() - .performSelectTool(ToolType.SHAPE); - onShapeToolOptionsView() - .performSelectShape(DrawableShape.RECTANGLE); - onShapeToolOptionsView() - .performSelectShapeDrawType(DrawableStyle.FILL); - onToolProperties() - .setColor(Color.BLACK); - drawShape(); - onToolBarView() - .performClickSelectedToolButton(); - - onShapeToolOptionsView() - .performSelectShape(DrawableShape.OVAL); - onShapeToolOptionsView() - .performSelectShapeDrawType(DrawableStyle.STROKE); - onToolProperties() - .setColor(Color.TRANSPARENT); - drawShape(); - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, DrawingSurfaceLocationProvider.TOOL_POSITION); - onDrawingSurfaceView() - .checkPixelColor(Color.TRANSPARENT, DrawingSurfaceLocationProvider.TOP_MIDDLE); - } - - @Test - public void testShapeToolBoxGetsPlacedCorrectWhenZoomedIn() { - onToolBarView() - .performSelectTool(ToolType.BRUSH); - - mainActivity.perspective.surfaceTranslationY = 200; - mainActivity.perspective.surfaceTranslationX = 50; - mainActivity.perspective.setScale(2.0f); - mainActivity.refreshDrawingSurface(); - - onToolBarView() - .performSelectTool(ToolType.SHAPE); - onShapeToolOptionsView() - .performSelectShape(DrawableShape.RECTANGLE); - onShapeToolOptionsView() - .performSelectShapeDrawType(DrawableStyle.FILL); - onTopBarView() - .performClickCheckmark(); - - onDrawingSurfaceView() - .checkPixelColor(Color.BLACK, mainActivity.perspective.surfaceCenterX - mainActivity.perspective.surfaceTranslationX, - mainActivity.perspective.surfaceCenterY - mainActivity.perspective.surfaceTranslationY); - } - - public void drawShape() { - onToolBarView() - .performCloseToolOptionsView(); - onTopBarView() - .performClickCheckmark(); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolIntegrationTest.kt new file mode 100644 index 0000000000..398a83e93c --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolIntegrationTest.kt @@ -0,0 +1,215 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +@file:Suppress("DEPRECATION") + +package org.catrobat.paintroid.test.espresso.tools + +import android.graphics.Color +import android.graphics.Paint +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.ActivityTestRule +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.R +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider +import org.catrobat.paintroid.test.espresso.util.OffsetLocationProvider +import org.catrobat.paintroid.test.espresso.util.UiInteractions +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.Companion.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.ShapeToolOptionsViewInteraction.Companion.onShapeToolOptionsView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.Companion.onToolBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.Companion.onToolProperties +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.Companion.onTopBarView +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.catrobat.paintroid.tools.ToolReference +import org.catrobat.paintroid.tools.ToolType +import org.catrobat.paintroid.tools.drawable.DrawableShape +import org.catrobat.paintroid.tools.drawable.DrawableStyle +import org.catrobat.paintroid.tools.implementation.BaseToolWithRectangleShape +import org.catrobat.paintroid.tools.implementation.ShapeTool +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class ShapeToolIntegrationTest { + @get:Rule + var launchActivityRule = ActivityTestRule(MainActivity::class.java) + + @get:Rule + var screenshotOnFailRule = ScreenshotOnFailRule() + private var toolReference: ToolReference? = null + private lateinit var mainActivity: MainActivity + + @Before + fun setUp() { + mainActivity = launchActivityRule.activity + toolReference = mainActivity.toolReference + + onToolBarView().performSelectTool(ToolType.SHAPE) + } + + private fun getCurrentToolBitmapPaint(): Paint? = (toolReference?.tool as ShapeTool?)?.shapeBitmapPaint + + private fun getToolPaint(): Paint = mainActivity.toolPaint.paint + + @Test + fun testEllipseIsDrawnOnBitmap() { + onShapeToolOptionsView().performSelectShape(DrawableShape.OVAL) + + val ellipseTool = toolReference?.tool as BaseToolWithRectangleShape? + val rectHeight = ellipseTool?.boxHeight + + onToolBarView().performCloseToolOptionsView() + onTopBarView().performClickCheckmark() + if (rectHeight != null) { + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + .checkPixelColor( + Color.BLACK, + OffsetLocationProvider.withOffset( + BitmapLocationProvider.MIDDLE, + (rectHeight / 2.5f).toInt(), + 0 + ) + ) + .checkPixelColor( + Color.TRANSPARENT, + OffsetLocationProvider.withOffset( + BitmapLocationProvider.MIDDLE, + (rectHeight / 2.5f).toInt(), + (rectHeight / 2.5f).toInt() + ) + ) + } + } + + @Test + fun testUndoRedo() { + onToolBarView().performCloseToolOptionsView() + onTopBarView().performClickCheckmark() + onDrawingSurfaceView().checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + onTopBarView().performUndo() + onDrawingSurfaceView().checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + onTopBarView().performRedo() + onDrawingSurfaceView().checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testFilledRectChangesColor() { + onToolBarView().performCloseToolOptionsView() + onToolProperties().setColorResource(R.color.pocketpaint_color_picker_brown1) + onTopBarView().performClickCheckmark() + onDrawingSurfaceView() + .checkPixelColorResource( + R.color.pocketpaint_color_picker_brown1, + BitmapLocationProvider.MIDDLE + ) + } + + @Test + fun testDrawWithHeartShape() { + onShapeToolOptionsView().performSelectShape(DrawableShape.HEART) + onToolBarView().performCloseToolOptionsView() + onTopBarView().performClickCheckmark() + onDrawingSurfaceView().checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testAntiAliasingIsOffIfShapeOutlineWidthIsOne() { + onToolBarView().performSelectTool(ToolType.SHAPE) + onShapeToolOptionsView().performSelectShapeDrawType(DrawableStyle.STROKE) + onShapeToolOptionsView().performSetOutlineWidth(UiInteractions.touchCenterLeft()) + drawShape() + + val bitmapPaint: Paint? = getCurrentToolBitmapPaint() + val toolPaint: Paint = getToolPaint() + + if (bitmapPaint != null) { + Assert.assertFalse("BITMAP_PAINT antialiasing should be off", bitmapPaint.isAntiAlias) + } + Assert.assertTrue("TOOL_PAINT antialiasing should be on", toolPaint.isAntiAlias) + } + + @Test + fun testDoNotUseRegularToolPaintInShapeTool() { + onToolBarView().performSelectTool(ToolType.SHAPE) + onShapeToolOptionsView().performSelectShapeDrawType(DrawableStyle.FILL) + drawShape() + + val bitmapPaint: Paint? = getCurrentToolBitmapPaint() + val toolPaint: Paint = getToolPaint() + + Assert.assertNotEquals("bitmapPaint and toolPaint should differ", bitmapPaint, toolPaint) + } + + @Test + fun testShapeWithOutlineAlsoWorksWithTransparentColor() { + onToolBarView() + .performSelectTool(ToolType.SHAPE) + onShapeToolOptionsView() + .performSelectShape(DrawableShape.RECTANGLE) + onShapeToolOptionsView() + .performSelectShapeDrawType(DrawableStyle.FILL) + onToolProperties() + .setColor(Color.BLACK) + drawShape() + onToolBarView() + .performOpenToolOptionsView() + onShapeToolOptionsView() + .performSelectShape(DrawableShape.OVAL) + onShapeToolOptionsView() + .performSelectShapeDrawType(DrawableStyle.STROKE) + onToolProperties() + .setColor(Color.TRANSPARENT) + drawShape() + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, DrawingSurfaceLocationProvider.TOOL_POSITION) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, DrawingSurfaceLocationProvider.TOP_MIDDLE) + } + + @Test + fun testShapeToolBoxGetsPlacedCorrectWhenZoomedIn() { + onToolBarView().performSelectTool(ToolType.BRUSH) + + mainActivity.perspective.surfaceTranslationY = 200f + mainActivity.perspective.surfaceTranslationX = 50f + mainActivity.perspective.scale = 2.0f + mainActivity.refreshDrawingSurface() + + onToolBarView().performSelectTool(ToolType.SHAPE) + onShapeToolOptionsView().performSelectShape(DrawableShape.RECTANGLE) + onShapeToolOptionsView().performSelectShapeDrawType(DrawableStyle.FILL) + onTopBarView().performClickCheckmark() + onDrawingSurfaceView() + .checkPixelColor( + Color.BLACK, + mainActivity.perspective.surfaceCenterX - mainActivity.perspective.surfaceTranslationX, + mainActivity.perspective.surfaceCenterY - mainActivity.perspective.surfaceTranslationY + ) + } + + private fun drawShape() { + onToolBarView().performCloseToolOptionsView() + onTopBarView().performClickCheckmark() + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolOrientationIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolOrientationIntegrationTest.java deleted file mode 100644 index a37df5f3ca..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolOrientationIntegrationTest.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2022 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.tools; - -import android.content.pm.ActivityInfo; -import android.graphics.Bitmap; - -import org.catrobat.paintroid.MainActivity; -import org.catrobat.paintroid.R; -import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; -import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; -import org.catrobat.paintroid.tools.ToolType; -import org.catrobat.paintroid.tools.Workspace; -import org.catrobat.paintroid.tools.drawable.DrawableShape; -import org.catrobat.paintroid.tools.drawable.DrawableStyle; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -import java.util.Arrays; - -import androidx.test.rule.ActivityTestRule; - -import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt; -import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ShapeToolOptionsViewInteraction.onShapeToolOptionsView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; -import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; -import static org.junit.Assert.assertTrue; -import static org.junit.runners.Parameterized.Parameter; -import static org.junit.runners.Parameterized.Parameters; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.isSelected; -import static androidx.test.espresso.matcher.ViewMatchers.withId; - -@RunWith(Parameterized.class) -public class ShapeToolOrientationIntegrationTest { - - @Rule - public ActivityTestRule activityTestRule = new ActivityTestRule<>(MainActivity.class); - - @Rule - public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); - - @Parameter - public DrawableShape shape; - @Parameter(1) - public int shapeId; - - private Workspace workspace; - - @Parameters(name = "{0}") - public static Iterable data() { - return Arrays.asList(new Object[][]{ - {DrawableShape.RECTANGLE, R.id.pocketpaint_shapes_square_btn}, - {DrawableShape.OVAL, R.id.pocketpaint_shapes_circle_btn}, - {DrawableShape.HEART, R.id.pocketpaint_shapes_heart_btn}, - {DrawableShape.STAR, R.id.pocketpaint_shapes_star_btn} - }); - } - - @Before - public void setUp() { - workspace = activityTestRule.getActivity().workspace; - onToolBarView() - .performSelectTool(ToolType.SHAPE); - } - - @Test - public void testRememberShapeAfterOrientationChange() { - onShapeToolOptionsView() - .performSelectShape(shape); - onView(withId(shapeId)) - .check(matches(isSelected())); - onToolBarView() - .performCloseToolOptionsView(); - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.TOOL_POSITION)); - - Bitmap expectedBitmap = workspace.getBitmapOfCurrentLayer(); - - onTopBarView() - .performUndo(); - - activityTestRule.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - - onView(withId(shapeId)) - .check(matches(isSelected())); - - onToolBarView() - .performCloseToolOptionsView(); - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.TOOL_POSITION)); - - assertTrue(expectedBitmap.sameAs(workspace.getBitmapOfCurrentLayer())); - } - - @Test - public void testRememberOutlineShapeAfterOrientationChange() { - onShapeToolOptionsView() - .performSelectShape(shape) - .performSelectShapeDrawType(DrawableStyle.STROKE); - onView(withId(shapeId)) - .check(matches(isSelected())); - onView(withId(R.id.pocketpaint_shape_ibtn_outline)) - .check(matches(isSelected())); - onToolBarView() - .performCloseToolOptionsView(); - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.TOOL_POSITION)); - - Bitmap expectedBitmap = workspace.getBitmapOfCurrentLayer(); - onTopBarView() - .performUndo(); - - activityTestRule.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - onView(withId(shapeId)) - .check(matches(isSelected())); - onView(withId(R.id.pocketpaint_shape_ibtn_outline)) - .check(matches(isSelected())); - - onToolBarView() - .performCloseToolOptionsView(); - - onDrawingSurfaceView() - .perform(touchAt(DrawingSurfaceLocationProvider.TOOL_POSITION)); - - assertTrue(expectedBitmap.sameAs(workspace.getBitmapOfCurrentLayer())); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolOrientationIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolOrientationIntegrationTest.kt new file mode 100644 index 0000000000..0bf93a6d53 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolOrientationIntegrationTest.kt @@ -0,0 +1,133 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +@file:Suppress("DEPRECATION") + +package org.catrobat.paintroid.test.espresso.tools + +import android.content.pm.ActivityInfo +import androidx.test.espresso.Espresso +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.rule.ActivityTestRule +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.R +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider +import org.catrobat.paintroid.test.espresso.util.UiInteractions +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.Companion.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.ShapeToolOptionsViewInteraction.Companion.onShapeToolOptionsView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.Companion.onToolBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.Companion.onTopBarView +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.catrobat.paintroid.tools.ToolType +import org.catrobat.paintroid.tools.Workspace +import org.catrobat.paintroid.tools.drawable.DrawableShape +import org.catrobat.paintroid.tools.drawable.DrawableStyle +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +@RunWith(Parameterized::class) +class ShapeToolOrientationIntegrationTest { + + @get:Rule + val activityTestRule = ActivityTestRule(MainActivity::class.java) + + @get:Rule + val screenshotOnFailRule = ScreenshotOnFailRule() + + @Parameterized.Parameter + lateinit var shape: DrawableShape + + @JvmField + @Parameterized.Parameter(1) + var shapeId: Int = 0 + + lateinit var workspace: Workspace + + companion object { + @JvmStatic + @Parameterized.Parameters(name = "{0}") + fun data(): Iterable> = listOf( + arrayOf(DrawableShape.RECTANGLE, R.id.pocketpaint_shapes_square_btn), + arrayOf(DrawableShape.OVAL, R.id.pocketpaint_shapes_circle_btn), + arrayOf(DrawableShape.HEART, R.id.pocketpaint_shapes_heart_btn), + arrayOf(DrawableShape.STAR, R.id.pocketpaint_shapes_star_btn) + ) + } + + @Before + fun setUp() { + workspace = activityTestRule.activity.workspace + onToolBarView() + .performSelectTool(ToolType.SHAPE) + } + + @Test + fun testRememberShapeAfterOrientationChange() { + onShapeToolOptionsView().performSelectShape(shape) + Espresso.onView(ViewMatchers.withId(shapeId)) + .check(ViewAssertions.matches(ViewMatchers.isSelected())) + onToolBarView().performCloseToolOptionsView() + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.TOOL_POSITION)) + val expectedBitmap = workspace.bitmapOfCurrentLayer + onTopBarView().performUndo() + activityTestRule.activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + Espresso.onView(ViewMatchers.withId(shapeId)) + .check(ViewAssertions.matches(ViewMatchers.isSelected())) + onToolBarView().performCloseToolOptionsView() + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.TOOL_POSITION)) + expectedBitmap?.sameAs(workspace.bitmapOfCurrentLayer)?.let { Assert.assertTrue(it) } + } + + @Test + fun testRememberOutlineShapeAfterOrientationChange() { + onShapeToolOptionsView() + .performSelectShape(shape) + .performSelectShapeDrawType(DrawableStyle.STROKE) + Espresso.onView(ViewMatchers.withId(shapeId)) + .check(ViewAssertions.matches(ViewMatchers.isSelected())) + Espresso.onView(ViewMatchers.withId(R.id.pocketpaint_shape_ibtn_outline)) + .check(ViewAssertions.matches(ViewMatchers.isSelected())) + onToolBarView() + .performCloseToolOptionsView() + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.TOOL_POSITION)) + val expectedBitmap = workspace.bitmapOfCurrentLayer + onTopBarView() + .performUndo() + activityTestRule.activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + Espresso.onView(ViewMatchers.withId(shapeId)) + .check(ViewAssertions.matches(ViewMatchers.isSelected())) + Espresso.onView(ViewMatchers.withId(R.id.pocketpaint_shape_ibtn_outline)) + .check(ViewAssertions.matches(ViewMatchers.isSelected())) + onToolBarView() + .performCloseToolOptionsView() + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.TOOL_POSITION)) + if (expectedBitmap != null) { + Assert.assertTrue(expectedBitmap.sameAs(workspace.bitmapOfCurrentLayer)) + } + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/SmudgeToolIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/SmudgeToolIntegrationTest.kt index d9353fc856..47439537e5 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/SmudgeToolIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/SmudgeToolIntegrationTest.kt @@ -63,12 +63,12 @@ class SmudgeToolIntegrationTest { fun testSmudgeToolOptionsDialog() { val smudgeTool = toolReference.tool as SmudgeTool ToolBarViewInteraction.onToolBarView() - .performClickSelectedToolButton() + .performSelectTool(ToolType.SMUDGE) val pressureInput = onView(withId(R.id.pocketpaint_smudge_tool_dialog_pressure_input)) val pressureSeekBar = onView(withId(R.id.pocketpaint_pressure_seek_bar)) val testPressureText = "100" - pressureInput.check(matches(withText(Integer.toString(DEFAULT_PRESSURE_IN_PERCENT)))) + pressureInput.check(matches(withText(DEFAULT_PRESSURE_IN_PERCENT.toString()))) pressureInput.perform(replaceText(testPressureText), ViewActions.closeSoftKeyboard()) pressureInput.check(matches(withText(testPressureText))) pressureSeekBar.check(matches(withProgress(testPressureText.toInt()))) @@ -78,7 +78,7 @@ class SmudgeToolIntegrationTest { val dragInput = onView(withId(R.id.pocketpaint_smudge_tool_dialog_drag_input)) val dragSeekBar = onView(withId(R.id.pocketpaint_drag_seek_bar)) val testDragText = "100" - dragInput.check(matches(withText(Integer.toString(DEFAULT_DRAG_IN_PERCENT)))) + dragInput.check(matches(withText(DEFAULT_DRAG_IN_PERCENT.toString()))) dragInput.perform(replaceText(testDragText), ViewActions.closeSoftKeyboard()) dragInput.check(matches(withText(testDragText))) dragSeekBar.check(matches(withProgress(testDragText.toInt()))) diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/SprayToolIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/SprayToolIntegrationTest.kt index 7c3374c18a..c09bb8fc8d 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/SprayToolIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/SprayToolIntegrationTest.kt @@ -18,7 +18,10 @@ */ package org.catrobat.paintroid.test.espresso.tools +import android.graphics.Color import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.replaceText import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.withId @@ -27,11 +30,20 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.rule.ActivityTestRule import org.catrobat.paintroid.MainActivity import org.catrobat.paintroid.R +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider +import org.catrobat.paintroid.test.espresso.util.UiInteractions +import org.catrobat.paintroid.test.espresso.util.UiMatcher import org.catrobat.paintroid.test.espresso.util.UiMatcher.withProgress +import org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerViewInteraction.Companion.onColorPickerView +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.Companion.onDrawingSurfaceView import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.Companion.onToolProperties import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.catrobat.paintroid.tools.ToolReference import org.catrobat.paintroid.tools.ToolType import org.catrobat.paintroid.ui.tools.MIN_RADIUS +import org.hamcrest.Matchers.allOf import org.junit.Before import org.junit.Rule import org.junit.Test @@ -39,6 +51,7 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class SprayToolIntegrationTest { + private var toolReference: ToolReference? = null @get:Rule var launchActivityRule = ActivityTestRule(MainActivity::class.java) @@ -48,6 +61,7 @@ class SprayToolIntegrationTest { @Before fun setUp() { + toolReference = launchActivityRule.activity.toolReference ToolBarViewInteraction.onToolBarView().performSelectTool(ToolType.SPRAY) } @@ -61,4 +75,129 @@ class SprayToolIntegrationTest { onView(withId(R.id.pocketpaint_spray_radius_seek_bar)) .check(matches(withProgress(MIN_RADIUS))) } + + @Test + fun testSprayToolColor() { + ToolBarViewInteraction.onToolBarView() + .performSelectTool(ToolType.SPRAY) + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testSprayToolTransparentColor() { + ToolBarViewInteraction.onToolBarView() + .performSelectTool(ToolType.SPRAY) + onColorPickerView() + .performOpenColorPicker() + onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_preset) + ) + ).perform(click()) + onView(withId(R.id.color_alpha_slider)).perform( + ViewActions.scrollTo(), + UiInteractions.touchCenterMiddle() + ) + onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(click()) + onColorPickerView() + .onPositiveButton() + .perform(click()) + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + + val selectedColor = toolReference?.tool?.drawPaint!!.color + onDrawingSurfaceView() + .checkPixelColor(selectedColor, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testSprayToolWithHandleMoveColor() { + ToolBarViewInteraction.onToolBarView() + .performSelectTool(ToolType.SPRAY) + onToolProperties() + .setColor(Color.BLACK) + onDrawingSurfaceView() + .perform(UiInteractions.swipe(DrawingSurfaceLocationProvider.MIDDLE, DrawingSurfaceLocationProvider.BOTTOM_MIDDLE)) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testSprayToolWithHandleMoveTransparentColor() { + ToolBarViewInteraction.onToolBarView() + .performSelectTool(ToolType.SPRAY) + onColorPickerView() + .performOpenColorPicker() + onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_preset) + ) + ).perform(click()) + onView(withId(R.id.color_alpha_slider)).perform( + ViewActions.scrollTo(), + UiInteractions.touchCenterMiddle() + ) + onView( + allOf( + withId(R.id.color_picker_tab_icon), + UiMatcher.withBackground(R.drawable.ic_color_picker_tab_rgba) + ) + ).perform(click()) + onColorPickerView() + .onPositiveButton() + .perform(click()) + onDrawingSurfaceView() + .perform( + UiInteractions.swipe( + DrawingSurfaceLocationProvider.MIDDLE, + DrawingSurfaceLocationProvider.BOTTOM_MIDDLE + ) + ) + + val selectedColor = toolReference?.tool?.drawPaint!!.color + onDrawingSurfaceView() + .checkPixelColor(selectedColor, BitmapLocationProvider.MIDDLE) + } + + fun testSprayRadiusToStrokeWidthStaysConsistent() { + ToolBarViewInteraction.onToolBarView().performSelectTool(ToolType.BRUSH) + + var radius = "30" + onView(withId(R.id.pocketpaint_stroke_width_width_text)) + .perform(replaceText(radius)) + .check(matches(withText(radius))) + onView(withId(R.id.pocketpaint_stroke_width_seek_bar)) + .check(matches(withProgress(radius.toInt()))) + + ToolBarViewInteraction.onToolBarView().performSelectTool(ToolType.SPRAY) + onView(withId(R.id.pocketpaint_radius_text)) + .perform(replaceText(radius)) + .check(matches(withText(radius))) + onView(withId(R.id.pocketpaint_spray_radius_seek_bar)) + .check(matches(withProgress(radius.toInt()))) + + radius = "20" + onView(withId(R.id.pocketpaint_radius_text)) + .perform(replaceText(radius)) + .check(matches(withText(radius))) + onView(withId(R.id.pocketpaint_spray_radius_seek_bar)) + .check(matches(withProgress(radius.toInt()))) + + ToolBarViewInteraction.onToolBarView().performSelectTool(ToolType.BRUSH) + onView(withId(R.id.pocketpaint_stroke_width_width_text)) + .perform(replaceText(radius)) + .check(matches(withText(radius))) + onView(withId(R.id.pocketpaint_stroke_width_seek_bar)) + .check(matches(withProgress(radius.toInt()))) + } } diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolFontListTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolFontListTest.kt index 4b7a34bc56..99bf3cfd0e 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolFontListTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolFontListTest.kt @@ -34,7 +34,7 @@ import androidx.test.rule.ActivityTestRule import org.catrobat.paintroid.MainActivity import org.catrobat.paintroid.R import org.catrobat.paintroid.test.espresso.util.UiMatcher -import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.Companion.onToolBarView import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule import org.catrobat.paintroid.tools.ToolType import org.junit.Rule @@ -110,7 +110,7 @@ class TextToolFontListTest { 3, ViewMatchers.hasDescendant( UiMatcher.hasTypeFace( - dubaiFontFace + dubaiFontFace!! ) ) ) @@ -123,7 +123,7 @@ class TextToolFontListTest { UiMatcher.atPosition( 4, ViewMatchers.hasDescendant( - UiMatcher.hasTypeFace(stcFontFace) + UiMatcher.hasTypeFace(stcFontFace!!) ) ) ) diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolFontListTestArabic.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolFontListTestArabic.kt index b376142122..9c055368aa 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolFontListTestArabic.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolFontListTestArabic.kt @@ -38,7 +38,7 @@ import org.catrobat.paintroid.test.espresso.rtl.util.RtlActivityTestRule import org.catrobat.paintroid.test.espresso.rtl.util.RtlUiTestUtils import org.catrobat.paintroid.test.espresso.util.EspressoUtils.configuration import org.catrobat.paintroid.test.espresso.util.UiMatcher -import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.Companion.onToolBarView import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule import org.catrobat.paintroid.tools.ToolType import org.junit.Assert @@ -118,7 +118,7 @@ class TextToolFontListTestArabic { 3, ViewMatchers.hasDescendant( UiMatcher.hasTypeFace( - dubaiFontFace + dubaiFontFace!! ) ) ) @@ -131,7 +131,7 @@ class TextToolFontListTestArabic { UiMatcher.atPosition( 4, ViewMatchers.hasDescendant( - UiMatcher.hasTypeFace(stcFontFace) + UiMatcher.hasTypeFace(stcFontFace!!) ) ) ) diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolIntegrationTest.kt index eeffbf060c..f17d6d50b0 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolIntegrationTest.kt @@ -34,9 +34,12 @@ import androidx.test.espresso.Espresso.onView import androidx.test.espresso.Espresso.pressBack import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions.click -import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.RecyclerViewActions import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withHint +import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.rule.ActivityTestRule import com.google.android.material.button.MaterialButton @@ -49,10 +52,10 @@ import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider import org.catrobat.paintroid.test.espresso.util.MainActivityHelper import org.catrobat.paintroid.test.espresso.util.UiInteractions import org.catrobat.paintroid.test.espresso.util.UiMatcher -import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView -import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView -import org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.onToolProperties -import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.Companion.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.Companion.onToolBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.Companion.onToolProperties +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.Companion.onTopBarView import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule import org.catrobat.paintroid.tools.FontType import org.catrobat.paintroid.tools.ToolReference @@ -62,6 +65,7 @@ import org.catrobat.paintroid.tools.implementation.MARGIN_TOP import org.catrobat.paintroid.tools.implementation.TEXT_SIZE_MAGNIFICATION_FACTOR import org.catrobat.paintroid.tools.implementation.TextTool import org.catrobat.paintroid.ui.tools.FontListAdapter +import org.hamcrest.CoreMatchers.not import org.junit.Assert import org.junit.Before import org.junit.Ignore @@ -69,11 +73,6 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import kotlin.math.roundToInt -import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.espresso.matcher.ViewMatchers.withHint -import androidx.test.espresso.matcher.ViewMatchers.isDisplayed -import org.hamcrest.CoreMatchers.not @RunWith(AndroidJUnit4::class) @Suppress("LargeClass") @@ -93,7 +92,7 @@ class TextToolIntegrationTest { private var boldToggleButton: MaterialButton? = null private var textSize: EditText? = null private var layerModel: LayerContracts.Model? = null - private var activity: MainActivity? = null + private lateinit var activity: MainActivity private var toolReference: ToolReference? = null private val surfaceBitmapHeight = layerModel?.height @@ -105,19 +104,19 @@ class TextToolIntegrationTest { fun setUp() { activity = launchActivityRule.activity activityHelper = MainActivityHelper(activity) - layerModel = activity?.layerModel - toolReference = activity?.toolReference + layerModel = activity.layerModel + toolReference = activity.toolReference onToolBarView().performSelectTool(ToolType.TEXT) textTool = toolReference?.tool as TextTool - textEditText = activity?.findViewById(R.id.pocketpaint_text_tool_dialog_input_text) - fontList = activity?.findViewById(R.id.pocketpaint_text_tool_dialog_list_font) + textEditText = activity.findViewById(R.id.pocketpaint_text_tool_dialog_input_text) + fontList = activity.findViewById(R.id.pocketpaint_text_tool_dialog_list_font) underlinedToggleButton = - activity?.findViewById(R.id.pocketpaint_text_tool_dialog_toggle_underlined) - italicToggleButton = activity?.findViewById(R.id.pocketpaint_text_tool_dialog_toggle_italic) - boldToggleButton = activity?.findViewById(R.id.pocketpaint_text_tool_dialog_toggle_bold) - textSize = activity?.findViewById(R.id.pocketpaint_font_size_text) + activity.findViewById(R.id.pocketpaint_text_tool_dialog_toggle_underlined) + italicToggleButton = activity.findViewById(R.id.pocketpaint_text_tool_dialog_toggle_italic) + boldToggleButton = activity.findViewById(R.id.pocketpaint_text_tool_dialog_toggle_bold) + textSize = activity.findViewById(R.id.pocketpaint_font_size_text) textTool?.resetBoxPosition() } @@ -176,7 +175,7 @@ class TextToolIntegrationTest { @Test fun testDialogKeyboardTextBoxAppearanceOnStartup() { onView(withId(R.id.pocketpaint_text_tool_dialog_input_text)) - .check(ViewAssertions.matches(ViewMatchers.hasFocus())) + .check(matches(ViewMatchers.hasFocus())) checkTextBoxDimensionsAndDefaultPosition() } @@ -184,12 +183,12 @@ class TextToolIntegrationTest { @Test fun testDialogDefaultValues() { onView(withId(R.id.pocketpaint_text_tool_dialog_input_text)) - .check(ViewAssertions.matches(withHint(R.string.text_tool_dialog_input_hint))) - .check(ViewAssertions.matches(ViewMatchers.withText(textTool?.text))) + .check(matches(withHint(R.string.text_tool_dialog_input_hint))) + .check(matches(ViewMatchers.withText(textTool?.text))) onToolBarView().performSelectTool(ToolType.TEXT) onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)) .check( - ViewAssertions.matches( + matches( UiMatcher.atPosition( 0, ViewMatchers.hasDescendant( @@ -200,7 +199,7 @@ class TextToolIntegrationTest { ) onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)) .check( - ViewAssertions.matches( + matches( UiMatcher.atPosition( 1, ViewMatchers.hasDescendant( @@ -212,7 +211,7 @@ class TextToolIntegrationTest { onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)) .perform(RecyclerViewActions.scrollToPosition(2)) .check( - ViewAssertions.matches( + matches( UiMatcher.atPosition( 2, ViewMatchers.hasDescendant( @@ -224,7 +223,7 @@ class TextToolIntegrationTest { onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)) .perform(RecyclerViewActions.scrollToPosition(3)) .check( - ViewAssertions.matches( + matches( UiMatcher.atPosition( 3, ViewMatchers.hasDescendant( @@ -236,7 +235,7 @@ class TextToolIntegrationTest { onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)) .perform(RecyclerViewActions.scrollToPosition(4)) .check( - ViewAssertions.matches( + matches( UiMatcher.atPosition( 4, ViewMatchers.hasDescendant( @@ -338,7 +337,7 @@ class TextToolIntegrationTest { val oldBoxWidth = toolMemberBoxWidth val oldBoxHeight = toolMemberBoxHeight - activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE Assert.assertEquals(TEST_TEXT, textEditText?.text.toString()) Assert.assertEquals(FontType.SANS_SERIF, (fontList?.adapter as FontListAdapter?)?.getSelectedItem()) @@ -695,17 +694,17 @@ class TextToolIntegrationTest { onToolBarView() .performSelectTool(ToolType.BRUSH) val scale = 2.0f - activity?.perspective?.scale = scale - activity?.perspective?.surfaceTranslationY = 200f - activity?.perspective?.surfaceTranslationX = 50f - activity?.refreshDrawingSurface() + activity.perspective.scale = scale + activity.perspective.surfaceTranslationY = 200f + activity.perspective.surfaceTranslationX = 50f + activity.refreshDrawingSurface() onToolBarView() .performSelectTool(ToolType.TEXT) runBlocking { delay(1500) } enterTestText() - activity?.perspective?.let { Assert.assertEquals(scale, it.scale, 0.0001f) } + activity.perspective.let { Assert.assertEquals(scale, it.scale, 0.0001f) } } @Test @@ -717,10 +716,10 @@ class TextToolIntegrationTest { onToolBarView().performSelectTool(ToolType.BRUSH) val scale = 2.0f - activity?.perspective?.scale = scale - activity?.perspective?.surfaceTranslationY = 200f - activity?.perspective?.surfaceTranslationX = 50f - activity?.refreshDrawingSurface() + activity.perspective.scale = scale + activity.perspective.surfaceTranslationY = 200f + activity.perspective.surfaceTranslationX = 50f + activity.refreshDrawingSurface() onToolBarView().performSelectTool(ToolType.TEXT) runBlocking { @@ -728,10 +727,10 @@ class TextToolIntegrationTest { } enterTestText() - textToolAfterZoom = activity?.toolReference?.tool as TextTool + textToolAfterZoom = activity.toolReference.tool as TextTool val positionAfterZoom = toolMemberBoxPosition - Assert.assertEquals(scale, activity!!.perspective.scale, 0.0001f) + Assert.assertEquals(scale, activity.perspective.scale, 0.0001f) onTopBarView().performClickCheckmark() Assert.assertNotEquals(initialPosition, positionAfterZoom) } @@ -853,7 +852,7 @@ class TextToolIntegrationTest { .perform(ViewActions.replaceText(textToEnter)) Espresso.closeSoftKeyboard() onView(withId(R.id.pocketpaint_text_tool_dialog_input_text)) - .check(ViewAssertions.matches(ViewMatchers.withText(textToEnter))) + .check(matches(ViewMatchers.withText(textToEnter))) } private fun enterTestText() { enterTextInput(TEST_TEXT) } @@ -861,7 +860,7 @@ class TextToolIntegrationTest { private fun enterMultilineTestText() { enterTextInput(TEST_TEXT_MULTILINE) } private fun selectFormatting(format: FormattingOptions) { - onView(ViewMatchers.withText(getFormattingOptionAsString(format))).perform(ViewActions.click()) + onView(ViewMatchers.withText(getFormattingOptionAsString(format))).perform(click()) } private fun selectFontType(fontType: FontType) { @@ -874,27 +873,25 @@ class TextToolIntegrationTest { ) ) onView(ViewMatchers.withText(getFontTypeAsString(fontType))) - .perform(ViewActions.click()) + .perform(click()) } private fun getFontTypeAsString(fontType: FontType): String? { return when (fontType) { - FontType.SANS_SERIF -> activity?.getString(R.string.text_tool_dialog_font_sans_serif) - FontType.SERIF -> activity?.getString(R.string.text_tool_dialog_font_serif) - FontType.MONOSPACE -> activity?.getString(R.string.text_tool_dialog_font_monospace) - FontType.STC -> activity?.getString(R.string.text_tool_dialog_font_arabic_stc) - FontType.DUBAI -> activity?.getString(R.string.text_tool_dialog_font_dubai) + FontType.SANS_SERIF -> activity.getString(R.string.text_tool_dialog_font_sans_serif) + FontType.SERIF -> activity.getString(R.string.text_tool_dialog_font_serif) + FontType.MONOSPACE -> activity.getString(R.string.text_tool_dialog_font_monospace) + FontType.STC -> activity.getString(R.string.text_tool_dialog_font_arabic_stc) + FontType.DUBAI -> activity.getString(R.string.text_tool_dialog_font_dubai) else -> null } } private fun getFormattingOptionAsString(format: FormattingOptions): String { return when (format) { - FormattingOptions.UNDERLINE -> activity?.getString(R.string.text_tool_dialog_underline_shortcut) - .toString() - FormattingOptions.ITALIC -> activity?.getString(R.string.text_tool_dialog_italic_shortcut) - .toString() - FormattingOptions.BOLD -> activity?.getString(R.string.text_tool_dialog_bold_shortcut).toString() + FormattingOptions.UNDERLINE -> activity.getString(R.string.text_tool_dialog_underline_shortcut) + FormattingOptions.ITALIC -> activity.getString(R.string.text_tool_dialog_italic_shortcut) + FormattingOptions.BOLD -> activity.getString(R.string.text_tool_dialog_bold_shortcut) } } diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TransformToolIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TransformToolIntegrationTest.kt index c2e292a489..03f955f52e 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TransformToolIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TransformToolIntegrationTest.kt @@ -38,12 +38,12 @@ import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider import org.catrobat.paintroid.test.espresso.util.EspressoUtils.waitForToast import org.catrobat.paintroid.test.espresso.util.MainActivityHelper import org.catrobat.paintroid.test.espresso.util.UiInteractions -import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.Companion.onDrawingSurfaceView import org.catrobat.paintroid.test.espresso.util.wrappers.LayerMenuViewInteraction -import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.Companion.onToolBarView import org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction -import org.catrobat.paintroid.test.espresso.util.wrappers.TransformToolOptionsViewInteraction.onTransformToolOptionsView +import org.catrobat.paintroid.test.espresso.util.wrappers.TransformToolOptionsViewInteraction.Companion.onTransformToolOptionsView import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule import org.catrobat.paintroid.tools.ToolReference import org.catrobat.paintroid.tools.ToolType @@ -129,7 +129,7 @@ class TransformToolIntegrationTest { displayWidth = activityHelper.displayWidth displayHeight = activityHelper.displayHeight maxBitmapSize = displayHeight * displayWidth * MAXIMUM_BITMAP_SIZE_FACTOR.toInt() - val workingBitmap = layerModel.currentLayer!!.bitmap!! + val workingBitmap = layerModel.currentLayer!!.bitmap initialWidth = workingBitmap.width initialHeight = workingBitmap.height onToolBarView() @@ -188,8 +188,8 @@ class TransformToolIntegrationTest { .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) onToolBarView() .performSelectTool(ToolType.TRANSFORM) - val width = layerModel.currentLayer!!.bitmap!!.width - val height = layerModel.currentLayer!!.bitmap!!.height + val width = layerModel.currentLayer!!.bitmap.width + val height = layerModel.currentLayer!!.bitmap.height val position = newPointF(toolPosition) runBlocking { onTransformToolOptionsView() @@ -276,7 +276,7 @@ class TransformToolIntegrationTest { ) assertEquals(boundingBoxWidth, toolSelectionBoxWidth, Float.MIN_VALUE) - assertThat(boundingBoxHeight, greaterThan(toolSelectionBoxHeight)) + assertEquals(boundingBoxHeight, toolSelectionBoxHeight, Float.MIN_VALUE) } @Test @@ -296,7 +296,7 @@ class TransformToolIntegrationTest { @Test fun testIfOnePixelIsFound() { - val workingBitmap = layerModel.currentLayer!!.bitmap!! + val workingBitmap = layerModel.currentLayer!!.bitmap workingBitmap.setPixel(initialWidth / 2, initialHeight / 2, Color.BLACK) onToolBarView() .performSelectTool(ToolType.TRANSFORM) @@ -313,7 +313,7 @@ class TransformToolIntegrationTest { @Test fun testIfMultiplePixelAreFound() { - val workingBitmap = layerModel.currentLayer!!.bitmap!! + val workingBitmap = layerModel.currentLayer!!.bitmap workingBitmap.setPixel(1, 1, Color.BLACK) workingBitmap.setPixel(initialWidth - 1, initialHeight - 1, Color.BLACK) onToolBarView() @@ -331,7 +331,7 @@ class TransformToolIntegrationTest { @Test fun testIfDrawingSurfaceBoundsAreFoundAndNotCropped() { - val workingBitmap = layerModel.currentLayer!!.bitmap!! + val workingBitmap = layerModel.currentLayer!!.bitmap workingBitmap.setPixel(initialWidth / 2, 0, Color.BLACK) workingBitmap.setPixel(0, initialHeight / 2, Color.BLACK) workingBitmap.setPixel(initialWidth - 1, initialHeight / 2, Color.BLACK) @@ -353,7 +353,7 @@ class TransformToolIntegrationTest { fun testIfClickOnCanvasCrops() { onToolBarView() .performSelectTool(ToolType.TRANSFORM) - var workingBitmap = layerModel.currentLayer!!.bitmap!! + var workingBitmap = layerModel.currentLayer!!.bitmap workingBitmap.eraseColor(Color.BLACK) for (indexWidth in 0 until initialWidth) { workingBitmap.setPixel(indexWidth, 0, Color.TRANSPARENT) @@ -367,7 +367,7 @@ class TransformToolIntegrationTest { .performClickCheckmark() onDrawingSurfaceView() .checkBitmapDimension(initialWidth, --initialHeight) - workingBitmap = layerModel.currentLayer!!.bitmap!! + workingBitmap = layerModel.currentLayer!!.bitmap for (indexWidth in 0 until initialWidth) { workingBitmap.setPixel(indexWidth, initialHeight - 1, Color.TRANSPARENT) } @@ -382,7 +382,7 @@ class TransformToolIntegrationTest { .performClickCheckmark() onDrawingSurfaceView() .checkBitmapDimension(initialWidth, --initialHeight) - workingBitmap = layerModel.currentLayer!!.bitmap!! + workingBitmap = layerModel.currentLayer!!.bitmap for (indexHeight in 0 until initialHeight) { workingBitmap.setPixel(0, indexHeight, Color.TRANSPARENT) } @@ -397,7 +397,7 @@ class TransformToolIntegrationTest { .performClickCheckmark() onDrawingSurfaceView() .checkBitmapDimension(--initialWidth, initialHeight) - workingBitmap = layerModel.currentLayer!!.bitmap!! + workingBitmap = layerModel.currentLayer!!.bitmap for (indexHeight in 0 until initialHeight) { workingBitmap.setPixel(initialWidth - 1, indexHeight, Color.TRANSPARENT) } @@ -416,7 +416,7 @@ class TransformToolIntegrationTest { @Test fun testSmallBitmapResizing() { - val workingBitmap = layerModel.currentLayer!!.bitmap!! + val workingBitmap = layerModel.currentLayer!!.bitmap workingBitmap.setPixel(initialWidth / 2, initialHeight / 2, Color.BLACK) onToolBarView() .performSelectTool(ToolType.TRANSFORM) @@ -438,7 +438,7 @@ class TransformToolIntegrationTest { @Test fun testCenterBitmapAfterCropAndUndo() { - drawPlus(layerModel.currentLayer!!.bitmap!!, initialWidth / 2) + drawPlus(layerModel.currentLayer!!.bitmap, initialWidth / 2) onToolBarView() .performSelectTool(ToolType.TRANSFORM) runBlocking { @@ -448,12 +448,12 @@ class TransformToolIntegrationTest { } TopBarViewInteraction.onTopBarView() .performClickCheckmark() - val croppedBitmap = layerModel.currentLayer!!.bitmap!! + val croppedBitmap = layerModel.currentLayer!!.bitmap assertThat(initialHeight, greaterThan(croppedBitmap.height)) assertThat(initialWidth, greaterThan(croppedBitmap.width)) TopBarViewInteraction.onTopBarView() .performUndo() - val undoBitmap = layerModel.currentLayer!!.bitmap!! + val undoBitmap = layerModel.currentLayer!!.bitmap assertEquals( "undoBitmap.getHeight should be initialHeight", undoBitmap.height.toLong(), @@ -468,7 +468,7 @@ class TransformToolIntegrationTest { @Test fun testIfBordersAreAlignedCorrectAfterCrop() { - drawPlus(layerModel.currentLayer!!.bitmap!!, initialWidth / 2) + drawPlus(layerModel.currentLayer!!.bitmap, initialWidth / 2) onToolBarView() .performSelectTool(ToolType.TRANSFORM) runBlocking { @@ -478,7 +478,7 @@ class TransformToolIntegrationTest { } onDrawingSurfaceView() .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.TOOL_POSITION)) - val croppedBitmap = layerModel.currentLayer!!.bitmap!! + val croppedBitmap = layerModel.currentLayer!!.bitmap val width = croppedBitmap.width val height = croppedBitmap.height val tool = toolReference.tool as TransformTool @@ -490,7 +490,7 @@ class TransformToolIntegrationTest { @Test fun testMoveLeftCroppingBorderAndDoCrop() { - drawPlus(layerModel.currentLayer!!.bitmap!!, initialWidth / 2) + drawPlus(layerModel.currentLayer!!.bitmap, initialWidth / 2) onToolBarView() .performSelectTool(ToolType.TRANSFORM) runBlocking { @@ -500,7 +500,7 @@ class TransformToolIntegrationTest { } TopBarViewInteraction.onTopBarView() .performClickCheckmark() - val height = layerModel.currentLayer!!.bitmap!!.height + val height = layerModel.currentLayer!!.bitmap.height val toolPosition = toolPosition val newSelectionBoxWidth: Float = toolSelectionBoxWidth / 2 toolSelectionBoxWidth = newSelectionBoxWidth @@ -515,7 +515,7 @@ class TransformToolIntegrationTest { @Test fun testMoveRightCroppingBorderAndDoCrop() { - drawPlus(layerModel.currentLayer!!.bitmap!!, initialWidth / 2) + drawPlus(layerModel.currentLayer!!.bitmap, initialWidth / 2) onToolBarView() .performSelectTool(ToolType.TRANSFORM) runBlocking { @@ -524,7 +524,7 @@ class TransformToolIntegrationTest { } TopBarViewInteraction.onTopBarView() .performClickCheckmark() - val height = layerModel.currentLayer!!.bitmap!!.height + val height = layerModel.currentLayer!!.bitmap.height val toolPosition = toolPosition val newSelectionBoxWidth: Float = toolSelectionBoxWidth / 2 toolSelectionBoxWidth = newSelectionBoxWidth @@ -537,7 +537,7 @@ class TransformToolIntegrationTest { @Test fun testMoveTopCroppingBorderAndDoCrop() { - drawPlus(layerModel.currentLayer!!.bitmap!!, initialWidth / 2) + drawPlus(layerModel.currentLayer!!.bitmap, initialWidth / 2) onToolBarView() .performSelectTool(ToolType.TRANSFORM) runBlocking { @@ -547,7 +547,7 @@ class TransformToolIntegrationTest { } TopBarViewInteraction.onTopBarView() .performClickCheckmark() - val width = layerModel.currentLayer!!.bitmap!!.width + val width = layerModel.currentLayer!!.bitmap.width val toolPosition = toolPosition val newSelectionBoxHeight: Float = toolSelectionBoxHeight / 2 toolSelectionBoxHeight = newSelectionBoxHeight @@ -560,7 +560,7 @@ class TransformToolIntegrationTest { @Test fun testMoveBottomCroppingBorderAndDoCrop() { - drawPlus(layerModel.currentLayer!!.bitmap!!, initialWidth / 2) + drawPlus(layerModel.currentLayer!!.bitmap, initialWidth / 2) onToolBarView() .performSelectTool(ToolType.TRANSFORM) runBlocking { @@ -570,7 +570,7 @@ class TransformToolIntegrationTest { } TopBarViewInteraction.onTopBarView() .performClickCheckmark() - val width = layerModel.currentLayer!!.bitmap!!.width + val width = layerModel.currentLayer!!.bitmap.width val toolPosition = toolPosition val newSelectionBoxHeight: Float = toolSelectionBoxHeight / 2 toolSelectionBoxHeight = newSelectionBoxHeight @@ -601,7 +601,7 @@ class TransformToolIntegrationTest { var height = initialHeight var cropSize = initialWidth / 8 width -= cropSize - layerModel.currentLayer!!.bitmap!!.setPixels( + layerModel.currentLayer!!.bitmap.setPixels( IntArray(cropSize * height), 0, cropSize, @@ -623,7 +623,7 @@ class TransformToolIntegrationTest { .checkBitmapDimension(width, height) cropSize = initialHeight / 8 height -= cropSize - layerModel.currentLayer!!.bitmap!!.setPixels( + layerModel.currentLayer!!.bitmap.setPixels( IntArray(cropSize * width), 0, width, 0, 0, width, cropSize ) @@ -640,7 +640,7 @@ class TransformToolIntegrationTest { .checkBitmapDimension(width, height) cropSize = initialWidth / 8 width -= cropSize - layerModel.currentLayer!!.bitmap!!.setPixels( + layerModel.currentLayer!!.bitmap.setPixels( IntArray(cropSize * height), 0, cropSize, @@ -662,7 +662,7 @@ class TransformToolIntegrationTest { .checkBitmapDimension(width, height) cropSize = initialHeight / 8 height -= cropSize - layerModel.currentLayer!!.bitmap!!.setPixels( + layerModel.currentLayer!!.bitmap.setPixels( IntArray(cropSize * width), 0, width, @@ -689,11 +689,11 @@ class TransformToolIntegrationTest { onToolBarView() .performSelectTool(ToolType.TRANSFORM) .performCloseToolOptionsView() - drawPlus(layerModel.currentLayer!!.bitmap!!, initialWidth / 2) + drawPlus(layerModel.currentLayer!!.bitmap, initialWidth / 2) setToolSelectionBoxDimensions(initialWidth / 8f, initialHeight / 8f) onDrawingSurfaceView() .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.TOOL_POSITION)) - val croppedBitmap = layerModel.currentLayer!!.bitmap!! + val croppedBitmap = layerModel.currentLayer!!.bitmap val height = croppedBitmap.height val width = croppedBitmap.width val tool = toolReference.tool as TransformTool @@ -718,7 +718,7 @@ class TransformToolIntegrationTest { onDrawingSurfaceView() .perform(UiInteractions.swipe(dragFrom, dragTo)) .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.TOOL_POSITION)) - val enlargedBitmap = layerModel.currentLayer!!.bitmap!! + val enlargedBitmap = layerModel.currentLayer!!.bitmap val bitmapSize = enlargedBitmap.height + enlargedBitmap.width assertTrue(bitmapSize < maxBitmapSize) } @@ -759,38 +759,38 @@ class TransformToolIntegrationTest { setToolPosition(toolPosition.x - 1, toolPosition.y) TopBarViewInteraction.onTopBarView() .performClickCheckmark() - var height: Int = layerModel.currentLayer!!.bitmap!!.height + var height: Int = layerModel.currentLayer!!.bitmap.height pixels = IntArray(height) - layerModel.currentLayer!!.bitmap!!.getPixels(pixels, 0, 1, 0, 0, 1, height) + layerModel.currentLayer!!.bitmap.getPixels(pixels, 0, 1, 0, 0, 1, height) for (pixel in pixels) { assertEquals(Color.TRANSPARENT.toLong(), pixel.toLong()) } setToolPosition(toolPosition.x + 1, toolPosition.y) TopBarViewInteraction.onTopBarView() .performClickCheckmark() - var width: Int = layerModel.currentLayer!!.bitmap!!.width - height = layerModel.currentLayer!!.bitmap!!.height + var width: Int = layerModel.currentLayer!!.bitmap.width + height = layerModel.currentLayer!!.bitmap.height pixels = IntArray(height) - layerModel.currentLayer!!.bitmap!!.getPixels(pixels, 0, 1, width - 1, 0, 1, height) + layerModel.currentLayer!!.bitmap.getPixels(pixels, 0, 1, width - 1, 0, 1, height) for (pixel in pixels) { assertEquals(Color.TRANSPARENT.toLong(), pixel.toLong()) } setToolPosition(toolPosition.x, toolPosition.y - 1) TopBarViewInteraction.onTopBarView() .performClickCheckmark() - width = layerModel.currentLayer!!.bitmap!!.width + width = layerModel.currentLayer!!.bitmap.width pixels = IntArray(width) - layerModel.currentLayer!!.bitmap!!.getPixels(pixels, 0, width, 0, 0, width, 1) + layerModel.currentLayer!!.bitmap.getPixels(pixels, 0, width, 0, 0, width, 1) for (pixel in pixels) { assertEquals(Color.TRANSPARENT.toLong(), pixel.toLong()) } setToolPosition(toolPosition.x, toolPosition.y + 1) TopBarViewInteraction.onTopBarView() .performClickCheckmark() - width = layerModel.currentLayer!!.bitmap!!.width - height = layerModel.currentLayer!!.bitmap!!.height + width = layerModel.currentLayer!!.bitmap.width + height = layerModel.currentLayer!!.bitmap.height pixels = IntArray(width) - layerModel.currentLayer!!.bitmap!!.getPixels(pixels, 0, width, 0, height - 1, width, 1) + layerModel.currentLayer!!.bitmap.getPixels(pixels, 0, width, 0, height - 1, width, 1) for (pixel in pixels) { assertEquals(Color.TRANSPARENT.toLong(), pixel.toLong()) } @@ -1216,7 +1216,7 @@ class TransformToolIntegrationTest { @Test fun testTransformToolSetCenterCloseCenter() { - drawPlus(layerModel.currentLayer!!.bitmap!!, initialWidth / 2) + drawPlus(layerModel.currentLayer!!.bitmap, initialWidth / 2) onToolBarView() .performSelectTool(ToolType.TRANSFORM) @@ -1235,7 +1235,7 @@ class TransformToolIntegrationTest { @Test fun testTransformToolSetCenterFarCenter() { - drawPlus(layerModel.currentLayer!!.bitmap!!, initialWidth / 2) + drawPlus(layerModel.currentLayer!!.bitmap, initialWidth / 2) onToolBarView() .performSelectTool(ToolType.HAND) @@ -1266,7 +1266,7 @@ class TransformToolIntegrationTest { @Test fun testClickingOnCheckmarkDoesNotResetZoomOrPlacement() { - drawPlus(layerModel.currentLayer!!.bitmap!!, initialWidth / 2) + drawPlus(layerModel.currentLayer!!.bitmap, initialWidth / 2) perspective.translate(100f, 100f) perspective.multiplyScale(0.1f) val oldScale = perspective.scale @@ -1281,9 +1281,13 @@ class TransformToolIntegrationTest { .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.BOTTOM_RIGHT_CORNER)) runBlocking { TopBarViewInteraction.onTopBarView().performClickCheckmark() + delay(1000) } assertEquals(oldScale, perspective.scale) assertEquals(oldTranslationY, perspective.surfaceTranslationY) + runBlocking { + delay(1000) + } assertEquals(oldTranslationX, perspective.surfaceTranslationX) } diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/WatercolorToolIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/WatercolorToolIntegrationTest.kt index 5529a5d804..8c40407116 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/WatercolorToolIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/WatercolorToolIntegrationTest.kt @@ -34,7 +34,7 @@ import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider import org.catrobat.paintroid.test.espresso.util.UiInteractions import org.catrobat.paintroid.test.espresso.util.wrappers.BottomNavigationViewInteraction -import org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerViewInteraction.onColorPickerView +import org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerViewInteraction.Companion.onColorPickerView import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/BitmapLocationProvider.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/BitmapLocationProvider.java deleted file mode 100644 index 332d0693b7..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/BitmapLocationProvider.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2022 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.util; - -import android.view.View; - -import org.catrobat.paintroid.MainActivity; -import org.catrobat.paintroid.tools.Workspace; - -import androidx.test.espresso.action.CoordinatesProvider; - -import static org.catrobat.paintroid.test.espresso.util.MainActivityHelper.getMainActivityFromView; - -public enum BitmapLocationProvider implements CoordinatesProvider{ - MIDDLE { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .5f, .5f); - } - }, - MIDDLE_RIGHT { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, 1f, .5f); - } - }, - OUTSIDE_MIDDLE_RIGHT { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, 1.5f, .5f); - } - }, - HALFWAY_RIGHT_MIDDLE { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .75f, .5f); - } - }, - HALFWAY_LEFT_MIDDLE { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .25f, .5f); - } - }, - HALFWAY_BOTTOM_MIDDLE { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .5f, .75f); - } - }, - HALFWAY_TOP_MIDDLE { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .5f, .25f); - } - }, - HALFWAY_TOP_LEFT { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .25f, .25f); - } - }, - HALFWAY_BOTTOM_RIGHT { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .75f, .75f); - } - }, - HALFWAY_BOTTOM_LEFT { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .25f, .75f); - } - }; - - private static float[] calculatePercentageOffset(View view, float percentageX, float percentageY) { - MainActivity mainActivity = getMainActivityFromView(view); - Workspace workspace = mainActivity.workspace; - float pointX = (workspace.getWidth() - 1) * percentageX; - float pointY = workspace.getHeight() * percentageY; - return new float[] {pointX, pointY}; - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/BitmapLocationProvider.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/BitmapLocationProvider.kt new file mode 100644 index 0000000000..4b4dfc98c2 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/BitmapLocationProvider.kt @@ -0,0 +1,71 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.util + +import android.view.View +import androidx.test.espresso.action.CoordinatesProvider +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.test.espresso.util.MainActivityHelper.Companion.getMainActivityFromView + +enum class BitmapLocationProvider : CoordinatesProvider { + MIDDLE { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .5f, .5f) + }, + MIDDLE_RIGHT { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, 1f, .5f) + }, + OUTSIDE_MIDDLE_RIGHT { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, 1.5f, .5f) + }, + HALFWAY_RIGHT_MIDDLE { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .75f, .5f) + }, + HALFWAY_LEFT_MIDDLE { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .25f, .5f) + }, + HALFWAY_BOTTOM_MIDDLE { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .5f, .75f) + }, + HALFWAY_TOP_MIDDLE { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .5f, .25f) + }, + HALFWAY_TOP_LEFT { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .25f, .25f) + }, + HALFWAY_BOTTOM_RIGHT { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .75f, .75f) + }, + HALFWAY_BOTTOM_LEFT { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .25f, .75f) + }; + + companion object { + private fun calculatePercentageOffset( + view: View, + percentageX: Float, + percentageY: Float + ): FloatArray { + val mainActivity: MainActivity = getMainActivityFromView(view) + val workspace = mainActivity.workspace + val pointX = (workspace.width - 1) * percentageX + val pointY = workspace.height * percentageY + return floatArrayOf(pointX, pointY) + } + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/CustomSwiper.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/CustomSwiper.java deleted file mode 100644 index ed47dc3029..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/CustomSwiper.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2015 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.util; - -import android.os.SystemClock; -import android.view.MotionEvent; - -import androidx.test.espresso.UiController; -import androidx.test.espresso.action.MotionEvents; -import androidx.test.espresso.action.Swiper; - -public enum CustomSwiper implements Swiper { - ACCURATE { - @Override - public Status sendSwipe(UiController uiController, float[] startCoordinates, float[] endCoordinates, float[] precision) { - final float[][] steps = interpolate(startCoordinates, endCoordinates, STEPS); - final int delayBetweenMovements = DURATION / steps.length; - - MotionEvent downEvent = MotionEvents.sendDown(uiController, startCoordinates, precision).down; - try { - for (int i = 0; i < steps.length; i++) { - MotionEvents.sendMovement(uiController, downEvent, steps[i]); - long desiredTime = downEvent.getDownTime() + delayBetweenMovements * i; - long timeUntilDesired = desiredTime - SystemClock.uptimeMillis(); - if (timeUntilDesired > 10) { - uiController.loopMainThreadForAtLeast(timeUntilDesired); - } - } - MotionEvents.sendUp(uiController, downEvent, endCoordinates); - } finally { - downEvent.recycle(); - } - return Status.SUCCESS; - } - }; - - private static final int STEPS = 10; - private static final int DURATION = 500; - - private static float[][] interpolate(float[] start, float[] end, int steps) { - float[][] res = new float[steps][2]; - - res[0][0] = start[0]; - res[0][1] = start[1]; - - for (int i = 2; i < steps; i++) { - res[i - 1][0] = start[0] + (end[0] - start[0]) * i / (steps + 2f); - res[i - 1][1] = start[1] + (end[1] - start[1]) * i / (steps + 2f); - } - - res[steps - 1][0] = end[0]; - res[steps - 1][1] = end[1]; - - return res; - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/CustomSwiper.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/CustomSwiper.kt new file mode 100644 index 0000000000..85a3e23e00 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/CustomSwiper.kt @@ -0,0 +1,65 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.util + +import android.os.SystemClock +import androidx.test.espresso.UiController +import androidx.test.espresso.action.MotionEvents +import androidx.test.espresso.action.Swiper + +enum class CustomSwiper : Swiper { + ACCURATE { + override fun sendSwipe(uiController: UiController, startCoordinates: FloatArray, endCoordinates: FloatArray, precision: FloatArray): Swiper.Status { + val steps = interpolate(startCoordinates, endCoordinates, STEPS) + val delayBetweenMovements = DURATION / steps.size + val downEvent = MotionEvents.sendDown(uiController, startCoordinates, precision).down + try { + for (i in steps.indices) { + MotionEvents.sendMovement(uiController, downEvent, steps[i]) + val desiredTime = downEvent.downTime + delayBetweenMovements * i + val timeUntilDesired = desiredTime - SystemClock.uptimeMillis() + if (timeUntilDesired > 10) { + uiController.loopMainThreadForAtLeast(timeUntilDesired) + } + } + MotionEvents.sendUp(uiController, downEvent, endCoordinates) + } finally { + downEvent.recycle() + } + return Swiper.Status.SUCCESS + } + }; + + companion object { + private const val STEPS = 10 + private const val DURATION = 500 + private fun interpolate(start: FloatArray, end: FloatArray, steps: Int): Array { + val res = Array(steps) { FloatArray(2) } + res[0][0] = start[0] + res[0][1] = start[1] + for (i in 2 until steps) { + res[i - 1][0] = start[0] + (end[0] - start[0]) * i / (steps + 2f) + res[i - 1][1] = start[1] + (end[1] - start[1]) * i / (steps + 2f) + } + res[steps - 1][0] = end[0] + res[steps - 1][1] = end[1] + return res + } + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/DrawingSurfaceLocationProvider.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/DrawingSurfaceLocationProvider.java deleted file mode 100644 index 30700ad939..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/DrawingSurfaceLocationProvider.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2015 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.util; - -import android.graphics.PointF; -import android.view.View; - -import org.catrobat.paintroid.MainActivity; -import org.catrobat.paintroid.tools.Workspace; -import org.catrobat.paintroid.tools.implementation.BaseToolWithShape; - -import androidx.test.espresso.action.CoordinatesProvider; - -import static org.catrobat.paintroid.test.espresso.util.MainActivityHelper.getMainActivityFromView; -import static org.catrobat.paintroid.test.espresso.util.PositionCoordinatesProvider.calculateViewOffset; - -public enum DrawingSurfaceLocationProvider implements CoordinatesProvider { - MIDDLE { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .5f, .5f); - } - }, - LEFT_MIDDLE { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .0f, .5f); - } - }, - HALFWAY_LEFT_MIDDLE { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .25f, .5f); - } - }, - RIGHT_MIDDLE { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, 1f, .5f); - } - }, - HALFWAY_RIGHT_MIDDLE { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .75f, .5f); - } - }, - TOP_MIDDLE { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .5f, 0f); - } - }, - HALFWAY_TOP_MIDDLE { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .5f, .25f); - } - }, - HALFWAY_BOTTOM_MIDDLE { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .5f, .75f); - } - }, - HALFWAY_TOP_LEFT { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .25f, .25f); - } - }, - HALFWAY_TOP_RIGHT { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .75f, .25f); - } - }, - HALFWAY_BOTTOM_LEFT { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .25f, .75f); - } - }, - HALFWAY_BOTTOM_RIGHT { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .75f, .75f); - } - }, - BOTTOM_RIGHT_CLOSE_CENTER { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .55f, .55f); - } - }, - BOTTOM_RIGHT_CORNER { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, 0.95f, .9f); - } - }, - BOTTOM_MIDDLE { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .5f, 0.9f); - } - }, - OUTSIDE_MIDDLE_RIGHT { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, 1.5f, .5f); - } - }, - OUTSIDE_MIDDLE_LEFT { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, -.3f, .5f); - } - }, - OUTSIDE_MIDDLE_BOTTOM { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, 1.3f, .5f); - } - }, - OUTSIDE_MIDDLE_TOP { - @Override - public float[] calculateCoordinates(View view) { - return calculatePercentageOffset(view, .5f, -.3f); - } - }, - TOOL_POSITION { - @Override - public float[] calculateCoordinates(View view) { - MainActivity mainActivity = getMainActivityFromView(view); - Workspace workspace = mainActivity.workspace; - PointF toolPosition = ((BaseToolWithShape) mainActivity.toolReference.getTool()).toolPosition; - PointF point = workspace.getSurfacePointFromCanvasPoint(toolPosition); - return calculateViewOffset(view, point.x, point.y); - } - }; - - private static float[] calculatePercentageOffset(View view, float percentageX, float percentageY) { - MainActivity mainActivity = getMainActivityFromView(view); - Workspace workspace = mainActivity.workspace; - float pointX = workspace.getWidth() * percentageX; - float pointY = workspace.getHeight() * percentageY; - PointF point = workspace.getSurfacePointFromCanvasPoint(new PointF(pointX, pointY)); - return calculateViewOffset(view, point.x, point.y); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/DrawingSurfaceLocationProvider.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/DrawingSurfaceLocationProvider.kt new file mode 100644 index 0000000000..81d8a053ab --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/DrawingSurfaceLocationProvider.kt @@ -0,0 +1,109 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.util + +import android.graphics.PointF +import android.view.View +import androidx.test.espresso.action.CoordinatesProvider +import org.catrobat.paintroid.tools.implementation.BaseToolWithShape + +enum class DrawingSurfaceLocationProvider : CoordinatesProvider { + MIDDLE { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .5f, .5f) + }, + LEFT_MIDDLE { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .0f, .5f) + }, + HALFWAY_LEFT_MIDDLE { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .25f, .5f) + }, + RIGHT_MIDDLE { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, 1f, .5f) + }, + HALFWAY_RIGHT_MIDDLE { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .75f, .5f) + }, + TOP_MIDDLE { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .5f, 0f) + }, + HALFWAY_TOP_MIDDLE { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .5f, .25f) + }, + HALFWAY_BOTTOM_MIDDLE { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .5f, .75f) + }, + HALFWAY_TOP_LEFT { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .25f, .25f) + }, + HALFWAY_TOP_RIGHT { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .75f, .25f) + }, + HALFWAY_BOTTOM_LEFT { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .25f, .75f) + }, + HALFWAY_BOTTOM_RIGHT { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .75f, .75f) + }, + BOTTOM_RIGHT_CLOSE_CENTER { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .55f, .55f) + }, + BOTTOM_RIGHT_CORNER { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, 0.95f, .9f) + }, + BOTTOM_MIDDLE { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .5f, 0.9f) + }, + OUTSIDE_MIDDLE_RIGHT { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, 1.5f, .5f) + }, + OUTSIDE_MIDDLE_LEFT { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, -.3f, .5f) + }, + OUTSIDE_MIDDLE_BOTTOM { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, 1.3f, .5f) + }, + OUTSIDE_MIDDLE_TOP { + override fun calculateCoordinates(view: View): FloatArray = calculatePercentageOffset(view, .5f, -.3f) + }, + TOOL_POSITION { + override fun calculateCoordinates(view: View): FloatArray { + val mainActivity = MainActivityHelper.getMainActivityFromView(view) + val workspace = mainActivity.workspace + val toolPosition = + (mainActivity.toolReference.tool as BaseToolWithShape?)!!.toolPosition + val point = workspace.getSurfacePointFromCanvasPoint(toolPosition) + return PositionCoordinatesProvider.calculateViewOffset(view, point.x, point.y) + } + }; + + companion object { + private fun calculatePercentageOffset( + view: View, + percentageX: Float, + percentageY: Float + ): FloatArray { + val mainActivity = MainActivityHelper.getMainActivityFromView(view) + val workspace = mainActivity.workspace + val pointX = workspace.width * percentageX + val pointY = workspace.height * percentageY + val point = workspace.getSurfacePointFromCanvasPoint(PointF(pointX, pointY)) + return PositionCoordinatesProvider.calculateViewOffset(view, point.x, point.y) + } + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/EspressoUtils.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/EspressoUtils.kt index effec27e2c..f40903af83 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/EspressoUtils.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/EspressoUtils.kt @@ -84,7 +84,7 @@ object EspressoUtils { @SuppressWarnings("SwallowedException") fun waitForToast(viewMatcher: Matcher?, duration: Int) { val waitTime = System.currentTimeMillis() + duration - val viewInteraction = Espresso.onView(viewMatcher).inRoot(UiMatcher.isToast()) + val viewInteraction = Espresso.onView(viewMatcher).inRoot(UiMatcher.isToast) while (System.currentTimeMillis() < waitTime) { try { viewInteraction.check(ViewAssertions.matches(ViewMatchers.isDisplayed())) diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/LanguageSupport.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/LanguageSupport.kt similarity index 53% rename from Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/LanguageSupport.java rename to Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/LanguageSupport.kt index ca2466dcf8..03d2d9bac3 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/LanguageSupport.java +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/LanguageSupport.kt @@ -16,31 +16,24 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ +package org.catrobat.paintroid.test.espresso.util -package org.catrobat.paintroid.test.espresso.util; +import android.content.Context +import android.os.Build +import java.util.* -import android.content.Context; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.os.Build; - -import java.util.Locale; - -public final class LanguageSupport { - private LanguageSupport() { - throw new IllegalArgumentException(); - } - - public static void setLocale(Context context, Locale locale) { - if (Build.VERSION.SDK_INT >= 24) { - Locale.setDefault(Locale.Category.DISPLAY, locale); - } else { - Locale.setDefault(locale); - } - - Resources resources = context.getResources(); - Configuration conf = resources.getConfiguration(); - conf.setLocale(locale); - resources.updateConfiguration(conf, resources.getDisplayMetrics()); - } +class LanguageSupport private constructor() { + companion object { + fun setLocale(context: Context, locale: Locale?) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + Locale.setDefault(Locale.Category.DISPLAY, locale) + } else { + Locale.setDefault(locale) + } + val resources = context.resources + val conf = resources.configuration + conf.setLocale(locale) + resources.updateConfiguration(conf, resources.displayMetrics) + } + } } diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/MainActivityHelper.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/MainActivityHelper.java deleted file mode 100644 index 6b6d445b40..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/MainActivityHelper.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2015 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.util; - -import android.content.Context; -import android.content.ContextWrapper; -import android.graphics.Insets; -import android.graphics.Point; -import android.graphics.Rect; -import android.os.Build; -import android.view.View; -import android.view.WindowInsets; -import android.view.WindowManager; -import android.view.WindowMetrics; - -import org.catrobat.paintroid.MainActivity; - -public class MainActivityHelper { - private final MainActivity activity; - - public MainActivityHelper(MainActivity activity) { - this.activity = activity; - } - - public Point getDisplaySize() { - Point displaySize = new Point(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - WindowManager windowManager = activity.getWindowManager(); - WindowMetrics windowMetrics = windowManager.getCurrentWindowMetrics(); - Insets windowInsets = windowMetrics.getWindowInsets().getInsetsIgnoringVisibility( - WindowInsets.Type.navigationBars() | WindowInsets.Type.displayCutout() - ); - float insetsWidth = windowInsets.right + windowInsets.left; - float insetsHeight = windowInsets.top + windowInsets.bottom; - Rect b = windowMetrics.getBounds(); - float width = b.width() - insetsWidth; - float height = b.height() - insetsHeight; - displaySize.x = (int) width; - displaySize.y = (int) height; - } else { - activity.getWindowManager().getDefaultDisplay().getSize(displaySize); - } - return displaySize; - } - - public int getDisplayWidth() { - return getDisplaySize().x; - } - - public int getDisplayHeight() { - return getDisplaySize().y; - } - - public int getScreenOrientation() { - return activity.getRequestedOrientation(); - } - - public void setScreenOrientation(int orientation) { - activity.setRequestedOrientation(orientation); - } - - public static MainActivity getMainActivityFromView(View view) { - Context context = view.getContext(); - while (context instanceof ContextWrapper) { - if (context instanceof MainActivity) { - return (MainActivity) context; - } - context = ((ContextWrapper) context).getBaseContext(); - } - throw new RuntimeException("View context does not implement MainActivity"); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/MainActivityHelper.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/MainActivityHelper.kt new file mode 100644 index 0000000000..4b4d867f5c --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/MainActivityHelper.kt @@ -0,0 +1,74 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.util + +import android.content.ContextWrapper +import android.graphics.Point +import android.os.Build +import android.view.View +import android.view.WindowInsets +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.test.espresso.util.wrappers.MainActivityImplementationExeption + +data class MainActivityHelper(private val activity: MainActivity) { + + val displaySize: Point + get() { + val displaySize = Point() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + val windowManager = activity.windowManager + val windowMetrics = windowManager.currentWindowMetrics + val windowInsets = windowMetrics.windowInsets.getInsetsIgnoringVisibility( + WindowInsets.Type.navigationBars() or WindowInsets.Type.displayCutout() + ) + val insetsWidth = (windowInsets.right + windowInsets.left).toFloat() + val insetsHeight = (windowInsets.top + windowInsets.bottom).toFloat() + val b = windowMetrics.bounds + val width = b.width() - insetsWidth + val height = b.height() - insetsHeight + displaySize.x = width.toInt() + displaySize.y = height.toInt() + } else { + activity.windowManager.defaultDisplay.getSize(displaySize) + } + return displaySize + } + val displayWidth: Int + get() = displaySize.x + val displayHeight: Int + get() = displaySize.y + var screenOrientation: Int + get() = activity.requestedOrientation + set(orientation) { + activity.requestedOrientation = orientation + } + + companion object { + fun getMainActivityFromView(view: View): MainActivity { + var context = view.context + while (context is ContextWrapper) { + if (context is MainActivity) { + return context + } + context = context.baseContext + } + throw MainActivityImplementationExeption("View context does not implement MainActivity") + } + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/OffsetLocationProvider.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/OffsetLocationProvider.java deleted file mode 100644 index 9eeb1be567..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/OffsetLocationProvider.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2022 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.util; - -import android.view.View; - -import androidx.test.espresso.action.CoordinatesProvider; - -public class OffsetLocationProvider implements CoordinatesProvider { - private final CoordinatesProvider locationProvider; - private final int xOffset; - private final int yOffset; - - public OffsetLocationProvider(CoordinatesProvider locationProvider, int xOffset, int yOffset) { - - this.locationProvider = locationProvider; - this.xOffset = xOffset; - this.yOffset = yOffset; - } - - public static CoordinatesProvider withOffset(CoordinatesProvider locationProvider, int xOffset, int yOffset) { - return new OffsetLocationProvider(locationProvider, xOffset, yOffset); - } - - @Override - public float[] calculateCoordinates(View view) { - float[] coordinates = locationProvider.calculateCoordinates(view); - coordinates[0] += xOffset; - coordinates[1] += yOffset; - return coordinates; - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/util/RtlUiTestUtils.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/OffsetLocationProvider.kt similarity index 52% rename from Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/util/RtlUiTestUtils.java rename to Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/OffsetLocationProvider.kt index 39acffbd48..e0db57740a 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/util/RtlUiTestUtils.java +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/OffsetLocationProvider.kt @@ -1,4 +1,4 @@ -/** +/* * Paintroid: An image manipulation application for Android. * Copyright (C) 2010-2022 The Catrobat Team * () @@ -16,16 +16,21 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ +package org.catrobat.paintroid.test.espresso.util -package org.catrobat.paintroid.test.espresso.rtl.util; +import android.view.View +import androidx.test.espresso.action.CoordinatesProvider -public final class RtlUiTestUtils { - private RtlUiTestUtils() { - throw new AssertionError(); - } +class OffsetLocationProvider(private val locationProvider: CoordinatesProvider, private val xOffset: Int, private val yOffset: Int) : CoordinatesProvider { + override fun calculateCoordinates(view: View): FloatArray { + val coordinates = locationProvider.calculateCoordinates(view) + coordinates[0] += xOffset.toFloat() + coordinates[1] += yOffset.toFloat() + return coordinates + } - public static boolean checkTextDirection(String string) { - return Character.getDirectionality(string.charAt(0)) == Character.DIRECTIONALITY_RIGHT_TO_LEFT - || Character.getDirectionality(string.charAt(0)) == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC; - } + companion object { + @JvmStatic + fun withOffset(locationProvider: CoordinatesProvider, xOffset: Int, yOffset: Int): CoordinatesProvider = OffsetLocationProvider(locationProvider, xOffset, yOffset) + } } diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/PositionCoordinatesProvider.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/PositionCoordinatesProvider.java deleted file mode 100644 index bbc957eb85..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/PositionCoordinatesProvider.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2015 The Catrobat Team - * () - *

- * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - *

- * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - *

- * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.util; - -import android.view.View; - -import androidx.test.espresso.action.CoordinatesProvider; - -public class PositionCoordinatesProvider implements CoordinatesProvider { - private final float xCoordinate; - private final float yCoordinate; - - public PositionCoordinatesProvider(float x, float y) { - this.xCoordinate = x; - this.yCoordinate = y; - } - - public static CoordinatesProvider at(float x, float y) { - return new PositionCoordinatesProvider(x, y); - } - - @Override - public float[] calculateCoordinates(View view) { - return calculateViewOffset(view, xCoordinate, yCoordinate); - } - - public static float[] calculateViewOffset(View view, float x, float y) { - final int[] screenLocation = new int[2]; - view.getLocationOnScreen(screenLocation); - - final float touchX = screenLocation[0] + x; - final float touchY = screenLocation[1] + y; - return new float[] {touchX, touchY}; - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/PositionCoordinatesProvider.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/PositionCoordinatesProvider.kt new file mode 100644 index 0000000000..f687395783 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/PositionCoordinatesProvider.kt @@ -0,0 +1,42 @@ +/** + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * (//developer.catrobat.org/credits>) + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see //www.gnu.org/licenses/>. + */ +package org.catrobat.paintroid.test.espresso.util + +import android.view.View +import androidx.test.espresso.action.CoordinatesProvider + +class PositionCoordinatesProvider(private val xCoordinate: Float, private val yCoordinate: Float) : CoordinatesProvider { + override fun calculateCoordinates(view: View): FloatArray = calculateViewOffset(view, xCoordinate, yCoordinate) + + companion object { + @JvmStatic + fun at(x: Float, y: Float): CoordinatesProvider = PositionCoordinatesProvider(x, y) + @JvmStatic + fun calculateViewOffset(view: View, x: Float, y: Float): FloatArray { + val screenLocation = IntArray(2) + view.getLocationOnScreen(screenLocation) + val touchX = screenLocation[0] + x + val touchY = screenLocation[1] + y + return floatArrayOf(touchX, touchY) + } + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiInteractions.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiInteractions.java deleted file mode 100644 index 60bece8b8b..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiInteractions.java +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2015 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.util; - -import android.graphics.PointF; -import android.util.Log; -import android.view.InputDevice; -import android.view.MotionEvent; -import android.view.View; -import android.widget.ProgressBar; -import android.widget.SeekBar; - -import org.hamcrest.Matcher; - -import static org.catrobat.paintroid.test.espresso.util.CustomSwiper.ACCURATE; -import static org.hamcrest.Matchers.is; - -import java.lang.reflect.Method; - -import androidx.test.espresso.UiController; -import androidx.test.espresso.ViewAction; -import androidx.test.espresso.ViewAssertion; -import androidx.test.espresso.action.CoordinatesProvider; -import androidx.test.espresso.action.GeneralClickAction; -import androidx.test.espresso.action.GeneralLocation; -import androidx.test.espresso.action.GeneralSwipeAction; -import androidx.test.espresso.action.MotionEvents; -import androidx.test.espresso.action.Press; -import androidx.test.espresso.action.ScrollToAction; -import androidx.test.espresso.action.Swipe; -import androidx.test.espresso.action.Tap; -import androidx.test.espresso.action.Tapper; -import androidx.test.espresso.matcher.ViewMatchers; -import androidx.viewpager.widget.ViewPager; - -import static androidx.test.espresso.action.ViewActions.actionWithAssertions; -import static androidx.test.espresso.matcher.ViewMatchers.assertThat; -import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast; -import static androidx.test.espresso.matcher.ViewMatchers.isRoot; -import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; - -public final class UiInteractions { - - private UiInteractions() { - } - - public static ViewAction unconstrainedScrollTo() { - return actionWithAssertions(new UnconstrainedScrollToAction()); - } - - public static ViewAction waitFor(final long millis) { - return new ViewAction() { - @Override - public Matcher getConstraints() { - return isRoot(); - } - - @Override - public String getDescription() { - return "Wait for " + millis + " milliseconds."; - } - - @Override - public void perform(UiController uiController, final View view) { - uiController.loopMainThreadForAtLeast(millis); - } - }; - } - - public static ViewAssertion assertRecyclerViewCount(final int expectedCount) { - return (view, noViewFoundException) -> { - if (noViewFoundException != null) { - throw noViewFoundException; - } - - org.catrobat.paintroid.ui.LayerAdapter adapter = (org.catrobat.paintroid.ui.LayerAdapter) ((androidx.recyclerview.widget.RecyclerView) view).getAdapter(); - if (adapter != null) { - assertThat(adapter.getItemCount(), is(expectedCount)); - } - }; - } - - public static ViewAction setProgress(final int progress) { - return new ViewAction() { - - @Override - public Matcher getConstraints() { - return isAssignableFrom(SeekBar.class); - } - - @Override - public String getDescription() { - return "Set a progress"; - } - - @Override - public void perform(UiController uiController, View view) { - try { - Method privateSetProgressMethod = ProgressBar.class.getDeclaredMethod("setProgressInternal", Integer.TYPE, Boolean.TYPE, Boolean.TYPE); - privateSetProgressMethod.setAccessible(true); - privateSetProgressMethod.invoke(view, progress, true, true); - } catch (ReflectiveOperationException e) { - Log.e("SET PROGRESS", "could not set progress"); - } - } - }; - } - - public static ViewAction clickOutside(final Direction direction) { - return actionWithAssertions( - new GeneralClickAction(Tap.SINGLE, view -> { - android.graphics.Rect r = new android.graphics.Rect(); - view.getGlobalVisibleRect(r); - switch (direction) { - case ABOVE: - return new float[]{r.centerX(), r.top - 50}; - case BELOW: - return new float[]{r.centerX(), r.bottom + 50}; - case LEFT: - return new float[]{r.left - 50, r.centerY()}; - case RIGHT: - return new float[]{r.right + 50, r.centerY()}; - } - return null; - }, Press.FINGER, 0, 1) - ); - } - - public static ViewAction touchAt(final CoordinatesProvider provider) { - return touchAt(provider, Tap.SINGLE); - } - - public static ViewAction touchAt(final PointF coordinates) { - return touchAt(coordinates, Tap.SINGLE); - } - - public static ViewAction touchAt(final int x, final int y) { - return touchAt((float) x, (float) y); - } - - public static ViewAction touchAt(final float x, final float y) { - return touchAt(x, y, Tap.SINGLE); - } - - public static ViewAction touchLongAt(final CoordinatesProvider provider) { - return touchAt(provider, Tap.LONG); - } - - public static ViewAction touchLongAt(final PointF coordinates) { - return touchAt(coordinates, Tap.LONG); - } - - public static ViewAction touchLongAt(final float x, final float y) { - return touchAt(x, y, Tap.LONG); - } - - public static ViewAction touchAt(final CoordinatesProvider provider, final Tapper tapStyle) { - return actionWithAssertions( - new GeneralClickAction(tapStyle, provider, Press.FINGER, 0, 0)); - } - - public static ViewAction touchAt(final PointF coordinates, final Tapper tapStyle) { - return touchAt(coordinates.x, coordinates.y, tapStyle); - } - - public static ViewAction touchAt(final float x, final float y, final Tapper tapStyle) { - return actionWithAssertions( - new GeneralClickAction(tapStyle, PositionCoordinatesProvider.at(x, y), Press.FINGER, 0, 0) - ); - } - - public static ViewAction touchCenterLeft() { - return new GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER_LEFT, Press.FINGER, 0, 0); - } - - public static ViewAction touchCenterTop() { - return new GeneralClickAction(Tap.SINGLE, GeneralLocation.TOP_CENTER, Press.FINGER, 0, 0); - } - - public static ViewAction touchCenterBottom() { - return new GeneralClickAction(Tap.SINGLE, GeneralLocation.BOTTOM_CENTER, Press.FINGER, 0, 0); - } - - public static ViewAction touchCenterMiddle() { - return new GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER, Press.FINGER, 0, 0); - } - - public static ViewAction touchCenterRight() { - return new GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER_RIGHT, Press.FINGER, 0, 0); - } - - public static ViewAction swipe(PointF start, PointF end) { - return swipe((int) start.x, (int) start.y, (int) end.x, (int) end.y); - } - - public static ViewAction swipe(float startX, float startY, float endX, float endY) { - return swipe((int) startX, (int) startY, (int) endX, (int) endY); - } - - public static ViewAction swipe(int startX, int startY, int endX, int endY) { - return swipe(PositionCoordinatesProvider.at(startX, startY), PositionCoordinatesProvider.at(endX, endY)); - } - - public static ViewAction swipe(CoordinatesProvider startCoordinatesProvider, CoordinatesProvider endCoordinatesProvider) { - return new GeneralSwipeAction(Swipe.FAST, startCoordinatesProvider, endCoordinatesProvider, Press.FINGER); - } - - public static ViewAction swipeAccurate(CoordinatesProvider startCoordinatesProvider, CoordinatesProvider endCoordinatesProvider) { - return new GeneralSwipeAction(ACCURATE, startCoordinatesProvider, endCoordinatesProvider, Press.FINGER); - } - - public static ViewAction selectViewPagerPage(final int pos) { - return new ViewAction() { - @Override - public Matcher getConstraints() { - return isAssignableFrom(ViewPager.class); - } - - @Override - public String getDescription() { - return "select page in ViewPager"; - } - - @Override - public void perform(UiController uiController, View view) { - ((ViewPager) view).setCurrentItem(pos); - } - }; - } - - public enum Direction { - ABOVE, - BELOW, - LEFT, - RIGHT - } - - private static class UnconstrainedScrollToAction implements ViewAction { - private final ViewAction action = new ScrollToAction(); - - @Override - public Matcher getConstraints() { - return withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE); - } - - @Override - public String getDescription() { - return action.getDescription(); - } - - @Override - public void perform(UiController uiController, View view) { - action.perform(uiController, view); - } - } - - public static class DefinedLongTap implements Tapper { - - private final int longPressTimeout; - - DefinedLongTap(int longPressTimeout) { - this.longPressTimeout = longPressTimeout; - } - - public static Tapper withPressTimeout(final int longPressTimeout) { - return new DefinedLongTap(longPressTimeout); - } - - @Override - public Status sendTap( - UiController uiController, float[] coordinates, float[] precision, int inputDevice, - int buttonState) { - MotionEvent downEvent = MotionEvents.sendDown(uiController, coordinates, precision, - inputDevice, buttonState).down; - try { - // Duration before a press turns into a long press. - // Factor 1.5 is needed, otherwise a long press is not safely detected. - // See android.test.TouchUtils longClickView - long longPressTimeout = (long) (this.longPressTimeout * 1.5f); - uiController.loopMainThreadForAtLeast(longPressTimeout); - - if (!MotionEvents.sendUp(uiController, downEvent)) { - MotionEvents.sendCancel(uiController, downEvent); - return Status.FAILURE; - } - } finally { - downEvent.recycle(); - } - return Status.SUCCESS; - } - - /** - * @deprecated use other sendTap instead - */ - @Deprecated - @Override - public Status sendTap(UiController uiController, float[] coordinates, float[] precision) { - return sendTap(uiController, coordinates, precision, InputDevice.SOURCE_UNKNOWN, - MotionEvent.BUTTON_PRIMARY); - } - } - - public static class PressAndReleaseActions { - - static MotionEvent motionEvent = null; - - public static PressAction pressAction(DrawingSurfaceLocationProvider coordinates) { - return new PressAction(coordinates); - } - - public static ReleaseAction releaseAction() { - return new ReleaseAction(); - } - - public static void tearDownPressAndRelease() { - motionEvent = null; - } - - public static class PressAction implements ViewAction { - - DrawingSurfaceLocationProvider coordinates; - - PressAction(DrawingSurfaceLocationProvider coords) { - this.coordinates = coords; - } - - @Override - public Matcher getConstraints() { - return isDisplayingAtLeast(90); - } - - @Override - public String getDescription() { - return "Press Action"; - } - - @Override - public void perform(UiController uiController, View view) { - if (motionEvent != null) { - throw new AssertionError("Only one view can be held at a time"); - } - float[] coords = coordinates.calculateCoordinates(view); - float[] precision = Press.FINGER.describePrecision(); - - motionEvent = MotionEvents.sendDown(uiController, coords, precision).down; - } - } - - public static class ReleaseAction implements ViewAction { - - @Override - public Matcher getConstraints() { - return isDisplayingAtLeast(90); - } - - @Override - public String getDescription() { - return "Release"; - } - - @Override - public void perform(UiController uiController, View view) { - if (motionEvent == null) { - throw new AssertionError("Only one view can be held at a time"); - } - MotionEvents.sendUp(uiController, motionEvent); - } - } - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiInteractions.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiInteractions.kt new file mode 100644 index 0000000000..4a19745683 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiInteractions.kt @@ -0,0 +1,333 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.util + +import android.graphics.PointF +import android.graphics.Rect +import android.util.Log +import android.view.InputDevice +import android.view.MotionEvent +import android.view.View +import android.widget.ProgressBar +import android.widget.SeekBar +import androidx.recyclerview.widget.RecyclerView +import androidx.test.espresso.NoMatchingViewException +import androidx.test.espresso.UiController +import androidx.test.espresso.ViewAction +import androidx.test.espresso.ViewAssertion +import androidx.test.espresso.action.CoordinatesProvider +import androidx.test.espresso.action.GeneralClickAction +import androidx.test.espresso.action.GeneralLocation +import androidx.test.espresso.action.GeneralSwipeAction +import androidx.test.espresso.action.MotionEvents +import androidx.test.espresso.action.Press +import androidx.test.espresso.action.ScrollToAction +import androidx.test.espresso.action.Swipe +import androidx.test.espresso.action.Tap +import androidx.test.espresso.action.Tapper +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.viewpager.widget.ViewPager +import org.catrobat.paintroid.ui.LayerAdapter +import org.hamcrest.Matcher +import org.hamcrest.Matchers +import java.lang.Boolean +import kotlin.AssertionError +import kotlin.Deprecated +import kotlin.Float +import kotlin.FloatArray +import kotlin.Int +import kotlin.Long +import kotlin.String +import kotlin.floatArrayOf + +object UiInteractions { + fun unconstrainedScrollTo(): ViewAction = ViewActions.actionWithAssertions(UnconstrainedScrollToAction()) + + fun waitFor(millis: Long): ViewAction { + return object : ViewAction { + override fun getConstraints(): Matcher { + return ViewMatchers.isRoot() + } + + override fun getDescription(): String { + return "Wait for $millis milliseconds." + } + + override fun perform(uiController: UiController, view: View) { + uiController.loopMainThreadForAtLeast(millis) + } + } + } + + fun assertRecyclerViewCount(expectedCount: Int): ViewAssertion { + return ViewAssertion { view: View, noViewFoundException: NoMatchingViewException? -> + if (noViewFoundException != null) { + throw noViewFoundException + } + val adapter = (view as RecyclerView).adapter as LayerAdapter? + if (adapter != null) { + ViewMatchers.assertThat( + adapter.itemCount, + Matchers.`is`(expectedCount) + ) + } + } + } + + fun setProgress(progress: Int): ViewAction { + return object : ViewAction { + override fun getConstraints(): Matcher { + return ViewMatchers.isAssignableFrom(SeekBar::class.java) + } + + override fun getDescription(): String { + return "Set a progress" + } + + override fun perform(uiController: UiController, view: View) { + try { + val privateSetProgressMethod = + ProgressBar::class.java.getDeclaredMethod( + "setProgressInternal", + Integer.TYPE, + Boolean.TYPE, + Boolean.TYPE + ) + privateSetProgressMethod.isAccessible = true + privateSetProgressMethod.invoke(view, progress, true, true) + } catch (e: ReflectiveOperationException) { + Log.e("SET PROGRESS", "could not set progress") + } + } + } + } + + fun clickOutside(direction: Direction?): ViewAction? { + return ViewActions.actionWithAssertions( + GeneralClickAction(Tap.SINGLE, CoordinatesProvider { view: View -> + val r = Rect() + view.getGlobalVisibleRect(r) + when (direction) { + Direction.ABOVE -> return@CoordinatesProvider floatArrayOf( + r.centerX().toFloat(), + (r.top - 50).toFloat() + ) + Direction.BELOW -> return@CoordinatesProvider floatArrayOf( + r.centerX().toFloat(), + (r.bottom + 50).toFloat() + ) + Direction.LEFT -> return@CoordinatesProvider floatArrayOf( + (r.left - 50).toFloat(), + r.centerY().toFloat() + ) + Direction.RIGHT -> return@CoordinatesProvider floatArrayOf( + (r.right + 50).toFloat(), + r.centerY().toFloat() + ) + } + null + }, Press.FINGER, 0, 1) + ) + } + fun touchAt(x: Int, y: Int): ViewAction = touchAt(x.toFloat(), y.toFloat()) + + fun touchLongAt(provider: CoordinatesProvider?): ViewAction = touchAt(provider, Tap.LONG) + + fun touchLongAt(coordinates: PointF): ViewAction = touchAt(coordinates, Tap.LONG) + + fun touchLongAt(x: Float, y: Float): ViewAction = touchAt(x, y, Tap.LONG) + + @JvmOverloads + fun touchAt(provider: CoordinatesProvider?, tapStyle: Tapper? = Tap.SINGLE): ViewAction { + return ViewActions.actionWithAssertions( + GeneralClickAction(tapStyle, provider, Press.FINGER, 0, 0) + ) + } + + @JvmOverloads + fun touchAt(coordinates: PointF, tapStyle: Tapper? = Tap.SINGLE): ViewAction = touchAt(coordinates.x, coordinates.y, tapStyle) + + @JvmOverloads + fun touchAt(x: Float, y: Float, tapStyle: Tapper? = Tap.SINGLE): ViewAction { + return ViewActions.actionWithAssertions( + GeneralClickAction(tapStyle, PositionCoordinatesProvider.at(x, y), Press.FINGER, 0, 0) + ) + } + + fun touchCenterLeft(): ViewAction = GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER_LEFT, Press.FINGER, 0, 0) + + fun touchCenterTop(): ViewAction = GeneralClickAction(Tap.SINGLE, GeneralLocation.TOP_CENTER, Press.FINGER, 0, 0) + + fun touchCenterBottom(): ViewAction = GeneralClickAction(Tap.SINGLE, GeneralLocation.BOTTOM_CENTER, Press.FINGER, 0, 0) + + fun touchCenterMiddle(): ViewAction = GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER, Press.FINGER, 0, 0) + + fun touchCenterRight(): ViewAction = GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER_RIGHT, Press.FINGER, 0, 0) + + fun swipe(start: PointF, end: PointF): ViewAction = swipe(start.x.toInt(), start.y.toInt(), end.x.toInt(), end.y.toInt()) + + fun swipe(startX: Float, startY: Float, endX: Float, endY: Float): ViewAction = swipe(startX.toInt(), startY.toInt(), endX.toInt(), endY.toInt()) + + fun swipe(startX: Int, startY: Int, endX: Int, endY: Int): ViewAction { + return swipe( + PositionCoordinatesProvider.at(startX.toFloat(), startY.toFloat()), + PositionCoordinatesProvider.at(endX.toFloat(), endY.toFloat()) + ) + } + + fun swipe( + startCoordinatesProvider: CoordinatesProvider?, + endCoordinatesProvider: CoordinatesProvider? + ): ViewAction { + return GeneralSwipeAction( + Swipe.FAST, + startCoordinatesProvider, + endCoordinatesProvider, + Press.FINGER + ) + } + + fun swipeAccurate( + startCoordinatesProvider: CoordinatesProvider?, + endCoordinatesProvider: CoordinatesProvider? + ): ViewAction { + return GeneralSwipeAction( + CustomSwiper.ACCURATE, + startCoordinatesProvider, + endCoordinatesProvider, + Press.FINGER + ) + } + + fun selectViewPagerPage(pos: Int): ViewAction { + return object : ViewAction { + override fun getConstraints(): Matcher { + return ViewMatchers.isAssignableFrom(ViewPager::class.java) + } + + override fun getDescription(): String { + return "select page in ViewPager" + } + + override fun perform(uiController: UiController, view: View) { + (view as ViewPager).currentItem = pos + } + } + } + + enum class Direction { + ABOVE, BELOW, LEFT, RIGHT + } + + private class UnconstrainedScrollToAction : ViewAction { + private val action: ViewAction = ScrollToAction() + override fun getConstraints(): Matcher = ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE) + + override fun getDescription(): String = action.description + + override fun perform(uiController: UiController, view: View) = action.perform(uiController, view) + } + + class DefinedLongTap internal constructor(private val longPressTimeout: Int) : Tapper { + override fun sendTap( + uiController: UiController, + coordinates: FloatArray, + precision: FloatArray, + inputDevice: Int, + buttonState: Int + ): Tapper.Status { + val downEvent = MotionEvents.sendDown( + uiController, coordinates, precision, + inputDevice, buttonState + ).down + try { + // Duration before a press turns into a long press. + // Factor 1.5 is needed, otherwise a long press is not safely detected. + // See android.test.TouchUtils longClickView + val longPressTimeout = (longPressTimeout * 1.5f).toLong() + uiController.loopMainThreadForAtLeast(longPressTimeout) + if (!MotionEvents.sendUp(uiController, downEvent)) { + MotionEvents.sendCancel(uiController, downEvent) + return Tapper.Status.FAILURE + } + } finally { + downEvent.recycle() + } + return Tapper.Status.SUCCESS + } + + @Deprecated("use other sendTap instead") + override fun sendTap( + uiController: UiController, + coordinates: FloatArray, + precision: FloatArray + ): Tapper.Status { + return sendTap( + uiController, coordinates, precision, InputDevice.SOURCE_UNKNOWN, + MotionEvent.BUTTON_PRIMARY + ) + } + + companion object { + fun withPressTimeout(longPressTimeout: Int): Tapper = DefinedLongTap(longPressTimeout) + } + } + + object PressAndReleaseActions { + var motionEvent: MotionEvent? = null + fun pressAction(coordinates: DrawingSurfaceLocationProvider): PressAction = PressAction(coordinates) + + fun releaseAction(): ReleaseAction = ReleaseAction() + + fun tearDownPressAndRelease() { + motionEvent = null + } + + class PressAction internal constructor(var coordinates: DrawingSurfaceLocationProvider) : + ViewAction { + + override fun getConstraints(): Matcher = ViewMatchers.isDisplayingAtLeast(90) + + override fun getDescription(): String = "Press Action" + + override fun perform(uiController: UiController, view: View) { + if (motionEvent != null) { + throw AssertionError("Only one view can be held at a time") + } + val coords = coordinates.calculateCoordinates(view) + val precision = Press.FINGER.describePrecision() + motionEvent = MotionEvents.sendDown(uiController, coords, precision).down + } + } + + class ReleaseAction : ViewAction { + override fun getConstraints(): Matcher = ViewMatchers.isDisplayingAtLeast(90) + + override fun getDescription(): String = "Release" + + override fun perform(uiController: UiController, view: View) { + if (motionEvent == null) { + throw AssertionError("Only one view can be held at a time") + } + MotionEvents.sendUp(uiController, motionEvent) + } + } + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiMatcher.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiMatcher.java deleted file mode 100644 index 3fc0475ff4..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiMatcher.java +++ /dev/null @@ -1,600 +0,0 @@ -/** - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2015 The Catrobat Team - * () - *

- * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - *

- * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - *

- * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.util; - -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Rect; -import android.graphics.Typeface; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.LayerDrawable; -import android.graphics.drawable.StateListDrawable; -import android.graphics.drawable.VectorDrawable; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewParent; -import android.view.WindowManager; -import android.widget.Adapter; -import android.widget.AdapterView; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.SeekBar; -import android.widget.TableRow; -import android.widget.TextView; - -import org.hamcrest.Description; -import org.hamcrest.Matcher; -import org.hamcrest.TypeSafeMatcher; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; -import androidx.test.espresso.NoMatchingViewException; -import androidx.test.espresso.Root; -import androidx.test.espresso.ViewAssertion; -import androidx.test.espresso.matcher.BoundedMatcher; -import androidx.test.espresso.matcher.ViewMatchers; -import androidx.test.espresso.util.HumanReadables; -import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -import static androidx.core.util.Preconditions.checkNotNull; -import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; - -public final class UiMatcher { - - private UiMatcher() { - } - - public static Matcher atPosition(final int position, @NonNull final Matcher itemMatcher) { - checkNotNull(itemMatcher); - return new BoundedMatcher(RecyclerView.class) { - @Override - public void describeTo(Description description) { - description.appendText("has item at position " + position + ": "); - itemMatcher.describeTo(description); - } - - @Override - protected boolean matchesSafely(final RecyclerView view) { - RecyclerView.ViewHolder viewHolder = view.findViewHolderForAdapterPosition(position); - if (viewHolder == null) { - // has no item on such position - return false; - } - return itemMatcher.matches(viewHolder.itemView); - } - }; - } - - public static Matcher withIndex(final Matcher matcher, final int index) { - return new TypeSafeMatcher() { - int currentIndex = 0; - - @Override - public void describeTo(Description description) { - description.appendText("with index: "); - description.appendValue(index); - matcher.describeTo(description); - } - - @Override - public boolean matchesSafely(View view) { - return matcher.matches(view) && currentIndex++ == index; - } - }; - } - - public static Matcher hasTypeFace(final Typeface typeface) { - return new TypeSafeMatcher() { - - @Override - protected boolean matchesSafely(final View view) { - return view instanceof TextView && ((TextView) view).getTypeface() == typeface; - } - - @Override - public void describeTo(Description description) { - description.appendText("the selected TextView doesn't have the TypeFace:" + typeface); - } - }; - } - - public static Matcher hasChildPosition(final int position) { - return new TypeSafeMatcher() { - @Override - public void describeTo(Description description) { - description.appendText("is child #" + position); - } - - @Override - public boolean matchesSafely(View view) { - ViewParent viewParent = view.getParent(); - - if (!(viewParent instanceof ViewGroup)) { - return false; - } - - ViewGroup viewGroup = (ViewGroup) viewParent; - return viewGroup.indexOfChild(view) == position; - } - }; - } - - public static Matcher hasTablePosition(final int rowIndex, final int columnIndex) { - return new TypeSafeMatcher() { - @Override - public void describeTo(Description description) { - description.appendText("is child in cell @(" + rowIndex + "|" + columnIndex + ")"); - } - - @Override - public boolean matchesSafely(View view) { - ViewParent tableRow = view.getParent(); - if (!(tableRow instanceof ViewGroup)) { - return false; - } - if (((ViewGroup) tableRow).indexOfChild(view) != columnIndex) { - return false; - } - - ViewParent tableLayout = tableRow.getParent(); - if (!(tableLayout instanceof ViewGroup)) { - return false; - } - - return ((ViewGroup) tableLayout).indexOfChild((TableRow) tableRow) == rowIndex; - } - }; - } - - public static Matcher withBackgroundColor(final Matcher colorMatcher) { - - return new TypeSafeMatcher() { - @Override - protected boolean matchesSafely(View view) { - ColorDrawable colorDrawable = (ColorDrawable) view.getBackground(); - - if (colorDrawable == null) { - return false; - } - - int bgColor = colorDrawable.getColor(); - - return colorMatcher.matches(bgColor); - } - - @Override - public void describeTo(Description description) { - description.appendText("with background color: "); - colorMatcher.describeTo(description); - } - }; - } - - public static Matcher withBackgroundColor(final int color) { - - return new TypeSafeMatcher() { - @Override - protected boolean matchesSafely(View view) { - Drawable background = view.getBackground(); - - if (background == null) { - return false; - } - - if (background instanceof ColorDrawable) { - return color == ((ColorDrawable) background).getColor(); - } else if (background instanceof LayerDrawable) { - LayerDrawable layerDrawable = (LayerDrawable) background; - Drawable drawable = layerDrawable.getDrawable(0); - return drawable instanceof ColorDrawable - && color == ((ColorDrawable) drawable).getColor(); - } - return false; - } - - @Override - public void describeTo(Description description) { - description.appendText("with background color: " + color); - } - }; - } - - public static Matcher withTextColor(final Matcher colorMatcher) { - - return new TypeSafeMatcher() { - @Override - protected boolean matchesSafely(View view) { - if (!(view instanceof TextView)) { - return false; - } - - TextView textView = (TextView) view; - - int textColor = textView.getCurrentTextColor(); - - return colorMatcher.matches(textColor); - } - - @Override - public void describeTo(Description description) { - description.appendText("with text color: "); - colorMatcher.describeTo(description); - } - }; - } - - public static Matcher withTextColor(final int color) { - - return new TypeSafeMatcher() { - @Override - protected boolean matchesSafely(View view) { - if (!(view instanceof TextView)) { - return false; - } - - TextView textView = (TextView) view; - - int textColor = textView.getCurrentTextColor(); - - return textColor == color; - } - - @Override - public void describeTo(Description description) { - description.appendText("with text color: " + color); - } - }; - } - - public static Matcher withProgress(final int progress) { - - return new TypeSafeMatcher() { - @Override - protected boolean matchesSafely(View view) { - if (!(view instanceof SeekBar)) { - return false; - } - - SeekBar seekbarView = (SeekBar) view; - - int seekbarProgress = seekbarView.getProgress(); - - return seekbarProgress == progress; - } - - @Override - public void describeTo(Description description) { - description.appendText("with progress: " + progress); - } - }; - } - - public static Matcher withBackground(final int resourceId) { - - return new TypeSafeMatcher() { - String resourceName; - - @Override - protected boolean matchesSafely(View target) { - Resources resources = target.getContext().getResources(); - resourceName = resources.getResourceEntryName(resourceId); - - if (!(target instanceof ImageView)) { - return false; - } - - Drawable expectedDrawable = resources.getDrawable(resourceId); - Drawable targetDrawable = target.getBackground(); - - if (expectedDrawable == null || targetDrawable == null) { - return false; - } - - Bitmap expectedBitmap = ((BitmapDrawable) expectedDrawable).getBitmap(); - - if (targetDrawable instanceof BitmapDrawable) { - Bitmap bitmap = ((BitmapDrawable) targetDrawable).getBitmap(); - return bitmap.sameAs(expectedBitmap); - } else if (targetDrawable instanceof StateListDrawable) { - Bitmap bitmap = ((BitmapDrawable) targetDrawable.getCurrent()).getBitmap(); - return bitmap.sameAs(expectedBitmap); - } - return false; - } - - @Override - public void describeTo(Description description) { - description.appendText("with drawable from resource id: "); - description.appendValue(resourceId); - if (resourceName != null) { - description.appendText("["); - description.appendText(resourceName); - description.appendText("]"); - } - } - }; - } - - public static Matcher withChildren(final Matcher numberOfChildrenMatcher) { - - return new TypeSafeMatcher() { - - @Override - protected boolean matchesSafely(View target) { - if (!(target instanceof ViewGroup)) { - return false; - } - - return numberOfChildrenMatcher.matches(((ViewGroup) target).getChildCount()); - } - - @Override - public void describeTo(Description description) { - description.appendText("with children # is "); - numberOfChildrenMatcher.describeTo(description); - } - }; - } - - public static Matcher equalsNumberDots(final int value) { - return new BoundedMatcher(LinearLayout.class) { - private String layoutCount = null; - - @Override - public void describeTo(Description description) { - description.appendText("Number of dots does not match.\n"); - - description.appendText("Expected: " + value); - - if (layoutCount != null) { - description.appendText("\nIs: " + layoutCount); - } - } - - @Override - public boolean matchesSafely(LinearLayout layout) { - layoutCount = String.valueOf(layout.getChildCount()); - return layout.getChildCount() == value; - } - }; - } - - public static Matcher checkDotsColors(final int activeIndex, final int colorActive, - final int colorInactive) { - - return new BoundedMatcher(LinearLayout.class) { - private String errorTextView = null; - private int currentIndex = -1; - private int currentColor; - private int expectedColor; - - @Override - public boolean matchesSafely(LinearLayout layout) { - for (currentIndex = 0; currentIndex < layout.getChildCount(); currentIndex++) { - TextView textView = (TextView) layout.getChildAt(currentIndex); - - if (textView == null) { - errorTextView = "DotView is not TextView"; - return false; - } - - currentColor = textView.getCurrentTextColor(); - - if (currentIndex == activeIndex) { - if (currentColor != colorActive) { - expectedColor = colorActive; - return false; - } - } else { - if (currentColor != colorInactive) { - expectedColor = colorInactive; - return false; - } - } - } - - return true; - } - - @Override - public void describeTo(Description description) { - description.appendText("\nAt Index: " + currentIndex); - if (errorTextView != null) { - description.appendText("\nIs not a textview"); - return; - } - - description.appendText("Dot Color does not match "); - description.appendText("\nExcepted: " + expectedColor); - description.appendText("\nIs: " + currentColor); - } - }; - } - - public static Matcher withDrawable(final int resourceId) { - - return new TypeSafeMatcher() { - String resourceName; - - @Override - protected boolean matchesSafely(View target) { - Resources resources = target.getContext().getResources(); - resourceName = resources.getResourceEntryName(resourceId); - - if (!(target instanceof ImageView)) { - return false; - } - - Drawable expectedDrawable = resources.getDrawable(resourceId); - ImageView targetImageView = (ImageView) target; - Drawable targetDrawable = targetImageView.getDrawable(); - - if (expectedDrawable == null || targetDrawable == null) { - return false; - } - - Bitmap expectedBitmap; - - if (targetDrawable instanceof BitmapDrawable) { - Bitmap targetBitmap = ((BitmapDrawable) targetDrawable).getBitmap(); - expectedBitmap = ((BitmapDrawable) expectedDrawable).getBitmap(); - return targetBitmap.sameAs(expectedBitmap); - } else if (targetDrawable instanceof VectorDrawable || targetDrawable instanceof VectorDrawableCompat) { - Bitmap targetBitmap = vectorToBitmap((VectorDrawable) expectedDrawable); - expectedBitmap = vectorToBitmap((VectorDrawable) expectedDrawable); - return targetBitmap.sameAs(expectedBitmap); - } else if (targetDrawable instanceof StateListDrawable) { - Bitmap targetBitmap = vectorToBitmap((VectorDrawable) expectedDrawable); - expectedBitmap = vectorToBitmap((VectorDrawable) expectedDrawable); - return targetBitmap.sameAs(expectedBitmap); - } - return false; - } - - @Override - public void describeTo(Description description) { - description.appendText("with drawable from resource id: "); - description.appendValue(resourceId); - if (resourceName != null) { - description.appendText("["); - description.appendText(resourceName); - description.appendText("]"); - } - } - - private Bitmap vectorToBitmap(VectorDrawable vectorDrawable) { - return Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), - vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); - } - }; - } - - /** - * Matches {@link Root}s that are toasts (i.e. is not a window of the currently resumed activity). - * - * @see androidx.test.espresso.matcher.RootMatchers#isDialog() - */ - public static Matcher isToast() { - return new TypeSafeMatcher() { - @Override - public void describeTo(Description description) { - description.appendText("is toast"); - } - - @Override - public boolean matchesSafely(Root root) { - int type = root.getWindowLayoutParams().get().type; - return type == WindowManager.LayoutParams.TYPE_TOAST; - } - }; - } - - public static ViewAssertion isNotVisible() { - return new ViewAssertion() { - @Override - public void check(View view, NoMatchingViewException noView) { - if (view != null) { - boolean isRect = view.getGlobalVisibleRect(new Rect()); - boolean isVisible = withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE).matches(view); - boolean retVal = !(isRect && isVisible); - - assertThat("View is present in the hierarchy: " + HumanReadables.describe(view), - retVal, is(true)); - } - } - }; - } - - public static Matcher isOnLeftSide() { - return new TypeSafeMatcher() { - - @Override - public void describeTo(Description description) { - description.appendText("View is not on the Left Side"); - } - - @Override - public boolean matchesSafely(View view) { - int displayMiddle = Resources.getSystem().getDisplayMetrics().widthPixels / 2; - int viewStartX = (int) view.getX(); - int viewEndX = viewStartX + view.getWidth(); - - return viewStartX < displayMiddle && viewEndX < displayMiddle; - } - }; - } - - public static Matcher isOnRightSide() { - return new TypeSafeMatcher() { - - @Override - public void describeTo(Description description) { - description.appendText("View is not on the Right Side"); - } - - @Override - public boolean matchesSafely(View view) { - int displayMiddle = Resources.getSystem().getDisplayMetrics().widthPixels / 2; - int viewStartX = (int) view.getX(); - int viewEndX = viewStartX + view.getWidth(); - - return viewStartX > displayMiddle && viewEndX > displayMiddle; - } - }; - } - - public static Matcher withAdaptedData(final int resourceId) { - return new TypeSafeMatcher() { - - @Override - public void describeTo(Description description) { - description.appendText("with class name: "); - } - - @Override - public boolean matchesSafely(View view) { - String resourceName; - - if (!(view instanceof AdapterView)) { - return false; - } - - Resources resources = view.getContext().getResources(); - resourceName = resources.getString(resourceId); - - @SuppressWarnings("rawtypes") - Adapter adapter = ((AdapterView) view).getAdapter(); - for (int i = 0; i < adapter.getCount(); i++) { - if (resourceName.equals(((MenuItem) adapter.getItem(i)).getTitle().toString())) { - return true; - } - } - - return false; - } - }; - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiMatcher.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiMatcher.kt new file mode 100644 index 0000000000..7b742431f4 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiMatcher.kt @@ -0,0 +1,504 @@ +/** + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * (//developer.catrobat.org/credits>) + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see //www.gnu.org/licenses/>. + */ +package org.catrobat.paintroid.test.espresso.util + +import android.content.res.Resources +import android.graphics.Bitmap +import android.graphics.Rect +import android.graphics.Typeface +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable +import android.graphics.drawable.LayerDrawable +import android.graphics.drawable.StateListDrawable +import android.graphics.drawable.VectorDrawable +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import android.view.ViewParent +import android.view.WindowManager +import android.widget.Adapter +import android.widget.AdapterView +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.SeekBar +import android.widget.TableRow +import android.widget.TextView +import androidx.core.util.Preconditions +import androidx.recyclerview.widget.RecyclerView +import androidx.test.espresso.NoMatchingViewException +import androidx.test.espresso.Root +import androidx.test.espresso.ViewAssertion +import androidx.test.espresso.matcher.BoundedMatcher +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.util.HumanReadables +import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat +import org.hamcrest.Description +import org.hamcrest.Matcher +import org.hamcrest.Matchers +import org.hamcrest.TypeSafeMatcher +import org.junit.Assert + +object UiMatcher { + + private fun vectorToBitmap(vectorDrawable: VectorDrawable): Bitmap { + return Bitmap.createBitmap( + vectorDrawable.intrinsicWidth, + vectorDrawable.intrinsicHeight, Bitmap.Config.ARGB_8888 + ) + } + + fun atPosition(position: Int, itemMatcher: Matcher): BoundedMatcher { + Preconditions.checkNotNull(itemMatcher) + return object : BoundedMatcher(RecyclerView::class.java) { + override fun describeTo(description: Description) { + description.appendText("has item at position $position: ") + itemMatcher.describeTo(description) + } + + override fun matchesSafely(view: RecyclerView): Boolean { + val viewHolder: RecyclerView.ViewHolder? = + view.findViewHolderForAdapterPosition(position) + if (viewHolder == null) { + // has no item on such position + return false + } + return itemMatcher.matches(viewHolder.itemView) + } + } + } + + fun withIndex(matcher: Matcher, index: Int): TypeSafeMatcher { + return object : TypeSafeMatcher() { + var currentIndex: Int = 0 + override fun describeTo(description: Description) { + description.appendText("with index: ") + description.appendValue(index) + matcher.describeTo(description) + } + + public override fun matchesSafely(view: View?): Boolean { + return matcher.matches(view) && currentIndex++ == index + } + } + } + + fun hasTypeFace(typeface: Typeface): TypeSafeMatcher { + return object : TypeSafeMatcher() { + override fun matchesSafely(view: View?): Boolean { + return view is TextView && view.typeface === typeface + } + + override fun describeTo(description: Description) { + description.appendText("the selected TextView doesn't have the TypeFace:$typeface") + } + } + } + + fun hasChildPosition(position: Int): Matcher { + return object : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText("is child #$position") + } + + public override fun matchesSafely(view: View): Boolean { + val viewParent: ViewParent = view.parent + if (!(viewParent is ViewGroup)) { + return false + } + return viewParent.indexOfChild(view) == position + } + } + } + + fun hasTablePosition(rowIndex: Int, columnIndex: Int): Matcher { + return object : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText("is child in cell @($rowIndex|$columnIndex)") + } + + public override fun matchesSafely(view: View): Boolean { + val tableRow: ViewParent = view.parent + if (!(tableRow is ViewGroup)) { + return false + } + if (tableRow.indexOfChild(view) != columnIndex) { + return false + } + val tableLayout: ViewParent = tableRow.getParent() + if (!(tableLayout is ViewGroup)) { + return false + } + return tableLayout.indexOfChild(tableRow as TableRow) == rowIndex + } + } + } + + fun withBackgroundColor(colorMatcher: Matcher): Matcher { + return object : TypeSafeMatcher() { + override fun matchesSafely(view: View): Boolean { + val colorDrawable: ColorDrawable? = view.background as ColorDrawable + if (colorDrawable == null) { + return false + } + val bgColor: Int = colorDrawable.color + return colorMatcher.matches(bgColor) + } + + override fun describeTo(description: Description) { + description.appendText("with background color: ") + colorMatcher.describeTo(description) + } + } + } + + fun withBackgroundColor(color: Int): Matcher { + return object : TypeSafeMatcher() { + override fun matchesSafely(view: View): Boolean { + val background: Drawable? = view.background + if (background == null) { + return false + } + if (background is ColorDrawable) { + return color == background.color + } else if (background is LayerDrawable) { + val drawable: Drawable = background.getDrawable(0) + return drawable is ColorDrawable && + color == drawable.color + } + return false + } + + override fun describeTo(description: Description) { + description.appendText("with background color: $color") + } + } + } + + fun withTextColor(colorMatcher: Matcher): TypeSafeMatcher { + return object : TypeSafeMatcher() { + override fun matchesSafely(view: View?): Boolean { + if (!(view is TextView)) { + return false + } + val textColor: Int = view.currentTextColor + return colorMatcher.matches(textColor) + } + + override fun describeTo(description: Description) { + description.appendText("with text color: ") + colorMatcher.describeTo(description) + } + } + } + + fun withTextColor(color: Int): TypeSafeMatcher { + return object : TypeSafeMatcher() { + override fun matchesSafely(view: View?): Boolean { + if (!(view is TextView)) { + return false + } + val textColor: Int = view.currentTextColor + return textColor == color + } + + override fun describeTo(description: Description) { + description.appendText("with text color: $color") + } + } + } + + fun withProgress(progress: Int): TypeSafeMatcher { + return object : TypeSafeMatcher() { + override fun matchesSafely(view: View?): Boolean { + if (!(view is SeekBar)) { + return false + } + val seekbarProgress: Int = view.progress + return seekbarProgress == progress + } + + override fun describeTo(description: Description) { + description.appendText("with progress: $progress") + } + } + } + + fun withBackground(resourceId: Int): Matcher { + return object : TypeSafeMatcher() { + var resourceName: String? = null + override fun matchesSafely(target: View): Boolean { + val resources: Resources = target.context.resources + resourceName = resources.getResourceEntryName(resourceId) + if (!(target is ImageView)) { + return false + } + val expectedDrawable: Drawable? = resources.getDrawable(resourceId) + val targetDrawable: Drawable? = target.getBackground() + if (expectedDrawable == null || targetDrawable == null) { + return false + } + if (expectedDrawable::class != targetDrawable::class) { + return false + } + + if (targetDrawable is BitmapDrawable) { + val targetBitmap: Bitmap = targetDrawable.bitmap + val expectedBitmap = (expectedDrawable as BitmapDrawable).bitmap + return targetBitmap.sameAs(expectedBitmap) + } else if (targetDrawable is VectorDrawable) { + val targetBitmap: Bitmap = vectorToBitmap(targetDrawable) + val expectedBitmap = vectorToBitmap(expectedDrawable as VectorDrawable) + return targetBitmap.sameAs(expectedBitmap) + } else if (targetDrawable is StateListDrawable) { + val targetBitmap: Bitmap = (targetDrawable.getCurrent() as BitmapDrawable).bitmap + val expectedBitmap = (expectedDrawable as BitmapDrawable).bitmap + return targetBitmap.sameAs(expectedBitmap) + } + return false + } + + override fun describeTo(description: Description) { + description.appendText("with drawable from resource id: ") + description.appendValue(resourceId) + if (resourceName != null) { + description.appendText("[") + description.appendText(resourceName) + description.appendText("]") + } + } + } + } + + fun withChildren(numberOfChildrenMatcher: Matcher): TypeSafeMatcher { + return object : TypeSafeMatcher() { + override fun matchesSafely(target: View?): Boolean { + if (!(target is ViewGroup)) { + return false + } + return numberOfChildrenMatcher.matches(target.childCount) + } + + override fun describeTo(description: Description) { + description.appendText("with children # is ") + numberOfChildrenMatcher.describeTo(description) + } + } + } + + fun equalsNumberDots(value: Int): BoundedMatcher { + return object : BoundedMatcher(LinearLayout::class.java) { + private var layoutCount: String? = null + override fun describeTo(description: Description) { + description.appendText("Number of dots does not match.\n") + description.appendText("Expected: $value") + if (layoutCount != null) { + description.appendText("\nIs: $layoutCount") + } + } + + public override fun matchesSafely(layout: LinearLayout): Boolean { + layoutCount = layout.childCount.toString() + return layout.childCount == value + } + } + } + + fun checkDotsColors( + activeIndex: Int, colorActive: Int, + colorInactive: Int + ): BoundedMatcher { + return object : BoundedMatcher(LinearLayout::class.java) { + private var errorTextView: String? = null + private var currentIndex: Int = -1 + private var currentColor: Int = 0 + private var expectedColor: Int = 0 + public override fun matchesSafely(layout: LinearLayout): Boolean { + currentIndex = 0 + while (currentIndex < layout.childCount) { + val textView: TextView? = layout.getChildAt(currentIndex) as TextView + if (textView == null) { + errorTextView = "DotView is not TextView" + return false + } + currentColor = textView.currentTextColor + if (currentIndex == activeIndex) { + if (currentColor != colorActive) { + expectedColor = colorActive + return false + } + } else { + if (currentColor != colorInactive) { + expectedColor = colorInactive + return false + } + } + currentIndex++ + } + return true + } + + override fun describeTo(description: Description) { + description.appendText("\nAt Index: $currentIndex") + if (errorTextView != null) { + description.appendText("\nIs not a textview") + return + } + description.appendText("Dot Color does not match ") + description.appendText("\nExcepted: $expectedColor") + description.appendText("\nIs: $currentColor") + } + } + } + + fun withDrawable(resourceId: Int): Matcher { + return object : TypeSafeMatcher() { + var resourceName: String? = null + override fun matchesSafely(target: View): Boolean { + val resources: Resources = target.context.resources + resourceName = resources.getResourceEntryName(resourceId) + if (!(target is ImageView)) { + return false + } + val expectedDrawable: Drawable? = resources.getDrawable(resourceId) + val targetDrawable: Drawable? = target.drawable + if (expectedDrawable == null || targetDrawable == null) { + return false + } + val expectedBitmap: Bitmap + if (targetDrawable is BitmapDrawable) { + val targetBitmap: Bitmap = targetDrawable.bitmap + expectedBitmap = (expectedDrawable as BitmapDrawable).bitmap + return targetBitmap.sameAs(expectedBitmap) + } else if (targetDrawable is VectorDrawable || targetDrawable is VectorDrawableCompat) { + val targetBitmap: Bitmap = vectorToBitmap(targetDrawable as VectorDrawable) + expectedBitmap = vectorToBitmap(expectedDrawable as VectorDrawable) + return targetBitmap.sameAs(expectedBitmap) + } else if (targetDrawable is StateListDrawable) { + val targetBitmap: Bitmap = vectorToBitmap(targetDrawable.getCurrent() as VectorDrawable) + expectedBitmap = vectorToBitmap(expectedDrawable as VectorDrawable) + return targetBitmap.sameAs(expectedBitmap) + } + return false + } + + override fun describeTo(description: Description) { + description.appendText("with drawable from resource id: ") + description.appendValue(resourceId) + if (resourceName != null) { + description.appendText("[") + description.appendText(resourceName) + description.appendText("]") + } + } + } + } + + /** + * Matches [Root]s that are toasts (i.e. is not a window of the currently resumed activity). + * + * @see androidx.test.espresso.matcher.RootMatchers.isDialog + */ + val isToast: Matcher + get() = object : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText("is toast") + } + + public override fun matchesSafely(root: Root): Boolean { + val type: Int = root.windowLayoutParams.get().type + return type == WindowManager.LayoutParams.TYPE_TOAST + } + } + val isNotVisible: ViewAssertion + get() { + return object : ViewAssertion { + override fun check(view: View, noView: NoMatchingViewException) { + if (view != null) { + val isRect: Boolean = view.getGlobalVisibleRect(Rect()) + val isVisible: Boolean = + ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE) + .matches(view) + val retVal: Boolean = !(isRect && isVisible) + Assert.assertThat( + "View is present in the hierarchy: " + HumanReadables.describe(view), + retVal, Matchers.`is`(true) + ) + } + } + } + } + val isOnLeftSide: Matcher + get() { + return object : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText("View is not on the Left Side") + } + + public override fun matchesSafely(view: View): Boolean { + val displayMiddle: Int = Resources.getSystem().displayMetrics.widthPixels / 2 + val viewStartX: Int = view.x.toInt() + val viewEndX: Int = viewStartX + view.width + return viewStartX < displayMiddle && viewEndX < displayMiddle + } + } + } + val isOnRightSide: Matcher + get() { + return object : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText("View is not on the Right Side") + } + + public override fun matchesSafely(view: View): Boolean { + val displayMiddle: Int = Resources.getSystem().displayMetrics.widthPixels / 2 + val viewStartX: Int = view.x.toInt() + val viewEndX: Int = viewStartX + view.width + return viewStartX > displayMiddle && viewEndX > displayMiddle + } + } + } + + fun withAdaptedData(resourceId: Int): Matcher { + return object : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText("with class name: ") + } + + public override fun matchesSafely(view: View): Boolean { + val resourceName: String + if (!(view is AdapterView<*>)) { + return false + } + val resources: Resources = view.getContext().resources + resourceName = resources.getString(resourceId) + val adapter: Adapter = view.adapter + for (i in 0 until adapter.count) { + if (resourceName == (adapter.getItem(i) as MenuItem).title.toString()) { + return true + } + } + return false + } + } + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BottomNavigationViewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BottomNavigationViewInteraction.java deleted file mode 100644 index ce738e4b4f..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BottomNavigationViewInteraction.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2015 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.util.wrappers; - -import org.catrobat.paintroid.R; -import org.catrobat.paintroid.tools.ToolType; - -import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withDrawable; -import static org.hamcrest.Matchers.allOf; - -import androidx.test.espresso.ViewInteraction; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant; -import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; - -public final class BottomNavigationViewInteraction extends CustomViewInteraction { - private BottomNavigationViewInteraction() { - super(onView(withId(R.id.pocketpaint_bottom_navigation))); - } - - public static BottomNavigationViewInteraction onBottomNavigationView() { - return new BottomNavigationViewInteraction(); - } - - public ViewInteraction onToolsClicked() { - return onView(allOf(withId(R.id.icon), isDescendantOfA(withId(R.id.action_tools)))) - .perform(click()); - } - - public ViewInteraction onCurrentClicked() { - return onView(allOf(withId(R.id.icon), isDescendantOfA(withId(R.id.action_current_tool)))) - .perform(click()); - } - - public ViewInteraction checkShowsCurrentTool(ToolType toolType) { - onView(allOf(withId(R.id.icon), isDescendantOfA(withId(R.id.action_current_tool)))) - .check(matches(withDrawable(toolType.getDrawableResource()))); - - return onView(withId(R.id.action_current_tool)) - .check(matches(hasDescendant(withText(toolType.getNameResource())))); - } - - public ViewInteraction onColorClicked() { - return onView(allOf(withId(R.id.icon), isDescendantOfA(withId(R.id.action_color_picker)))) - .perform(click()); - } - - public ViewInteraction onLayersClicked() { - return onView(allOf(withId(R.id.icon), isDescendantOfA(withId(R.id.action_layers)))) - .perform(click()); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BottomNavigationViewInteraction.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BottomNavigationViewInteraction.kt new file mode 100644 index 0000000000..182d6a5570 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BottomNavigationViewInteraction.kt @@ -0,0 +1,90 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.util.wrappers + +import androidx.test.espresso.Espresso +import androidx.test.espresso.ViewInteraction +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.withId +import org.catrobat.paintroid.R +import org.catrobat.paintroid.test.espresso.util.UiMatcher +import org.catrobat.paintroid.tools.ToolType +import org.hamcrest.Matchers.allOf + +class BottomNavigationViewInteraction private constructor() : + CustomViewInteraction(Espresso.onView(withId(R.id.pocketpaint_bottom_navigation))) { + fun onToolsClicked(): ViewInteraction { + return Espresso.onView( + allOf( + withId(R.id.icon), + ViewMatchers.isDescendantOfA(withId(R.id.action_tools)) + ) + ) + .perform(ViewActions.click()) + } + + fun onCurrentClicked(): ViewInteraction { + return Espresso.onView( + allOf( + withId(R.id.icon), + ViewMatchers.isDescendantOfA(withId(R.id.action_current_tool)) + ) + ) + .perform(ViewActions.click()) + } + + fun checkShowsCurrentTool(toolType: ToolType): ViewInteraction { + Espresso.onView( + allOf( + withId(R.id.icon), + ViewMatchers.isDescendantOfA(withId(R.id.action_current_tool)) + ) + ) + .check(ViewAssertions.matches(UiMatcher.withDrawable(toolType.drawableResource))) + return Espresso.onView(withId(R.id.action_current_tool)) + .check(ViewAssertions.matches(ViewMatchers.hasDescendant(ViewMatchers.withText(toolType.nameResource)))) + } + + fun onColorClicked(): ViewInteraction { + return Espresso.onView( + allOf( + withId(R.id.icon), + ViewMatchers.isDescendantOfA(withId(R.id.action_color_picker)) + ) + ) + .perform(ViewActions.click()) + } + + fun onLayersClicked(): ViewInteraction { + return Espresso.onView( + allOf( + withId(R.id.icon), + ViewMatchers.isDescendantOfA(withId(R.id.action_layers)) + ) + ) + .perform(ViewActions.click()) + } + + companion object { + @JvmStatic + fun onBottomNavigationView() = BottomNavigationViewInteraction() + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BrushPickerViewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BrushPickerViewInteraction.java deleted file mode 100644 index 00f1b6c78f..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BrushPickerViewInteraction.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2015 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.util.wrappers; - -import org.catrobat.paintroid.R; - -import androidx.test.espresso.ViewInteraction; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.matcher.ViewMatchers.withId; - -public final class BrushPickerViewInteraction extends CustomViewInteraction { - private BrushPickerViewInteraction() { - super(onView(withId(R.id.pocketpaint_layout_tool_options))); - } - - public static BrushPickerViewInteraction onBrushPickerView() { - return new BrushPickerViewInteraction(); - } - - public ViewInteraction onStrokeWidthSeekBar() { - return onView(withId(R.id.pocketpaint_stroke_width_seek_bar)); - } - - public ViewInteraction onStrokeWidthTextView() { - return onView(withId(R.id.pocketpaint_stroke_width_width_text)); - } - - public ViewInteraction onStrokeCapSquareView() { - return onView(withId(R.id.pocketpaint_stroke_ibtn_rect)); - } - - public ViewInteraction onStrokeCapRoundView() { - return onView(withId(R.id.pocketpaint_stroke_ibtn_circle)); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BrushPickerViewInteraction.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BrushPickerViewInteraction.kt new file mode 100644 index 0000000000..a6300f22d4 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BrushPickerViewInteraction.kt @@ -0,0 +1,39 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.util.wrappers + +import androidx.test.espresso.Espresso +import androidx.test.espresso.ViewInteraction +import androidx.test.espresso.matcher.ViewMatchers.withId +import org.catrobat.paintroid.R + +class BrushPickerViewInteraction private constructor() : + CustomViewInteraction(Espresso.onView(withId(R.id.pocketpaint_layout_tool_options))) { + fun onStrokeWidthSeekBar(): ViewInteraction = Espresso.onView(withId(R.id.pocketpaint_stroke_width_seek_bar)) + + fun onStrokeWidthTextView(): ViewInteraction = Espresso.onView(withId(R.id.pocketpaint_stroke_width_width_text)) + + fun onStrokeCapSquareView(): ViewInteraction = Espresso.onView(withId(R.id.pocketpaint_stroke_ibtn_rect)) + + fun onStrokeCapRoundView(): ViewInteraction = Espresso.onView(withId(R.id.pocketpaint_stroke_ibtn_circle)) + + companion object { + fun onBrushPickerView(): BrushPickerViewInteraction = BrushPickerViewInteraction() + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BrushToolOptionsViewInteraction.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BrushToolOptionsViewInteraction.kt index 87b69f2505..5ee2fd2314 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BrushToolOptionsViewInteraction.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BrushToolOptionsViewInteraction.kt @@ -29,11 +29,11 @@ import androidx.test.espresso.matcher.ViewMatchers.withId import org.hamcrest.core.IsNot.not class BrushToolOptionsViewInteraction private constructor() : - CustomViewInteraction(onView(withId(R.id.pocketpaint_layout_tool_options))) { + CustomViewInteraction(onView(withId(R.id.pocketpaint_layout_tool_options))) { companion object { fun onBrushToolOptionsView(): BrushToolOptionsViewInteraction = - BrushToolOptionsViewInteraction() + BrushToolOptionsViewInteraction() } private fun getButtonIdFromShapeDrawType(strokeCapType: Cap): Int { @@ -46,7 +46,7 @@ class BrushToolOptionsViewInteraction private constructor() : fun performSelectStrokeCapType(strokeCapType: Cap): BrushToolOptionsViewInteraction { onView(withId(getButtonIdFromShapeDrawType(strokeCapType))) - .perform(click()) + .perform(click()) return this } diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ColorPickerPreviewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ColorPickerPreviewInteraction.java deleted file mode 100644 index b206d904d2..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ColorPickerPreviewInteraction.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2022 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package org.catrobat.paintroid.test.espresso.util.wrappers; - -import org.catrobat.paintroid.R; - -import androidx.test.espresso.ViewInteraction; - -import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withBackgroundColor; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; - -public final class ColorPickerPreviewInteraction extends CustomViewInteraction { - - private ColorPickerPreviewInteraction() { - super(onView(withId(R.id.previewSurface))); - } - - public static ColorPickerPreviewInteraction onColorPickerPreview() { - return new ColorPickerPreviewInteraction(); - } - - public ViewInteraction onPositiveButton() { - return onView(withId(android.R.id.button1)) - // to avoid following exception when running on emulator: - // Caused by: java.lang.SecurityException: - // Injecting to another application requires INJECT_EVENTS permission - .perform(closeSoftKeyboard()); - } - - public ViewInteraction onNegativeButton() { - return onView(withId(android.R.id.button2)) - // to avoid following exception when running on emulator: - // Caused by: java.lang.SecurityException: - // Injecting to another application requires INJECT_EVENTS permission - .perform(closeSoftKeyboard()); - } - - public void checkColorPreviewColor(int color) { - onView(withId(R.id.colorPreview)) - .check(matches(withBackgroundColor(color))); - } - - public ColorPickerPreviewInteraction performCloseColorPickerPreviewWithDoneButton() { - check(matches(isDisplayed())); - onView(withId(R.id.doneAction)) - .perform(click()); - return this; - } - - public ColorPickerPreviewInteraction performCloseColorPickerPreviewWithBackButtonDecline() { - check(matches(isDisplayed())); - onView(withId(R.id.backAction)) - .perform(click()); - - onNegativeButton() - .perform(click()); - return this; - } - - public ColorPickerPreviewInteraction performCloseColorPickerPreviewWithBackButtonAccept() { - check(matches(isDisplayed())); - onView(withId(R.id.backAction)) - .perform(click()); - - onPositiveButton() - .perform(click()); - return this; - } - - public void assertShowColorPickerPreviewBackDialog() { - check(matches(isDisplayed())); - onView(withId(R.id.backAction)) - .perform(click()); - - onView(withId(android.R.id.button1)) - .check(matches(isDisplayed())); - - onView(withId(android.R.id.button2)) - .check(matches(isDisplayed())); - - onView(withText(R.string.color_picker_save_dialog_title)) - .check(matches(isDisplayed())); - - onView(withText(R.string.color_picker_save_dialog_msg)) - .check(matches(isDisplayed())); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ColorPickerPreviewInteraction.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ColorPickerPreviewInteraction.kt new file mode 100644 index 0000000000..18054d109d --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ColorPickerPreviewInteraction.kt @@ -0,0 +1,94 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.util.wrappers + +import androidx.test.espresso.Espresso +import androidx.test.espresso.ViewInteraction +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import org.catrobat.paintroid.R +import org.catrobat.paintroid.test.espresso.util.UiMatcher + +class ColorPickerPreviewInteraction private constructor() : + CustomViewInteraction(Espresso.onView(withId(R.id.previewSurface))) { + fun onPositiveButton(): ViewInteraction { + return Espresso.onView(withId(android.R.id.button1)) // to avoid following exception when running on emulator: + // Caused by: java.lang.SecurityException: + // Injecting to another application requires INJECT_EVENTS permission + .perform(ViewActions.closeSoftKeyboard()) + } + + fun onNegativeButton(): ViewInteraction { + return Espresso.onView(withId(android.R.id.button2)) // to avoid following exception when running on emulator: + // Caused by: java.lang.SecurityException: + // Injecting to another application requires INJECT_EVENTS permission + .perform(ViewActions.closeSoftKeyboard()) + } + + fun checkColorPreviewColor(color: Int) { + Espresso.onView(withId(R.id.colorPreview)) + .check(ViewAssertions.matches(UiMatcher.withBackgroundColor(color))) + } + + fun performCloseColorPickerPreviewWithDoneButton(): ColorPickerPreviewInteraction { + check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.doneAction)) + .perform(ViewActions.click()) + return this + } + + fun performCloseColorPickerPreviewWithBackButtonDecline(): ColorPickerPreviewInteraction { + check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.backAction)) + .perform(ViewActions.click()) + onNegativeButton() + .perform(ViewActions.click()) + return this + } + + fun performCloseColorPickerPreviewWithBackButtonAccept(): ColorPickerPreviewInteraction { + check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.backAction)) + .perform(ViewActions.click()) + onPositiveButton() + .perform(ViewActions.click()) + return this + } + + fun assertShowColorPickerPreviewBackDialog() { + check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.backAction)) + .perform(ViewActions.click()) + Espresso.onView(withId(android.R.id.button1)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(android.R.id.button2)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withText(R.string.color_picker_save_dialog_title)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withText(R.string.color_picker_save_dialog_msg)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + + companion object { + fun onColorPickerPreview(): ColorPickerPreviewInteraction = ColorPickerPreviewInteraction() + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ColorPickerViewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ColorPickerViewInteraction.java deleted file mode 100644 index acb092dee6..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ColorPickerViewInteraction.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2022 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.util.wrappers; - -import android.widget.TableLayout; -import android.widget.TableRow; - -import org.catrobat.paintroid.R; -import org.catrobat.paintroid.colorpicker.ColorHistoryView; -import org.catrobat.paintroid.colorpicker.PresetSelectorView; - -import static org.catrobat.paintroid.test.espresso.util.UiMatcher.hasTablePosition; -import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withBackgroundColor; -import static org.catrobat.paintroid.test.espresso.util.wrappers.BottomNavigationViewInteraction.onBottomNavigationView; -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.containsString; - -import androidx.test.espresso.ViewInteraction; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard; -import static androidx.test.espresso.action.ViewActions.scrollTo; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom; -import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.withClassName; -import static androidx.test.espresso.matcher.ViewMatchers.withId; - -public final class ColorPickerViewInteraction extends CustomViewInteraction { - private static final int COLOR_PICKER_BUTTONS_PER_ROW = 4; - public static final int MAXIMUM_COLORS_IN_HISTORY = 4; - - protected ColorPickerViewInteraction() { - super(onView(withId(R.id.color_picker_view))); - } - - public static ColorPickerViewInteraction onColorPickerView() { - return new ColorPickerViewInteraction(); - } - - public ViewInteraction onPositiveButton() { - return onView(withId(android.R.id.button1)) - // to avoid following exception when running on emulator: - // Caused by: java.lang.SecurityException: - // Injecting to another application requires INJECT_EVENTS permission - .perform(closeSoftKeyboard()); - } - - public ColorPickerViewInteraction performOpenColorPicker() { - onBottomNavigationView() - .onColorClicked(); - return this; - } - - public ViewInteraction onNegativeButton() { - return onView(withId(android.R.id.button2)) - // to avoid following exception when running on emulator: - // Caused by: java.lang.SecurityException: - // Injecting to another application requires INJECT_EVENTS permission - .perform(closeSoftKeyboard()); - } - - public ViewInteraction clickPipetteButton() { - return onView(withId(R.id.color_picker_pipette_btn)) - .perform(click()); - } - - public void checkCurrentViewColor(int color) { - onView(withId(R.id.color_picker_current_color_view)) - .check(matches(withBackgroundColor(color))); - } - - public void checkNewColorViewColor(int color) { - onView(withId(R.id.color_picker_new_color_view)) - .check(matches(withBackgroundColor(color))); - } - - public ColorPickerViewInteraction performCloseColorPickerWithDialogButton() { - check(matches(isDisplayed())); - onPositiveButton() - .perform(click()); - return this; - } - - public ColorPickerViewInteraction performClickColorPickerPresetSelectorButton(int buttonPosition) { - final int colorButtonRowPosition = buttonPosition / COLOR_PICKER_BUTTONS_PER_ROW; - final int colorButtonColPosition = buttonPosition % COLOR_PICKER_BUTTONS_PER_ROW; - - onView(allOf(isDescendantOfA(withClassName(containsString(PresetSelectorView.class.getSimpleName()))), - isDescendantOfA(isAssignableFrom(TableLayout.class)), - isDescendantOfA(isAssignableFrom(TableRow.class)), - hasTablePosition(colorButtonRowPosition, colorButtonColPosition))) - .perform(closeSoftKeyboard()) - .perform(scrollTo()) - .perform(click()); - return this; - } - - public ColorPickerViewInteraction performClickOnHistoryColor(int buttonPosition) { - final int colorButtonColPosition = buttonPosition % COLOR_PICKER_BUTTONS_PER_ROW; - - onView(allOf(isDescendantOfA(withClassName(containsString(ColorHistoryView.class.getSimpleName()))), - isDescendantOfA(isAssignableFrom(TableLayout.class)), - isDescendantOfA(isAssignableFrom(TableRow.class)), - hasTablePosition(0, colorButtonColPosition))) - .perform(closeSoftKeyboard()) - .perform(scrollTo()) - .perform(click()); - return this; - } - - public void checkHistoryColor(int buttonPosition, int color) { - final int colorButtonColPosition = buttonPosition % COLOR_PICKER_BUTTONS_PER_ROW; - - onView(allOf(isDescendantOfA(withClassName(containsString(ColorHistoryView.class.getSimpleName()))), - isDescendantOfA(isAssignableFrom(TableLayout.class)), - isDescendantOfA(isAssignableFrom(TableRow.class)), - hasTablePosition(0, colorButtonColPosition))) - .check(matches(withBackgroundColor(color))); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ColorPickerViewInteraction.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ColorPickerViewInteraction.kt new file mode 100644 index 0000000000..e610d12696 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ColorPickerViewInteraction.kt @@ -0,0 +1,173 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.util.wrappers + +import android.widget.TableLayout +import android.widget.TableRow +import androidx.test.espresso.Espresso +import androidx.test.espresso.ViewInteraction +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.withId +import org.catrobat.paintroid.R +import org.catrobat.paintroid.colorpicker.ColorHistoryView +import org.catrobat.paintroid.colorpicker.PresetSelectorView +import org.catrobat.paintroid.test.espresso.util.UiMatcher +import org.hamcrest.Matchers +import org.catrobat.paintroid.test.espresso.util.wrappers.BottomNavigationViewInteraction.Companion.onBottomNavigationView + +class ColorPickerViewInteraction private constructor() : + CustomViewInteraction(Espresso.onView(withId(R.id.color_picker_view))) { + fun onPositiveButton(): ViewInteraction { + return Espresso.onView(withId(android.R.id.button1)) // to avoid following exception when running on emulator: + // Caused by: java.lang.SecurityException: + // Injecting to another application requires INJECT_EVENTS permission + .perform(ViewActions.closeSoftKeyboard()) + } + + fun performOpenColorPicker(): ColorPickerViewInteraction { + onBottomNavigationView() + .onColorClicked() + return this + } + + fun onNegativeButton(): ViewInteraction { + return Espresso.onView(withId(android.R.id.button2)) // to avoid following exception when running on emulator: + // Caused by: java.lang.SecurityException: + // Injecting to another application requires INJECT_EVENTS permission + .perform(ViewActions.closeSoftKeyboard()) + } + + fun clickPipetteButton(): ViewInteraction { + return Espresso.onView(withId(R.id.color_picker_pipette_btn)) + .perform(ViewActions.click()) + } + + fun checkCurrentViewColor(color: Int) { + Espresso.onView(withId(R.id.color_picker_current_color_view)) + .check(ViewAssertions.matches(UiMatcher.withBackgroundColor(color))) + } + + fun checkNewColorViewColor(color: Int) { + Espresso.onView(withId(R.id.color_picker_new_color_view)) + .check(ViewAssertions.matches(UiMatcher.withBackgroundColor(color))) + } + + fun performCloseColorPickerWithDialogButton(): ColorPickerViewInteraction { + check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + onPositiveButton() + .perform(ViewActions.click()) + return this + } + + fun performClickColorPickerPresetSelectorButton(buttonPosition: Int): ColorPickerViewInteraction { + val colorButtonRowPosition = buttonPosition / COLOR_PICKER_BUTTONS_PER_ROW + val colorButtonColPosition = buttonPosition % COLOR_PICKER_BUTTONS_PER_ROW + Espresso.onView( + Matchers.allOf( + ViewMatchers.isDescendantOfA( + ViewMatchers.withClassName( + Matchers.containsString( + PresetSelectorView::class.java.simpleName + ) + ) + ), + ViewMatchers.isDescendantOfA( + ViewMatchers.isAssignableFrom( + TableLayout::class.java + ) + ), + ViewMatchers.isDescendantOfA( + ViewMatchers.isAssignableFrom( + TableRow::class.java + ) + ), + UiMatcher.hasTablePosition(colorButtonRowPosition, colorButtonColPosition) + ) + ) + .perform(ViewActions.closeSoftKeyboard()) + .perform(ViewActions.scrollTo()) + .perform(ViewActions.click()) + return this + } + + fun performClickOnHistoryColor(buttonPosition: Int): ColorPickerViewInteraction { + val colorButtonColPosition = buttonPosition % COLOR_PICKER_BUTTONS_PER_ROW + Espresso.onView( + Matchers.allOf( + ViewMatchers.isDescendantOfA( + ViewMatchers.withClassName( + Matchers.containsString( + ColorHistoryView::class.java.simpleName + ) + ) + ), + ViewMatchers.isDescendantOfA( + ViewMatchers.isAssignableFrom( + TableLayout::class.java + ) + ), + ViewMatchers.isDescendantOfA( + ViewMatchers.isAssignableFrom( + TableRow::class.java + ) + ), + UiMatcher.hasTablePosition(0, colorButtonColPosition) + ) + ) + .perform(ViewActions.closeSoftKeyboard()) + .perform(ViewActions.scrollTo()) + .perform(ViewActions.click()) + return this + } + + fun checkHistoryColor(buttonPosition: Int, color: Int) { + val colorButtonColPosition = buttonPosition % COLOR_PICKER_BUTTONS_PER_ROW + Espresso.onView( + Matchers.allOf( + ViewMatchers.isDescendantOfA( + ViewMatchers.withClassName( + Matchers.containsString( + ColorHistoryView::class.java.simpleName + ) + ) + ), + ViewMatchers.isDescendantOfA( + ViewMatchers.isAssignableFrom( + TableLayout::class.java + ) + ), + ViewMatchers.isDescendantOfA( + ViewMatchers.isAssignableFrom( + TableRow::class.java + ) + ), + UiMatcher.hasTablePosition(0, colorButtonColPosition) + ) + ) + .check(ViewAssertions.matches(UiMatcher.withBackgroundColor(color))) + } + + companion object { + private const val COLOR_PICKER_BUTTONS_PER_ROW = 4 + const val MAXIMUM_COLORS_IN_HISTORY = 4 + fun onColorPickerView(): ColorPickerViewInteraction = ColorPickerViewInteraction() + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ConfirmQuitDialogInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ConfirmQuitDialogInteraction.java deleted file mode 100644 index 35a7a8cbc6..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ConfirmQuitDialogInteraction.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2022 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.util.wrappers; - -import android.widget.Button; - -import org.catrobat.paintroid.R; - -import androidx.test.espresso.ViewAssertion; -import androidx.test.espresso.ViewInteraction; - -import static org.hamcrest.Matchers.allOf; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.matcher.RootMatchers.isDialog; -import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; - -public final class ConfirmQuitDialogInteraction extends CustomViewInteraction { - private ConfirmQuitDialogInteraction() { - super(onView(withText(R.string.closing_security_question)).inRoot(isDialog())); - } - - public static ConfirmQuitDialogInteraction onConfirmQuitDialog() { - return new ConfirmQuitDialogInteraction(); - } - - public ViewInteraction onPositiveButton() { - return onView(allOf(withId(android.R.id.button1), withText(R.string.save_button_text), isAssignableFrom(Button.class))); - } - - public ConfirmQuitDialogInteraction checkPositiveButton(ViewAssertion matcher) { - onPositiveButton() - .check(matcher); - return this; - } - - public ViewInteraction onNegativeButton() { - return onView(allOf(withId(android.R.id.button2), withText(R.string.discard_button_text), isAssignableFrom(Button.class))); - } - - public ConfirmQuitDialogInteraction checkNegativeButton(ViewAssertion matcher) { - onNegativeButton() - .check(matcher); - return this; - } - - public ConfirmQuitDialogInteraction checkNeutralButton(ViewAssertion matcher) { - onView(withId(android.R.id.button3)) - .check(matcher); - return this; - } - - public ConfirmQuitDialogInteraction checkMessage(ViewAssertion matcher) { - onView(withText(R.string.closing_security_question)) - .check(matcher); - return this; - } - - public ConfirmQuitDialogInteraction checkTitle(ViewAssertion matcher) { - onView(withText(R.string.closing_security_question_title)) - .check(matcher); - return this; - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ConfirmQuitDialogInteraction.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ConfirmQuitDialogInteraction.kt new file mode 100644 index 0000000000..9b9fc705b2 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ConfirmQuitDialogInteraction.kt @@ -0,0 +1,93 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.util.wrappers + +import android.widget.Button +import androidx.test.espresso.Espresso +import androidx.test.espresso.ViewAssertion +import androidx.test.espresso.ViewInteraction +import androidx.test.espresso.matcher.RootMatchers +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.withText +import org.catrobat.paintroid.R +import org.hamcrest.Matchers.allOf + +class ConfirmQuitDialogInteraction private constructor() : + CustomViewInteraction( + Espresso.onView(withText(R.string.closing_security_question)) + .inRoot(RootMatchers.isDialog()) + ) { + fun onPositiveButton(): ViewInteraction { + return Espresso.onView( + allOf( + ViewMatchers.withId(android.R.id.button1), + withText(R.string.save_button_text), + ViewMatchers.isAssignableFrom( + Button::class.java + ) + ) + ) + } + + fun checkPositiveButton(matcher: ViewAssertion?): ConfirmQuitDialogInteraction { + onPositiveButton() + .check(matcher) + return this + } + + fun onNegativeButton(): ViewInteraction { + return Espresso.onView( + allOf( + ViewMatchers.withId(android.R.id.button2), + withText(R.string.discard_button_text), + ViewMatchers.isAssignableFrom( + Button::class.java + ) + ) + ) + } + + fun checkNegativeButton(matcher: ViewAssertion?): ConfirmQuitDialogInteraction { + onNegativeButton() + .check(matcher) + return this + } + + fun checkNeutralButton(matcher: ViewAssertion?): ConfirmQuitDialogInteraction { + Espresso.onView(ViewMatchers.withId(android.R.id.button3)) + .check(matcher) + return this + } + + fun checkMessage(matcher: ViewAssertion?): ConfirmQuitDialogInteraction { + Espresso.onView(withText(R.string.closing_security_question)) + .check(matcher) + return this + } + + fun checkTitle(matcher: ViewAssertion?): ConfirmQuitDialogInteraction { + Espresso.onView(withText(R.string.closing_security_question_title)) + .check(matcher) + return this + } + + companion object { + fun onConfirmQuitDialog(): ConfirmQuitDialogInteraction = ConfirmQuitDialogInteraction() + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/CustomViewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/CustomViewInteraction.java deleted file mode 100644 index 044b713959..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/CustomViewInteraction.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2015 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.util.wrappers; - -import org.hamcrest.Matcher; - -import androidx.test.espresso.FailureHandler; -import androidx.test.espresso.Root; -import androidx.test.espresso.ViewAction; -import androidx.test.espresso.ViewAssertion; -import androidx.test.espresso.ViewInteraction; - -public abstract class CustomViewInteraction { - protected ViewInteraction viewInteraction; - - protected CustomViewInteraction(ViewInteraction viewInteraction) { - this.viewInteraction = viewInteraction; - } - - public final ViewInteraction perform(final ViewAction... viewActions) { - return viewInteraction.perform(viewActions); - } - - public final ViewInteraction withFailureHandler(FailureHandler var1) { - return viewInteraction.withFailureHandler(var1); - } - - public final ViewInteraction inRoot(Matcher var1) { - return viewInteraction.inRoot(var1); - } - - public final ViewInteraction noActivity() { - return viewInteraction.noActivity(); - } - - public final ViewInteraction check(final ViewAssertion viewAssert) { - return viewInteraction.check(viewAssert); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/CustomViewInteraction.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/CustomViewInteraction.kt new file mode 100644 index 0000000000..7e4b7891e6 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/CustomViewInteraction.kt @@ -0,0 +1,39 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.util.wrappers + +import androidx.test.espresso.FailureHandler +import androidx.test.espresso.Root +import androidx.test.espresso.ViewAction +import androidx.test.espresso.ViewAssertion +import androidx.test.espresso.ViewInteraction +import org.hamcrest.Matcher + +open class CustomViewInteraction constructor(var viewInteraction: ViewInteraction?) { + + fun perform(vararg viewActions: ViewAction?): ViewInteraction = viewInteraction!!.perform(*viewActions) + + fun withFailureHandler(var1: FailureHandler?): ViewInteraction = viewInteraction!!.withFailureHandler(var1) + + fun inRoot(var1: Matcher?): ViewInteraction = viewInteraction!!.inRoot(var1) + + fun noActivity(): ViewInteraction = viewInteraction!!.noActivity() + + fun check(viewAssert: ViewAssertion?): ViewInteraction = viewInteraction!!.check(viewAssert) +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/DrawingSurfaceInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/DrawingSurfaceInteraction.java deleted file mode 100644 index 3201d1f8a9..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/DrawingSurfaceInteraction.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2015 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.util.wrappers; - -import android.graphics.Bitmap; -import android.view.View; - -import org.catrobat.paintroid.MainActivity; -import org.catrobat.paintroid.R; -import org.catrobat.paintroid.contract.LayerContracts; -import org.hamcrest.Description; -import org.hamcrest.Matcher; -import org.hamcrest.TypeSafeMatcher; - -import androidx.annotation.ColorInt; -import androidx.annotation.ColorRes; -import androidx.core.content.ContextCompat; -import androidx.test.espresso.action.CoordinatesProvider; -import androidx.test.platform.app.InstrumentationRegistry; - -import static org.catrobat.paintroid.test.espresso.util.MainActivityHelper.getMainActivityFromView; -import static org.hamcrest.Matchers.is; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.withId; - -public final class DrawingSurfaceInteraction extends CustomViewInteraction { - - private DrawingSurfaceInteraction() { - super(onView(withId(R.id.pocketpaint_drawing_surface_view))); - } - - public static DrawingSurfaceInteraction onDrawingSurfaceView() { - return new DrawingSurfaceInteraction(); - } - - public DrawingSurfaceInteraction checkPixelColor(@ColorInt final int expectedColor, final CoordinatesProvider coordinateProvider) { - check(matches(new TypeSafeMatcher() { - @Override - public void describeTo(Description description) { - description.appendText("Color at coordinates is " + Integer.toHexString(expectedColor)); - } - - @Override - protected boolean matchesSafely(View view) { - MainActivity activity = getMainActivityFromView(view); - Bitmap currentBitmap = activity.layerModel.getBitmapOfAllLayers(); - float[] coordinates = coordinateProvider.calculateCoordinates(view); - int actualColor = currentBitmap.getPixel((int) coordinates[0], (int) coordinates[1]); - return expectedColor == actualColor; - } - })); - return this; - } - - public DrawingSurfaceInteraction checkPixelColorOnLayer(@ColorInt final int expectedColor, final CoordinatesProvider coordinateProvider) { - check(matches(new TypeSafeMatcher() { - @Override - public void describeTo(Description description) { - description.appendText("Color at coordinates is " + Integer.toHexString(expectedColor)); - } - - @Override - protected boolean matchesSafely(View view) { - MainActivity activity = getMainActivityFromView(view); - Bitmap currentBitmap = activity.layerModel.getCurrentLayer().getBitmap(); - float[] coordinates = coordinateProvider.calculateCoordinates(view); - int actualColor = currentBitmap.getPixel((int) coordinates[0], (int) coordinates[1]); - return expectedColor == actualColor; - } - })); - return this; - } - - public DrawingSurfaceInteraction checkPixelColor(@ColorInt final int expectedColor, final float x, final float y) { - check(matches(new TypeSafeMatcher() { - @Override - public void describeTo(Description description) { - description.appendText("Color at coordinates is " + Integer.toHexString(expectedColor)); - } - - @Override - protected boolean matchesSafely(View view) { - MainActivity activity = getMainActivityFromView(view); - Bitmap currentBitmap = activity.layerModel.getBitmapOfAllLayers(); - int actualColor = currentBitmap.getPixel((int) x, (int) y); - return expectedColor == actualColor; - } - })); - return this; - } - - public DrawingSurfaceInteraction checkPixelColorResource(@ColorRes int expectedColorRes, CoordinatesProvider coordinateProvider) { - int expectedColor = ContextCompat.getColor(InstrumentationRegistry.getInstrumentation().getTargetContext(), expectedColorRes); - return checkPixelColor(expectedColor, coordinateProvider); - } - - public DrawingSurfaceInteraction checkBitmapDimension(final int expectedWidth, final int expectedHeight) { - check(matches(new TypeSafeMatcher() { - @Override - public void describeTo(Description description) { - description.appendText("Bitmap has is size " - + expectedWidth + "x and " - + expectedHeight + "y"); - } - - @Override - protected boolean matchesSafely(View view) { - MainActivity activity = getMainActivityFromView(view); - LayerContracts.Model layerModel = activity.layerModel; - Bitmap bitmap = layerModel.getCurrentLayer().getBitmap(); - return expectedWidth == bitmap.getWidth() && expectedHeight == bitmap.getHeight(); - } - })); - return this; - } - - public DrawingSurfaceInteraction checkLayerDimensions(int expectedWidth, int expectedHeight) { - checkThatLayerDimensions(is(expectedWidth), is(expectedHeight)); - return this; - } - - public DrawingSurfaceInteraction checkThatLayerDimensions(final Matcher matchesWidth, final Matcher matchesHeight) { - check(matches(new TypeSafeMatcher() { - @Override - public void describeTo(Description description) { - description.appendText("All layers have expected size"); - } - - @Override - protected boolean matchesSafely(View view) { - MainActivity activity = getMainActivityFromView(view); - LayerContracts.Model layerModel = activity.layerModel; - for (LayerContracts.Layer layer : layerModel.getLayers()) { - Bitmap bitmap = layer.getBitmap(); - if (!matchesWidth.matches(bitmap.getWidth()) || !matchesHeight.matches(bitmap.getHeight())) { - return false; - } - } - return true; - } - })); - - return this; - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/DrawingSurfaceInteraction.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/DrawingSurfaceInteraction.kt new file mode 100644 index 0000000000..4f16154abc --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/DrawingSurfaceInteraction.kt @@ -0,0 +1,177 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.util.wrappers + +import android.view.View +import androidx.annotation.ColorInt +import androidx.annotation.ColorRes +import androidx.core.content.ContextCompat +import androidx.test.espresso.Espresso +import androidx.test.espresso.action.CoordinatesProvider +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.platform.app.InstrumentationRegistry +import org.catrobat.paintroid.R +import org.catrobat.paintroid.contract.LayerContracts +import org.catrobat.paintroid.test.espresso.util.MainActivityHelper +import org.hamcrest.Description +import org.hamcrest.Matcher +import org.hamcrest.Matchers +import org.hamcrest.TypeSafeMatcher + +class DrawingSurfaceInteraction private constructor() : + CustomViewInteraction(Espresso.onView(withId(R.id.pocketpaint_drawing_surface_view))) { + fun checkPixelColor( + @ColorInt expectedColor: Int, + coordinateProvider: CoordinatesProvider + ): DrawingSurfaceInteraction { + check(ViewAssertions.matches(object : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText( + "Color at coordinates is " + Integer.toHexString( + expectedColor + ) + ) + } + + override fun matchesSafely(view: View?): Boolean { + val activity = MainActivityHelper.getMainActivityFromView(view!!) + val currentBitmap = activity.layerModel.getBitmapOfAllLayers() + val coordinates = coordinateProvider.calculateCoordinates(view) + val actualColor = + currentBitmap!!.getPixel(coordinates[0].toInt(), coordinates[1].toInt()) + return expectedColor == actualColor + } + })) + return this + } + + fun checkPixelColorOnLayer( + @ColorInt expectedColor: Int, + coordinateProvider: CoordinatesProvider + ): DrawingSurfaceInteraction { + check(ViewAssertions.matches(object : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText( + "Color at coordinates is " + Integer.toHexString( + expectedColor + ) + ) + } + + override fun matchesSafely(view: View?): Boolean { + val activity = MainActivityHelper.getMainActivityFromView(view!!) + val currentBitmap = activity.layerModel.currentLayer!!.bitmap + val coordinates = coordinateProvider.calculateCoordinates(view) + val actualColor = + currentBitmap.getPixel(coordinates[0].toInt(), coordinates[1].toInt()) + return expectedColor == actualColor + } + })) + return this + } + + fun checkPixelColor( + @ColorInt expectedColor: Int, + x: Float, + y: Float + ): DrawingSurfaceInteraction { + check(ViewAssertions.matches(object : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText( + "Color at coordinates is " + Integer.toHexString( + expectedColor + ) + ) + } + + override fun matchesSafely(view: View?): Boolean { + val activity = MainActivityHelper.getMainActivityFromView(view!!) + val currentBitmap = activity.layerModel.getBitmapOfAllLayers() + val actualColor = currentBitmap!!.getPixel(x.toInt(), y.toInt()) + return expectedColor == actualColor + } + })) + return this + } + + fun checkPixelColorResource( + @ColorRes expectedColorRes: Int, + coordinateProvider: CoordinatesProvider + ): DrawingSurfaceInteraction { + val expectedColor = ContextCompat.getColor( + InstrumentationRegistry.getInstrumentation().targetContext, + expectedColorRes + ) + return checkPixelColor(expectedColor, coordinateProvider) + } + + fun checkBitmapDimension(expectedWidth: Int, expectedHeight: Int): DrawingSurfaceInteraction { + check(ViewAssertions.matches(object : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText( + "Bitmap has is size " + + expectedWidth + "x and " + + expectedHeight + "y" + ) + } + + override fun matchesSafely(view: View?): Boolean { + val activity = MainActivityHelper.getMainActivityFromView(view!!) + val layerModel = activity.layerModel + val bitmap = layerModel.currentLayer!!.bitmap + return expectedWidth == bitmap.width && expectedHeight == bitmap.height + } + })) + return this + } + + fun checkLayerDimensions(expectedWidth: Int, expectedHeight: Int): DrawingSurfaceInteraction { + checkThatLayerDimensions(Matchers.`is`(expectedWidth), Matchers.`is`(expectedHeight)) + return this + } + + fun checkThatLayerDimensions( + matchesWidth: Matcher, + matchesHeight: Matcher + ): DrawingSurfaceInteraction { + check(ViewAssertions.matches(object : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText("All layers have expected size") + } + + override fun matchesSafely(view: View?): Boolean { + val activity = MainActivityHelper.getMainActivityFromView(view!!) + val layerModel = activity.layerModel + for (layer: LayerContracts.Layer in layerModel.layers) { + val bitmap = layer.bitmap + if (!matchesWidth.matches(bitmap.width) || !matchesHeight.matches(bitmap.height)) { + return false + } + } + return true + } + })) + return this + } + + companion object { + fun onDrawingSurfaceView(): DrawingSurfaceInteraction = DrawingSurfaceInteraction() + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/LayerMenuViewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/LayerMenuViewInteraction.java deleted file mode 100644 index e6a85953cd..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/LayerMenuViewInteraction.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2015 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.util.wrappers; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.drawable.Drawable; -import android.view.Gravity; -import android.view.View; -import android.widget.ImageView; - -import org.catrobat.paintroid.R; -import org.catrobat.paintroid.test.utils.RecyclerViewMatcher; -import org.hamcrest.Description; -import org.hamcrest.Matcher; -import org.hamcrest.TypeSafeMatcher; - -import static org.catrobat.paintroid.test.espresso.util.UiInteractions.assertRecyclerViewCount; -import static org.catrobat.paintroid.test.espresso.util.UiInteractions.setProgress; -import static org.catrobat.paintroid.test.espresso.util.wrappers.BottomNavigationViewInteraction.onBottomNavigationView; - -import androidx.annotation.ColorInt; -import androidx.test.espresso.ViewInteraction; -import androidx.test.espresso.contrib.DrawerActions; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.withId; - -public final class LayerMenuViewInteraction extends CustomViewInteraction { - private LayerMenuViewInteraction() { - super(onView(withId(R.id.pocketpaint_nav_view_layer))); - } - - public static LayerMenuViewInteraction onLayerMenuView() { - return new LayerMenuViewInteraction(); - } - - public ViewInteraction onButtonAdd() { - return onView(withId(R.id.pocketpaint_layer_side_nav_button_add)); - } - - public ViewInteraction onButtonDelete() { - return onView(withId(R.id.pocketpaint_layer_side_nav_button_delete)); - } - - public ViewInteraction onLayerList() { - return onView(withId(R.id.pocketpaint_layer_side_nav_list)); - } - - public LayerMenuViewInteraction checkLayerCount(int count) { - onLayerList() - .check(assertRecyclerViewCount(count)); - return this; - } - - public static RecyclerViewMatcher withRecyclerView(final int recyclerViewId) { - return new RecyclerViewMatcher(recyclerViewId); - } - - public LayerMenuViewInteraction performOpen() { - onBottomNavigationView() - .onLayersClicked(); - check(matches(isDisplayed())); - return this; - } - - public LayerMenuViewInteraction performSetOpacityTo(int opacityPercentage, int listPosition) { - onView(withRecyclerView(R.id.pocketpaint_layer_side_nav_list) - .atPositionOnView(listPosition, R.id.pocketpaint_layer_opacity_seekbar)) - .perform(setProgress(opacityPercentage)); - return this; - } - - public LayerMenuViewInteraction performClose() { - check(matches(isDisplayed())); - onView(withId(R.id.pocketpaint_drawer_layout)) - .perform(DrawerActions.close(Gravity.END)); - return this; - } - - public LayerMenuViewInteraction performSelectLayer(int listPosition) { - check(matches(isDisplayed())); - onView(withRecyclerView(R.id.pocketpaint_layer_side_nav_list) - .atPositionOnView(listPosition, R.id.pocketpaint_layer_preview_container)) - .perform(click()); - return this; - } - - public LayerMenuViewInteraction performScrollToPositionInLayerNavigation(int listPosition) { - check(matches(isDisplayed())); - onView(withId(R.id.pocketpaint_layer_side_nav_list)) - .perform(androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition(listPosition)); - return this; - } - - public LayerMenuViewInteraction performStartDragging(int listPosition) { - check(matches(isDisplayed())); - onView(withIndex(withId(org.catrobat.paintroid.R.id.pocketpaint_layer_drag_handle), listPosition)).perform(click()); - return this; - } - - public LayerMenuViewInteraction performAddLayer() { - check(matches(isDisplayed())); - onButtonAdd() - .perform(click()); - return this; - } - - public LayerMenuViewInteraction performDeleteLayer() { - check(matches(isDisplayed())); - onButtonDelete() - .perform(click()); - return this; - } - - public LayerMenuViewInteraction performToggleLayerVisibility(int position) { - check(matches(isDisplayed())); - onView(withIndex(withId(R.id.pocketpaint_checkbox_layer), position)).perform(click()); - return this; - } - - public static Matcher withIndex(final Matcher matcher, final int index) { - return new TypeSafeMatcher<>() { - int currentIndex = 0; - - @Override - public void describeTo(Description description) { - description.appendText("with index: "); - description.appendValue(index); - matcher.describeTo(description); - } - - @Override - public boolean matchesSafely(View view) { - return matcher.matches(view) && currentIndex++ == index; - } - }; - } - - public LayerMenuViewInteraction checkLayerAtPositionHasTopLeftPixelWithColor(int listPosition, @ColorInt final int expectedColor) { - onView(withIndex(withId(org.catrobat.paintroid.R.id.pocketpaint_item_layer_image), listPosition)) - .check(matches(new org.hamcrest.TypeSafeMatcher() { - @Override - public void describeTo(Description description) { - description.appendText("Color at coordinates is " + Integer.toHexString(expectedColor)); - } - - @Override - protected boolean matchesSafely(View view) { - Bitmap bitmap = getBitmap(((ImageView) view).getDrawable()); - int actualColor = bitmap.getPixel(0, 0); - return actualColor == expectedColor; - } - })); - return this; - } - - private Bitmap getBitmap(Drawable drawable) { - Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - drawable.draw(canvas); - return bitmap; - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/LayerMenuViewInteraction.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/LayerMenuViewInteraction.kt new file mode 100644 index 0000000000..133c05db90 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/LayerMenuViewInteraction.kt @@ -0,0 +1,162 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.util.wrappers + +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.drawable.Drawable +import android.view.Gravity +import android.view.View +import android.widget.ImageView +import androidx.annotation.ColorInt +import androidx.recyclerview.widget.RecyclerView +import androidx.test.espresso.Espresso +import androidx.test.espresso.ViewInteraction +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.contrib.DrawerActions +import androidx.test.espresso.contrib.RecyclerViewActions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.withId +import org.catrobat.paintroid.R +import org.catrobat.paintroid.test.espresso.util.UiInteractions +import org.catrobat.paintroid.test.espresso.util.wrappers.BottomNavigationViewInteraction.Companion.onBottomNavigationView +import org.catrobat.paintroid.test.utils.RecyclerViewMatcher +import org.hamcrest.Description +import org.hamcrest.Matcher +import org.hamcrest.TypeSafeMatcher + +class LayerMenuViewInteraction private constructor() : CustomViewInteraction(Espresso.onView(withId(R.id.pocketpaint_nav_view_layer))) { + fun onButtonAdd(): ViewInteraction = Espresso.onView(withId(R.id.pocketpaint_layer_side_nav_button_add)) + + fun onButtonDelete(): ViewInteraction = Espresso.onView(withId(R.id.pocketpaint_layer_side_nav_button_delete)) + + fun onLayerList(): ViewInteraction = Espresso.onView(withId(R.id.pocketpaint_layer_side_nav_list)) + + fun checkLayerCount(count: Int): LayerMenuViewInteraction { + onLayerList() + .check(UiInteractions.assertRecyclerViewCount(count)) + return this + } + + fun performOpen(): LayerMenuViewInteraction { + onBottomNavigationView() + .onLayersClicked() + check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + return this + } + + fun performSetOpacityTo(opacityPercentage: Int, listPosition: Int): LayerMenuViewInteraction { + Espresso.onView(withRecyclerView(R.id.pocketpaint_layer_side_nav_list) + .atPositionOnView(listPosition, R.id.pocketpaint_layer_opacity_seekbar)) + .perform(UiInteractions.setProgress(opacityPercentage)) + return this + } + + fun performClose(): LayerMenuViewInteraction { + check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.pocketpaint_drawer_layout)) + .perform(DrawerActions.close(Gravity.END)) + return this + } + + fun performSelectLayer(listPosition: Int): LayerMenuViewInteraction { + check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withRecyclerView(R.id.pocketpaint_layer_side_nav_list) + .atPositionOnView(listPosition, R.id.pocketpaint_layer_preview_container)) + .perform(ViewActions.click()) + return this + } + + fun performScrollToPositionInLayerNavigation(listPosition: Int): LayerMenuViewInteraction { + check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withId(R.id.pocketpaint_layer_side_nav_list)) + .perform(RecyclerViewActions.scrollToPosition(listPosition)) + return this + } + + fun performStartDragging(listPosition: Int): LayerMenuViewInteraction { + check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withIndex(withId(R.id.pocketpaint_layer_drag_handle), listPosition)).perform(ViewActions.click()) + return this + } + + fun performAddLayer(): LayerMenuViewInteraction { + check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + onButtonAdd() + .perform(ViewActions.click()) + return this + } + + fun performDeleteLayer(): LayerMenuViewInteraction { + check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + onButtonDelete() + .perform(ViewActions.click()) + return this + } + + fun performToggleLayerVisibility(position: Int): LayerMenuViewInteraction { + check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(withIndex(withId(R.id.pocketpaint_checkbox_layer), position)).perform(ViewActions.click()) + return this + } + + fun checkLayerAtPositionHasTopLeftPixelWithColor(listPosition: Int, @ColorInt expectedColor: Int): LayerMenuViewInteraction { + Espresso.onView(withIndex(withId(R.id.pocketpaint_item_layer_image), listPosition)) + .check(ViewAssertions.matches(object : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText("Color at coordinates is " + Integer.toHexString(expectedColor)) + } + + override fun matchesSafely(view: View): Boolean { + val bitmap = getBitmap((view as ImageView).drawable) + val actualColor = bitmap.getPixel(0, 0) + return actualColor == expectedColor + } + })) + return this + } + + private fun getBitmap(drawable: Drawable): Bitmap { + val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888) + val canvas = Canvas(bitmap) + drawable.setBounds(0, 0, canvas.width, canvas.height) + drawable.draw(canvas) + return bitmap + } + + companion object { + fun onLayerMenuView(): LayerMenuViewInteraction = LayerMenuViewInteraction() + + fun withRecyclerView(recyclerViewId: Int): RecyclerViewMatcher = RecyclerViewMatcher(recyclerViewId) + + fun withIndex(matcher: Matcher, index: Int): TypeSafeMatcher { + return object : TypeSafeMatcher() { + var currentIndex = 0 + override fun describeTo(description: Description) { + description.appendText("with index: ") + description.appendValue(index) + matcher.describeTo(description) + } + + public override fun matchesSafely(view: View?): Boolean = matcher.matches(view) && currentIndex++ == index + } + } + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/MainActivityImplementationExeption.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/MainActivityImplementationExeption.kt new file mode 100644 index 0000000000..11995d23aa --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/MainActivityImplementationExeption.kt @@ -0,0 +1,5 @@ +package org.catrobat.paintroid.test.espresso.util.wrappers + +class MainActivityImplementationExeption(s: String) : Exception() { + val expetion = s +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/OptionsMenuViewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/OptionsMenuViewInteraction.java deleted file mode 100644 index 21ebb418da..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/OptionsMenuViewInteraction.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.catrobat.paintroid.test.espresso.util.wrappers; - -import android.view.View; - -import org.hamcrest.CoreMatchers; - -import androidx.annotation.StringRes; -import androidx.appcompat.widget.MenuPopupWindow; -import androidx.test.espresso.ViewInteraction; - -import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withAdaptedData; -import static org.hamcrest.Matchers.not; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.assertion.ViewAssertions.matches; - -public final class OptionsMenuViewInteraction { - static ViewInteraction optionsMenu; - - private OptionsMenuViewInteraction() { - optionsMenu = onView(CoreMatchers.instanceOf(MenuPopupWindow.MenuDropDownListView.class)); - } - - public static OptionsMenuViewInteraction onOptionsMenu() { - return new OptionsMenuViewInteraction(); - } - - public OptionsMenuViewInteraction checkItemExists(@StringRes int item) { - optionsMenu.check(matches(withAdaptedData(item))); - - return this; - } - - public OptionsMenuViewInteraction checkItemDoesNotExist(@StringRes int item) { - optionsMenu.check(matches(not(withAdaptedData(item)))); - return this; - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/OptionsMenuViewInteraction.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/OptionsMenuViewInteraction.kt new file mode 100644 index 0000000000..f683fedf96 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/OptionsMenuViewInteraction.kt @@ -0,0 +1,35 @@ +package org.catrobat.paintroid.test.espresso.util.wrappers + +import androidx.annotation.StringRes +import androidx.appcompat.widget.MenuPopupWindow.MenuDropDownListView +import androidx.test.espresso.Espresso +import androidx.test.espresso.ViewInteraction +import androidx.test.espresso.assertion.ViewAssertions +import org.catrobat.paintroid.test.espresso.util.UiMatcher +import org.hamcrest.CoreMatchers +import org.hamcrest.Matchers + +class OptionsMenuViewInteraction private constructor() { + init { + optionsMenu = Espresso.onView( + CoreMatchers.instanceOf( + MenuDropDownListView::class.java + ) + ) + } + + fun checkItemExists(@StringRes item: Int): OptionsMenuViewInteraction { + optionsMenu.check(ViewAssertions.matches(UiMatcher.withAdaptedData(item))) + return this + } + + fun checkItemDoesNotExist(@StringRes item: Int): OptionsMenuViewInteraction { + optionsMenu.check(ViewAssertions.matches(Matchers.not(UiMatcher.withAdaptedData(item)))) + return this + } + + companion object { + lateinit var optionsMenu: ViewInteraction + fun onOptionsMenu(): OptionsMenuViewInteraction = OptionsMenuViewInteraction() + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ShapeToolOptionsViewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ShapeToolOptionsViewInteraction.java deleted file mode 100644 index d4e13a4f2a..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ShapeToolOptionsViewInteraction.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2015 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.util.wrappers; - -import org.catrobat.paintroid.R; -import org.catrobat.paintroid.tools.drawable.DrawableShape; -import org.catrobat.paintroid.tools.drawable.DrawableStyle; - -import androidx.test.espresso.ViewAction; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.matcher.ViewMatchers.withId; - -public final class ShapeToolOptionsViewInteraction extends CustomViewInteraction { - private ShapeToolOptionsViewInteraction() { - super(onView(withId(R.id.pocketpaint_layout_tool_options))); - } - - public static ShapeToolOptionsViewInteraction onShapeToolOptionsView() { - return new ShapeToolOptionsViewInteraction(); - } - - private int getButtonIdFromBaseShape(DrawableShape baseShape) { - switch (baseShape) { - case RECTANGLE: - return R.id.pocketpaint_shapes_square_btn; - case OVAL: - return R.id.pocketpaint_shapes_circle_btn; - case HEART: - return R.id.pocketpaint_shapes_heart_btn; - case STAR: - return R.id.pocketpaint_shapes_star_btn; - } - throw new IllegalArgumentException(); - } - - private int getButtonIdFromShapeDrawType(DrawableStyle shapeDrawType) { - switch (shapeDrawType) { - case STROKE: - return R.id.pocketpaint_shape_ibtn_outline; - case FILL: - return R.id.pocketpaint_shape_ibtn_fill; - } - throw new IllegalArgumentException(); - } - - public ShapeToolOptionsViewInteraction performSelectShape(DrawableShape shape) { - onView(withId(getButtonIdFromBaseShape(shape))) - .perform(click()); - return this; - } - - public ShapeToolOptionsViewInteraction performSelectShapeDrawType(DrawableStyle shapeDrawType) { - onView(withId(getButtonIdFromShapeDrawType(shapeDrawType))) - .perform(click()); - return this; - } - - public void performSetOutlineWidth(ViewAction setWidth) { - onView(withId(R.id.pocketpaint_shape_stroke_width_seek_bar)) - .perform(setWidth); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ShapeToolOptionsViewInteraction.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ShapeToolOptionsViewInteraction.kt new file mode 100644 index 0000000000..85dd0e3dd1 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ShapeToolOptionsViewInteraction.kt @@ -0,0 +1,68 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.util.wrappers + +import androidx.test.espresso.Espresso +import androidx.test.espresso.ViewAction +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.withId +import org.catrobat.paintroid.R +import org.catrobat.paintroid.tools.drawable.DrawableShape +import org.catrobat.paintroid.tools.drawable.DrawableStyle + +class ShapeToolOptionsViewInteraction private constructor() : + CustomViewInteraction(Espresso.onView(withId(R.id.pocketpaint_layout_tool_options))) { + private fun getButtonIdFromBaseShape(baseShape: DrawableShape): Int { + return when (baseShape) { + DrawableShape.RECTANGLE -> R.id.pocketpaint_shapes_square_btn + DrawableShape.OVAL -> R.id.pocketpaint_shapes_circle_btn + DrawableShape.HEART -> R.id.pocketpaint_shapes_heart_btn + DrawableShape.STAR -> R.id.pocketpaint_shapes_star_btn + } + } + + private fun getButtonIdFromShapeDrawType(shapeDrawType: DrawableStyle): Int { + return when (shapeDrawType) { + DrawableStyle.STROKE -> R.id.pocketpaint_shape_ibtn_outline + DrawableStyle.FILL -> R.id.pocketpaint_shape_ibtn_fill + } + } + + fun performSelectShape(shape: DrawableShape): ShapeToolOptionsViewInteraction { + Espresso.onView(ViewMatchers.withId(getButtonIdFromBaseShape(shape))) + .perform(ViewActions.click()) + return this + } + + fun performSelectShapeDrawType(shapeDrawType: DrawableStyle): ShapeToolOptionsViewInteraction { + Espresso.onView(ViewMatchers.withId(getButtonIdFromShapeDrawType(shapeDrawType))) + .perform(ViewActions.click()) + return this + } + + fun performSetOutlineWidth(setWidth: ViewAction?) { + Espresso.onView(withId(R.id.pocketpaint_shape_stroke_width_seek_bar)) + .perform(setWidth) + } + + companion object { + fun onShapeToolOptionsView(): ShapeToolOptionsViewInteraction = ShapeToolOptionsViewInteraction() + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ToolBarViewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ToolBarViewInteraction.java deleted file mode 100644 index a82743a220..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ToolBarViewInteraction.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2015 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.util.wrappers; - -import org.catrobat.paintroid.R; -import org.catrobat.paintroid.test.espresso.util.EspressoUtils; -import org.catrobat.paintroid.tools.ToolType; - -import static org.catrobat.paintroid.test.espresso.util.wrappers.BottomNavigationViewInteraction.onBottomNavigationView; -import static org.hamcrest.Matchers.not; - -import androidx.test.espresso.ViewInteraction; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.withId; - -public final class ToolBarViewInteraction extends CustomViewInteraction { - - private ToolBarViewInteraction() { - super(onView(withId(R.id.pocketpaint_toolbar))); - } - - public static ToolBarViewInteraction onToolBarView() { - return new ToolBarViewInteraction(); - } - - public ViewInteraction onSelectedToolButton() { - return onView(withId(getCurrentToolType().getToolButtonID())); - } - - public ViewInteraction onToolOptionsView() { - return onView(withId(R.id.pocketpaint_layout_tool_specific_options)); - } - - public ToolBarViewInteraction performClickSelectedToolButton() { - onBottomNavigationView() - .onToolsClicked(); - onSelectedToolButton() - .perform(click()); - return this; - } - - public ToolBarViewInteraction onToolsClicked() { - onBottomNavigationView() - .onToolsClicked(); - return this; - } - - public ToolBarViewInteraction performSelectTool(ToolType toolType) { - if (getCurrentToolType() != toolType) { - onBottomNavigationView() - .onToolsClicked(); - onView(withId(toolType.getToolButtonID())) - .perform(click()); - } - return this; - } - - private ToolType getCurrentToolType() { - return EspressoUtils.INSTANCE.getMainActivity().toolReference.getTool().getToolType(); - } - - public ToolBarViewInteraction performOpenToolOptionsView() { - onToolOptionsView() - .check(matches(not(isDisplayed()))); - onBottomNavigationView() - .onCurrentClicked(); - return this; - } - - public ToolBarViewInteraction performCloseToolOptionsView() { - onToolOptionsView() - .check(matches(isDisplayed())); - onBottomNavigationView() - .onCurrentClicked(); - return this; - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ToolBarViewInteraction.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ToolBarViewInteraction.kt new file mode 100644 index 0000000000..21cbcf7523 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ToolBarViewInteraction.kt @@ -0,0 +1,85 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.util.wrappers + +import androidx.test.espresso.Espresso +import androidx.test.espresso.ViewInteraction +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.withId +import org.catrobat.paintroid.R +import org.catrobat.paintroid.test.espresso.util.EspressoUtils +import org.catrobat.paintroid.test.espresso.util.wrappers.BottomNavigationViewInteraction.Companion.onBottomNavigationView +import org.catrobat.paintroid.tools.ToolType +import org.hamcrest.Matchers + +class ToolBarViewInteraction : + CustomViewInteraction(Espresso.onView(withId(R.id.pocketpaint_toolbar))) { + fun onSelectedToolButton(): ViewInteraction = Espresso.onView(withId(currentToolType.toolButtonID)) + + fun onToolOptionsView(): ViewInteraction = Espresso.onView(withId(R.id.pocketpaint_layout_tool_specific_options)) + + fun performClickSelectedToolButton(): ToolBarViewInteraction { + onBottomNavigationView() + .onToolsClicked() + onSelectedToolButton() + .perform(ViewActions.click()) + return this + } + + fun onToolsClicked(): ToolBarViewInteraction { + onBottomNavigationView() + .onToolsClicked() + return this + } + + fun performSelectTool(toolType: ToolType): ToolBarViewInteraction { + if (currentToolType !== toolType) { + onBottomNavigationView() + .onToolsClicked() + Espresso.onView(withId(toolType.toolButtonID)) + .perform(ViewActions.click()) + } + return this + } + + private val currentToolType: ToolType + get() = EspressoUtils.mainActivity.toolReference.tool!!.toolType + + fun performOpenToolOptionsView(): ToolBarViewInteraction { + onToolOptionsView() + .check(ViewAssertions.matches(Matchers.not(ViewMatchers.isDisplayed()))) + onBottomNavigationView() + .onCurrentClicked() + return this + } + + fun performCloseToolOptionsView(): ToolBarViewInteraction { + onToolOptionsView() + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + onBottomNavigationView() + .onCurrentClicked() + return this + } + + companion object { + fun onToolBarView(): ToolBarViewInteraction = ToolBarViewInteraction() + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ToolPropertiesInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ToolPropertiesInteraction.java deleted file mode 100644 index e069b54f6a..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ToolPropertiesInteraction.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2022 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.util.wrappers; - -import android.content.Context; -import android.graphics.Paint; -import android.graphics.Paint.Cap; - -import org.catrobat.paintroid.R; -import org.catrobat.paintroid.test.espresso.util.EspressoUtils; -import org.catrobat.paintroid.tools.Tool; - -import androidx.annotation.ColorInt; -import androidx.annotation.ColorRes; -import androidx.core.content.ContextCompat; -import androidx.test.platform.app.InstrumentationRegistry; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; - -public final class ToolPropertiesInteraction extends CustomViewInteraction { - private ToolPropertiesInteraction() { - super(null); - } - public static ToolPropertiesInteraction onToolProperties() { - return new ToolPropertiesInteraction(); - } - - public ToolPropertiesInteraction checkMatchesColor(@ColorInt int expectedColor) { - assertEquals(expectedColor, getCurrentTool().getDrawPaint().getColor()); - return this; - } - - public ToolPropertiesInteraction checkDoesNotMatchColor(@ColorInt int color) { - assertNotEquals(color, getCurrentTool().getDrawPaint().getColor()); - return this; - } - - public ToolPropertiesInteraction checkMatchesColorResource(@ColorRes int expectedColorRes) { - int expectedColor = ContextCompat.getColor(InstrumentationRegistry.getInstrumentation().getTargetContext(), expectedColorRes); - return checkMatchesColor(expectedColor); - } - - public ToolPropertiesInteraction checkCap(Cap expectedCap) { - Paint strokePaint = getCurrentTool().getDrawPaint(); - assertEquals(expectedCap, strokePaint.getStrokeCap()); - return this; - } - - public ToolPropertiesInteraction setCap(Cap expectedCap) { - getCurrentTool().changePaintStrokeCap(expectedCap); - return this; - } - - public ToolPropertiesInteraction checkStrokeWidth(float expectedStrokeWidth) { - Paint strokePaint = getCurrentTool().getDrawPaint(); - assertEquals(expectedStrokeWidth, strokePaint.getStrokeWidth(), Float.MIN_VALUE); - return this; - } - - public ToolPropertiesInteraction setStrokeWidth(float expectedStrokeWidth) { - getCurrentTool().changePaintStrokeWidth((int) expectedStrokeWidth); - return this; - } - - public ToolPropertiesInteraction setColor(int color) { - getCurrentTool().changePaintColor(color); - return this; - } - - public Tool getCurrentTool() { - return EspressoUtils.INSTANCE.getMainActivity().toolReference.getTool(); - } - - public ToolPropertiesInteraction setColorResource(@ColorRes int colorResource) { - int color = ContextCompat.getColor(InstrumentationRegistry.getInstrumentation().getTargetContext(), colorResource); - return setColor(color); - } - - public ToolPropertiesInteraction setColorPreset(int colorPresetPosition) { - Context targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - int[] presetColors = targetContext.getResources().getIntArray(R.array.pocketpaint_color_picker_preset_colors); - return setColor(presetColors[colorPresetPosition]); - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ToolPropertiesInteraction.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ToolPropertiesInteraction.kt new file mode 100644 index 0000000000..d10fcdb484 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ToolPropertiesInteraction.kt @@ -0,0 +1,92 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.paintroid.test.espresso.util.wrappers + +import android.graphics.Paint.Cap +import androidx.annotation.ColorInt +import androidx.annotation.ColorRes +import androidx.core.content.ContextCompat +import androidx.test.platform.app.InstrumentationRegistry +import org.catrobat.paintroid.test.espresso.util.EspressoUtils.mainActivity +import org.catrobat.paintroid.tools.Tool +import org.junit.Assert + +class ToolPropertiesInteraction private constructor() : CustomViewInteraction(null) { + fun checkMatchesColor(@ColorInt expectedColor: Int): ToolPropertiesInteraction { + Assert.assertEquals(expectedColor.toLong(), currentTool.drawPaint.color.toLong()) + return this + } + + fun checkDoesNotMatchColor(@ColorInt color: Int): ToolPropertiesInteraction { + Assert.assertNotEquals(color.toLong(), currentTool.drawPaint.color.toLong()) + return this + } + + fun checkMatchesColorResource(@ColorRes expectedColorRes: Int): ToolPropertiesInteraction { + val expectedColor = ContextCompat.getColor( + InstrumentationRegistry.getInstrumentation().targetContext, + expectedColorRes + ) + return checkMatchesColor(expectedColor) + } + + fun checkCap(expectedCap: Cap?): ToolPropertiesInteraction { + val strokePaint = currentTool.drawPaint + Assert.assertEquals(expectedCap, strokePaint.strokeCap) + return this + } + + fun setCap(expectedCap: Cap?): ToolPropertiesInteraction { + currentTool.changePaintStrokeCap(expectedCap!!) + return this + } + + fun checkStrokeWidth(expectedStrokeWidth: Float): ToolPropertiesInteraction { + val strokePaint = currentTool.drawPaint + Assert.assertEquals(expectedStrokeWidth, strokePaint.strokeWidth, Float.MIN_VALUE) + return this + } + + fun setStrokeWidth(expectedStrokeWidth: Float): ToolPropertiesInteraction { + currentTool.changePaintStrokeWidth(expectedStrokeWidth.toInt()) + return this + } + + fun setColor(color: Int): ToolPropertiesInteraction { + currentTool.changePaintColor(color) + return this + } + + private val currentTool: Tool + get() = mainActivity.toolReference.tool as Tool + + fun setColorResource(@ColorRes colorResource: Int): ToolPropertiesInteraction { + val color = ContextCompat.getColor( + InstrumentationRegistry.getInstrumentation().targetContext, + colorResource + ) + return setColor(color) + } + + companion object { + @JvmStatic + fun onToolProperties(): ToolPropertiesInteraction = ToolPropertiesInteraction() + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/TopBarViewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/TopBarViewInteraction.java deleted file mode 100644 index d48d78ca6f..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/TopBarViewInteraction.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2015 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.util.wrappers; - -import org.catrobat.paintroid.R; - -import androidx.test.espresso.Espresso; -import androidx.test.espresso.ViewInteraction; -import androidx.test.platform.app.InstrumentationRegistry; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.matcher.ViewMatchers.withId; - -public final class TopBarViewInteraction extends CustomViewInteraction { - private TopBarViewInteraction() { - super(onView(withId(R.id.pocketpaint_layout_top_bar))); - } - - public static TopBarViewInteraction onTopBarView() { - return new TopBarViewInteraction(); - } - - public ViewInteraction onUndoButton() { - return onView(withId(R.id.pocketpaint_btn_top_undo)); - } - - public ViewInteraction onRedoButton() { - return onView(withId(R.id.pocketpaint_btn_top_redo)); - } - - public ViewInteraction onCheckmarkButton() { - return onView(withId(R.id.pocketpaint_btn_top_checkmark)); - } - - public ViewInteraction onPlusButton() { - return onView(withId(R.id.pocketpaint_btn_top_plus)); - } - - public TopBarViewInteraction performUndo() { - onUndoButton() - .perform(click()); - return this; - } - - public TopBarViewInteraction performRedo() { - onRedoButton() - .perform(click()); - return this; - } - - public TopBarViewInteraction performClickCheckmark() { - onCheckmarkButton() - .perform(click()); - return this; - } - - public TopBarViewInteraction performClickPlus() { - onPlusButton() - .perform(click()); - return this; - } - - public TopBarViewInteraction performOpenMoreOptions() { - openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getInstrumentation().getTargetContext()); - return this; - } - - public TopBarViewInteraction onHomeClicked() { - Espresso.pressBack(); - return this; - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/TopBarViewInteraction.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/TopBarViewInteraction.kt new file mode 100644 index 0000000000..a1d965c205 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/TopBarViewInteraction.kt @@ -0,0 +1,74 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.util.wrappers + +import androidx.test.espresso.Espresso +import androidx.test.espresso.ViewInteraction +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.platform.app.InstrumentationRegistry +import org.catrobat.paintroid.R + +class TopBarViewInteraction : CustomViewInteraction(Espresso.onView(withId(R.id.pocketpaint_layout_top_bar))) { + fun onUndoButton(): ViewInteraction = Espresso.onView(withId(R.id.pocketpaint_btn_top_undo)) + + fun onRedoButton(): ViewInteraction = Espresso.onView(withId(R.id.pocketpaint_btn_top_redo)) + + fun onCheckmarkButton(): ViewInteraction = Espresso.onView(withId(R.id.pocketpaint_btn_top_checkmark)) + + fun onPlusButton(): ViewInteraction = Espresso.onView(withId(R.id.pocketpaint_btn_top_plus)) + + fun performUndo(): TopBarViewInteraction { + onUndoButton() + .perform(ViewActions.click()) + return this + } + + fun performRedo(): TopBarViewInteraction { + onRedoButton() + .perform(ViewActions.click()) + return this + } + + fun performClickCheckmark(): TopBarViewInteraction { + onCheckmarkButton() + .perform(ViewActions.click()) + return this + } + + fun performClickPlus(): TopBarViewInteraction { + onPlusButton() + .perform(ViewActions.click()) + return this + } + + fun performOpenMoreOptions(): TopBarViewInteraction { + Espresso.openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getInstrumentation().targetContext) + return this + } + + fun onHomeClicked(): TopBarViewInteraction { + Espresso.pressBack() + return this + } + + companion object { + fun onTopBarView(): TopBarViewInteraction = TopBarViewInteraction() + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/TransformToolOptionsViewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/TransformToolOptionsViewInteraction.java deleted file mode 100644 index 4acfd01a18..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/TransformToolOptionsViewInteraction.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2015 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.test.espresso.util.wrappers; - -import android.view.View; -import android.widget.EditText; -import android.widget.TextView; - -import org.catrobat.paintroid.R; -import org.hamcrest.Description; -import org.hamcrest.Matcher; -import org.hamcrest.TypeSafeMatcher; - -import static org.catrobat.paintroid.test.espresso.util.UiInteractions.setProgress; -import static org.hamcrest.Matchers.not; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.action.ViewActions.replaceText; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; - -public final class TransformToolOptionsViewInteraction extends CustomViewInteraction { - private TransformToolOptionsViewInteraction() { - super(onView(withId(R.id.pocketpaint_layout_tool_options))); - } - - public static TransformToolOptionsViewInteraction onTransformToolOptionsView() { - return new TransformToolOptionsViewInteraction(); - } - - public TransformToolOptionsViewInteraction performSetCenterClick() { - onView(withId(R.id.pocketpaint_transform_set_center_btn)) - .perform(click()); - return this; - } - - public TransformToolOptionsViewInteraction performAutoCrop() { - onView(withId(R.id.pocketpaint_transform_auto_crop_btn)) - .perform(click()); - return this; - } - - public TransformToolOptionsViewInteraction checkAutoDisplayed() { - onView(withText(R.string.transform_auto_crop_text)) - .check(matches(isDisplayed())); - return this; - } - - public TransformToolOptionsViewInteraction performRotateClockwise() { - onView(withId(R.id.pocketpaint_transform_rotate_right_btn)) - .perform(click()); - return this; - } - - public TransformToolOptionsViewInteraction performRotateCounterClockwise() { - onView(withId(R.id.pocketpaint_transform_rotate_left_btn)) - .perform(click()); - return this; - } - - public TransformToolOptionsViewInteraction performFlipVertical() { - onView(withId(R.id.pocketpaint_transform_flip_vertical_btn)) - .perform(click()); - return this; - } - - public TransformToolOptionsViewInteraction performFlipHorizontal() { - onView(withId(R.id.pocketpaint_transform_flip_horizontal_btn)) - .perform(click()); - return this; - } - - public TransformToolOptionsViewInteraction moveSliderTo(int moveTo) { - onView(withId(R.id.pocketpaint_transform_resize_seekbar)) - .perform(setProgress(moveTo)); - return this; - } - - public TransformToolOptionsViewInteraction performApplyResize() { - onView(withId(R.id.pocketpaint_transform_apply_resize_btn)) - .perform(click()); - return this; - } - - public TransformToolOptionsViewInteraction performEditResizeTextField(String size) { - onView(withId(R.id.pocketpaint_transform_resize_percentage_text)) - .perform(replaceText(size)); - return this; - } - - public TransformToolOptionsViewInteraction checkPercentageTextMatches(int expected) { - onView(withId(R.id.pocketpaint_transform_resize_percentage_text)) - .check(matches(withText(Integer.toString(expected)))); - return this; - } - - public TransformToolOptionsViewInteraction checkLayerWidthMatches(int expected) { - Integer expectedValue = expected; - onView(withId(R.id.pocketpaint_transform_width_value)) - .check(matches(hasValueEqualTo(expectedValue.toString()))); - return this; - } - - Matcher hasValueEqualTo(final String content) { - - return new TypeSafeMatcher() { - - @Override - public void describeTo(Description description) { - description.appendText("Has EditText/TextView the value: " + content); - } - - @Override - public boolean matchesSafely(View view) { - if (!(view instanceof TextView) && !(view instanceof EditText)) { - return false; - } - if (view != null) { - String text; - if (view instanceof TextView) { - text = ((TextView) view).getText().toString(); - } else { - text = ((EditText) view).getText().toString(); - } - return text.matches(content); - } - return false; - } - }; - } - - public TransformToolOptionsViewInteraction checkLayerHeightMatches(int expected) { - Integer expectedValue = expected; - onView(withId(R.id.pocketpaint_transform_height_value)) - .check(matches(hasValueEqualTo(expectedValue.toString()))); - return this; - } - - public TransformToolOptionsViewInteraction checkIsDisplayed() { - onView(withId(R.id.pocketpaint_layout_tool_options)) - .check(matches(isDisplayed())); - return this; - } - - public TransformToolOptionsViewInteraction checkIsNotDisplayed() { - onView(withId(R.id.pocketpaint_layout_tool_options)) - .check(matches(not(isDisplayed()))); - return this; - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/TransformToolOptionsViewInteraction.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/TransformToolOptionsViewInteraction.kt new file mode 100644 index 0000000000..600a68b3c0 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/TransformToolOptionsViewInteraction.kt @@ -0,0 +1,144 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.test.espresso.util.wrappers + +import android.view.View +import android.widget.EditText +import android.widget.TextView +import androidx.test.espresso.Espresso +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import org.catrobat.paintroid.R +import org.catrobat.paintroid.test.espresso.util.UiInteractions +import org.hamcrest.Description +import org.hamcrest.Matchers +import org.hamcrest.TypeSafeMatcher + +class TransformToolOptionsViewInteraction private constructor() : CustomViewInteraction(Espresso.onView(withId(R.id.pocketpaint_layout_tool_options))) { + fun performSetCenterClick(): TransformToolOptionsViewInteraction { + Espresso.onView(withId(R.id.pocketpaint_transform_set_center_btn)) + .perform(ViewActions.click()) + return this + } + + fun performAutoCrop(): TransformToolOptionsViewInteraction { + Espresso.onView(withId(R.id.pocketpaint_transform_auto_crop_btn)) + .perform(ViewActions.click()) + return this + } + + fun checkAutoDisplayed(): TransformToolOptionsViewInteraction { + Espresso.onView(withText(R.string.transform_auto_crop_text)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + return this + } + + fun performRotateClockwise(): TransformToolOptionsViewInteraction { + Espresso.onView(withId(R.id.pocketpaint_transform_rotate_right_btn)) + .perform(ViewActions.click()) + return this + } + + fun performRotateCounterClockwise(): TransformToolOptionsViewInteraction { + Espresso.onView(withId(R.id.pocketpaint_transform_rotate_left_btn)) + .perform(ViewActions.click()) + return this + } + + fun performFlipVertical(): TransformToolOptionsViewInteraction { + Espresso.onView(withId(R.id.pocketpaint_transform_flip_vertical_btn)) + .perform(ViewActions.click()) + return this + } + + fun performFlipHorizontal(): TransformToolOptionsViewInteraction { + Espresso.onView(withId(R.id.pocketpaint_transform_flip_horizontal_btn)) + .perform(ViewActions.click()) + return this + } + + fun moveSliderTo(moveTo: Int): TransformToolOptionsViewInteraction { + Espresso.onView(withId(R.id.pocketpaint_transform_resize_seekbar)) + .perform(UiInteractions.setProgress(moveTo)) + return this + } + + fun performApplyResize(): TransformToolOptionsViewInteraction { + Espresso.onView(withId(R.id.pocketpaint_transform_apply_resize_btn)) + .perform(ViewActions.click()) + return this + } + + fun performEditResizeTextField(size: String?): TransformToolOptionsViewInteraction { + Espresso.onView(withId(R.id.pocketpaint_transform_resize_percentage_text)) + .perform(ViewActions.replaceText(size)) + return this + } + + fun checkPercentageTextMatches(expected: Int): TransformToolOptionsViewInteraction { + Espresso.onView(withId(R.id.pocketpaint_transform_resize_percentage_text)) + .check(ViewAssertions.matches(ViewMatchers.withText(Integer.toString(expected)))) + return this + } + + fun checkLayerWidthMatches(expected: Int): TransformToolOptionsViewInteraction { + Espresso.onView(withId(R.id.pocketpaint_transform_width_value)) + .check(ViewAssertions.matches(hasValueEqualTo(expected.toString()))) + return this + } + + private fun hasValueEqualTo(content: String): TypeSafeMatcher { + return object : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText("Has EditText/TextView the value: $content") + } + + public override fun matchesSafely(view: View?): Boolean { + if (view !is TextView && view !is EditText) { return false } + if (view is EditText) { return view.text.toString() == content } + return (view as TextView).text.toString() == content + } + } + } + + fun checkLayerHeightMatches(expected: Int): TransformToolOptionsViewInteraction { + Espresso.onView(withId(R.id.pocketpaint_transform_height_value)) + .check(ViewAssertions.matches(hasValueEqualTo(expected.toString()))) + return this + } + + fun checkIsDisplayed(): TransformToolOptionsViewInteraction { + Espresso.onView(withId(R.id.pocketpaint_layout_tool_options)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + return this + } + + fun checkIsNotDisplayed(): TransformToolOptionsViewInteraction { + Espresso.onView(withId(R.id.pocketpaint_layout_tool_options)) + .check(ViewAssertions.matches(Matchers.not(ViewMatchers.isDisplayed()))) + return this + } + + companion object { + fun onTransformToolOptionsView(): TransformToolOptionsViewInteraction = TransformToolOptionsViewInteraction() + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ZoomWindowInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ZoomWindowInteraction.java deleted file mode 100644 index 9af9e06b73..0000000000 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ZoomWindowInteraction.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.catrobat.paintroid.test.espresso.util.wrappers; - -import android.view.View; -import android.widget.RelativeLayout; - -import org.catrobat.paintroid.MainActivity; -import org.catrobat.paintroid.R; -import org.hamcrest.Description; -import org.hamcrest.TypeSafeMatcher; - -import static org.catrobat.paintroid.test.espresso.util.MainActivityHelper.getMainActivityFromView; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.withId; - -public final class ZoomWindowInteraction extends CustomViewInteraction { - private ZoomWindowInteraction() { - super(onView(withId(R.id.pocketpaint_zoom_window_image))); - } - - public static ZoomWindowInteraction onZoomWindow() { - return new ZoomWindowInteraction(); - } - - public ZoomWindowInteraction checkAlignment(final int verb) { - check(matches(new TypeSafeMatcher() { - @Override - protected boolean matchesSafely(View view) { - MainActivity activity = getMainActivityFromView(view); - RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) - activity.findViewById(R.id.pocketpaint_zoom_window_image).getLayoutParams(); - - int rulesFromLayout = layoutParams.getRule(verb); - - return rulesFromLayout == -1; - } - - @Override - public void describeTo(Description description) { - description.appendText("The window's alignment"); - } - })); - return this; - } - - public ZoomWindowInteraction checkAlignmentBelowM(final int index) { - check(matches(new TypeSafeMatcher() { - @Override - protected boolean matchesSafely(View view) { - MainActivity activity = getMainActivityFromView(view); - RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) - activity.findViewById(R.id.pocketpaint_zoom_window_image).getLayoutParams(); - - int[] rulesFromLayout = layoutParams.getRules(); - - return rulesFromLayout[index] == -1; - } - - @Override - public void describeTo(Description description) { - description.appendText("The window's alignment"); - } - })); - return this; - } -} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ZoomWindowInteraction.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ZoomWindowInteraction.kt new file mode 100644 index 0000000000..4b02f8e15a --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ZoomWindowInteraction.kt @@ -0,0 +1,49 @@ +package org.catrobat.paintroid.test.espresso.util.wrappers + +import android.view.View +import android.widget.RelativeLayout +import androidx.test.espresso.Espresso +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers.withId +import org.catrobat.paintroid.R +import org.catrobat.paintroid.test.espresso.util.MainActivityHelper +import org.hamcrest.Description +import org.hamcrest.TypeSafeMatcher + +class ZoomWindowInteraction private constructor() : CustomViewInteraction(Espresso.onView(withId(R.id.pocketpaint_zoom_window_image))) { + fun checkAlignment(verb: Int): ZoomWindowInteraction { + check(ViewAssertions.matches(object : TypeSafeMatcher() { + override fun matchesSafely(view: View?): Boolean { + val activity = MainActivityHelper.getMainActivityFromView(view!!) + val layoutParams = activity.findViewById(R.id.pocketpaint_zoom_window_image).layoutParams as RelativeLayout.LayoutParams + val rulesFromLayout = layoutParams.getRule(verb) + return rulesFromLayout == -1 + } + + override fun describeTo(description: Description) { + description.appendText("The window's alignment") + } + })) + return this + } + + fun checkAlignmentBelowM(index: Int): ZoomWindowInteraction { + check(ViewAssertions.matches(object : TypeSafeMatcher() { + override fun matchesSafely(view: View?): Boolean { + val activity = MainActivityHelper.getMainActivityFromView(view!!) + val layoutParams = activity.findViewById(R.id.pocketpaint_zoom_window_image).layoutParams as RelativeLayout.LayoutParams + val rulesFromLayout = layoutParams.rules + return rulesFromLayout[index] == -1 + } + + override fun describeTo(description: Description) { + description.appendText("The window's alignment") + } + })) + return this + } + + companion object { + fun onZoomWindow(): ZoomWindowInteraction = ZoomWindowInteraction() + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/algorithm/FillAlgorithmTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/algorithm/FillAlgorithmTest.kt index ab7c0ac42b..f1856857f5 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/algorithm/FillAlgorithmTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/algorithm/FillAlgorithmTest.kt @@ -52,7 +52,7 @@ class FillAlgorithmTest { assertEquals("Wrong array size", height, algorithmPixels.size) assertEquals("Wrong array size", width, algorithmPixels[0].size) val algorithmTargetColor = fillAlgorithm.targetColor - val algorithmReplacementColor = fillAlgorithm.replacementColor + val algorithmReplacementColor = fillAlgorithm.colorToBeReplaced val algorithmColorTolerance = fillAlgorithm.colorToleranceThresholdSquared.toFloat() assertEquals( "Wrong target color", diff --git a/Paintroid/src/debug/AndroidManifest.xml b/Paintroid/src/debug/AndroidManifest.xml index 18ddbc41af..ff48bbb59f 100644 --- a/Paintroid/src/debug/AndroidManifest.xml +++ b/Paintroid/src/debug/AndroidManifest.xml @@ -21,6 +21,7 @@ + + diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/FileIO.kt b/Paintroid/src/main/java/org/catrobat/paintroid/FileIO.kt index 895c4484f2..864f069b0c 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/FileIO.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/FileIO.kt @@ -358,7 +358,7 @@ object FileIO { cursor?.use { if (cursor.moveToFirst()) { fileName = - cursor.getString(cursor.getColumnIndex(MediaStore.Images.ImageColumns.DISPLAY_NAME)) + cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.DISPLAY_NAME)) } } if (fileName.endsWith(FileType.JPG.toExtension()) || fileName.endsWith(".jpeg")) { @@ -406,9 +406,9 @@ object FileIO { val cursor = resolver.query(contentLocationUri, null, selection, selectionArgs, null) cursor?.run { while (moveToNext()) { - val fileName = getString(cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME)) + val fileName = getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME)) if (fileName == filename) { - val id = cursor.getLong(cursor.getColumnIndex(MediaStore.MediaColumns._ID)) + val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID)) close() return ContentUris.withAppendedId(contentLocationUri, id) } diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/MainActivity.kt b/Paintroid/src/main/java/org/catrobat/paintroid/MainActivity.kt index 57303d9379..e393a5d841 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/MainActivity.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/MainActivity.kt @@ -38,6 +38,7 @@ import android.view.WindowInsets import android.view.WindowManager import android.view.inputmethod.InputMethodManager import android.webkit.MimeTypeMap +import android.widget.ImageButton import androidx.annotation.VisibleForTesting import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar @@ -129,6 +130,9 @@ class MainActivity : AppCompatActivity(), MainView, CommandListener { @VisibleForTesting lateinit var toolOptionsViewController: ToolOptionsViewController + @VisibleForTesting + lateinit var layerAdapter: LayerAdapter + var idlingResource: CountingIdlingResource = CountingIdlingResource("MainIdleResource") @VisibleForTesting @@ -459,14 +463,13 @@ class MainActivity : AppCompatActivity(), MainView, CommandListener { ) perspective = Perspective(layerModel.width, layerModel.height) val listener = DefaultWorkspace.Listener { drawingSurface.refreshDrawingSurface() } - model = MainActivityModel() workspace = DefaultWorkspace( layerModel, perspective, listener, ) - commandSerializer = CommandSerializer(this, commandManager, model) model = MainActivityModel() + commandSerializer = CommandSerializer(this, commandManager, model) zoomWindowController = DefaultZoomWindowController( this, layerModel, @@ -474,7 +477,6 @@ class MainActivity : AppCompatActivity(), MainView, CommandListener { toolReference, UserPreferences(getPreferences(MODE_PRIVATE)) ) - model = MainActivityModel() defaultToolController = DefaultToolController( toolReference, toolOptionsViewController, @@ -518,6 +520,7 @@ class MainActivity : AppCompatActivity(), MainView, CommandListener { } private fun onCreateLayerMenu() { + setLayoutDirection() val layerLayout = findViewById(R.id.pocketpaint_nav_view_layer) val drawerLayout = findViewById(R.id.pocketpaint_drawer_layout) val layerListView = findViewById(R.id.pocketpaint_layer_side_nav_list) @@ -530,7 +533,7 @@ class MainActivity : AppCompatActivity(), MainView, CommandListener { val layoutManager = LinearLayoutManager(applicationContext, RecyclerView.VERTICAL, false) layerListView.layoutManager = layoutManager layerListView.manager = layoutManager - val layerAdapter = LayerAdapter(layerPresenter, this) + layerAdapter = LayerAdapter(layerPresenter, this) layerListView.setLayerAdapter(layerAdapter) presenterMain.setLayerAdapter(layerAdapter) layerPresenter.setAdapter(layerAdapter) @@ -543,6 +546,18 @@ class MainActivity : AppCompatActivity(), MainView, CommandListener { drawerLayout.addDrawerListener(drawerLayoutListener) } + private fun setLayoutDirection() { + var visibilityBtn = findViewById(R.id.pocketpaint_layer_side_nav_button_visibility) + var layerNavigationView = findViewById(R.id.pocketpaint_nav_view_layer) + if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL) { + visibilityBtn.setBackgroundResource(R.drawable.rounded_corner_top_rtl) + layerNavigationView.setBackgroundResource(R.drawable.layer_nav_view_background_rtl) + } else { + visibilityBtn.setBackgroundResource(R.drawable.rounded_corner_top_ltr) + layerNavigationView.setBackgroundResource(R.drawable.layer_nav_view_background_ltr) + } + } + private fun onCreateDrawingSurface() { drawingSurface = findViewById(R.id.pocketpaint_drawing_surface_view) drawingSurface.setArguments( diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/command/CommandManager.kt b/Paintroid/src/main/java/org/catrobat/paintroid/command/CommandManager.kt index b6cf5bb7ca..2c32275b16 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/command/CommandManager.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/command/CommandManager.kt @@ -65,6 +65,14 @@ interface CommandManager { fun popFirstCommandInRedo() + fun executeAllCommands() + + fun getUndoCommandCount(): Int + + fun getColorCommandCount(): Int + + fun isLastColorCommandOnTop(): Boolean + interface CommandListener { fun commandPostExecute() } diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/AsyncCommandManager.kt b/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/AsyncCommandManager.kt index 8a433faece..a8f3d6822f 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/AsyncCommandManager.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/AsyncCommandManager.kt @@ -195,4 +195,31 @@ open class AsyncCommandManager( } } } + + override fun executeAllCommands() { + CoroutineScope(Dispatchers.Default).launch { + mutex.withLock { + if (!shuttingDown) { + synchronized(layerModel) { + commandManager.executeAllCommands() + } + } + withContext(Dispatchers.Main) { + notifyCommandPostExecute() + } + } + } + } + + override fun getUndoCommandCount(): Int { + synchronized(layerModel) { return commandManager.getUndoCommandCount() } + } + + override fun getColorCommandCount(): Int { + synchronized(layerModel) { return commandManager.getColorCommandCount() } + } + + override fun isLastColorCommandOnTop(): Boolean { + synchronized(layerModel) { return commandManager.isLastColorCommandOnTop() } + } } diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/ColorChangedCommand.kt b/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/ColorChangedCommand.kt index d6039aac5a..033a09904d 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/ColorChangedCommand.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/ColorChangedCommand.kt @@ -56,6 +56,25 @@ class ColorChangedCommand(toolReference: ToolReference, context: Context, color: } } + fun runInUndoMode() { + if (toolReference.tool !is LineTool) { + (context as MainActivity).runOnUiThread { + toolReference.tool?.changePaintColor(color, false) + } + } else { + if (toolReference.tool is LineTool && !firstTime) { + (context as MainActivity).runOnUiThread { + (toolReference.tool as LineTool).undoColorChangedCommand(color, false) + } + } else { + (context as MainActivity).runOnUiThread { + toolReference.tool?.changePaintColor(color, false) + } + firstTime = false + } + } + } + override fun freeResources() { // No resources to free } diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/DefaultCommandManager.kt b/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/DefaultCommandManager.kt index 79dbc541f0..91ebbbd431 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/DefaultCommandManager.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/DefaultCommandManager.kt @@ -141,8 +141,11 @@ class DefaultCommandManager( layerModel.addLayerAt(0, this) } } - val commandToRestore = redoCommandList.pop() - undoCommandList.addFirst(commandToRestore) + + if (isRedoAvailable) { + val commandToRestore = redoCommandList.pop() + undoCommandList.addFirst(commandToRestore) + } return } @@ -153,15 +156,23 @@ class DefaultCommandManager( initialStateCommand?.run(canvas, layerModel) + val colorCommandCount = getColorCommandCount() + + var currentColorCommandCount = 0 val iterator = undoCommandList.descendingIterator() + while (iterator.hasNext()) { - var nextCommand = iterator.next() + val nextCommand = iterator.next() if (nextCommand is ColorChangedCommand && ignoreColorCommand) { continue } val currentLayer = layerModel.currentLayer canvas.setBitmap(currentLayer?.bitmap) - nextCommand.run(canvas, layerModel) + if (nextCommand is ColorChangedCommand && ++currentColorCommandCount < colorCommandCount) { + nextCommand.runInUndoMode() + } else { + nextCommand.run(canvas, layerModel) + } } if (!currentCommandName.matches(mergeLayerCommandRegex)) { @@ -169,7 +180,35 @@ class DefaultCommandManager( } } - private fun executeAllCommands() { + override fun getColorCommandCount(): Int { + val commandIterator = undoCommandList.iterator() + var counter = 0 + while (commandIterator.hasNext()) { + val nextCommand = commandIterator.next() + if (nextCommand is ColorChangedCommand) { + counter++ + } + } + return counter + } + + override fun isLastColorCommandOnTop(): Boolean { + var retVal = false + if (undoCommandList.first is ColorChangedCommand) { + val commandIterator = undoCommandList.iterator() + var counter = 0 + while (commandIterator.hasNext()) { + val nextCommand = commandIterator.next() + if (nextCommand is ColorChangedCommand) { + counter++ + } + } + retVal = counter == 1 + } + return retVal + } + + override fun executeAllCommands() { val layerCount = layerModel.layerCount val checkBoxes: MutableList = ArrayList(Collections.nCopies(layerCount, true)) @@ -247,11 +286,10 @@ class DefaultCommandManager( override fun undoInConnectedLinesMode() { val colorCommandList = removeColorCommands() if (undoCommandList.isNotEmpty()) { - val commandForUndo: Command - if (colorCommandList.isNotEmpty()) { - commandForUndo = colorCommandList.pop() + val commandForUndo: Command = if (colorCommandList.isNotEmpty()) { + colorCommandList.pop() } else { - commandForUndo = undoCommandList.pop() + undoCommandList.pop() } redoCommandList.addFirst(commandForUndo) handleUndo(commandForUndo) @@ -303,26 +341,29 @@ class DefaultCommandManager( } override fun redoInConnectedLinesMode() { - val command = redoCommandList.pop() - if (command is ColorChangedCommand) { - val colorCommandList = removeColorCommands() - if (undoCommandList.isNotEmpty()) { - val firstNonColorCommand = undoCommandList.first - val color = command.color + if (isRedoAvailable) { + val command = redoCommandList.pop() + + if (command is ColorChangedCommand) { + val colorCommandList = removeColorCommands() + if (undoCommandList.isNotEmpty()) { + val firstNonColorCommand = undoCommandList.first + val color = command.color - if (firstNonColorCommand is PathCommand) { - firstNonColorCommand.paint.color = color - } else if (firstNonColorCommand is PointCommand) { - firstNonColorCommand.paint.color = color + if (firstNonColorCommand is PathCommand) { + firstNonColorCommand.paint.color = color + } else if (firstNonColorCommand is PointCommand) { + firstNonColorCommand.paint.color = color + } + executeAllCommands() } - executeAllCommands() + addAndExecuteCommands(colorCommandList) } - addAndExecuteCommands(colorCommandList) - } - undoCommandList.addFirst(command) + undoCommandList.addFirst(command) - executeCommand(command) - notifyCommandExecuted() + executeCommand(command) + notifyCommandExecuted() + } } override fun getCommandManagerModelForCatrobatImage(): CommandManagerModel? { @@ -339,35 +380,45 @@ class DefaultCommandManager( } override fun adjustUndoListForClippingTool() { - if (undoCommandList.first.toString().split(".", "@").size < FIVE) { - return - } - val commandName = undoCommandList.first.toString().split(".", "@")[FIVE] - if (commandName == ClippingCommand::class.java.simpleName) { - val clippingCommand = undoCommandList.pop() - undoCommandList.pop() - undoCommandList.addFirst(clippingCommand) + if (isUndoAvailable) { + if (undoCommandList.first.toString().split(".", "@").size < FIVE) { + return + } + val commandName = undoCommandList.first.toString().split(".", "@")[FIVE] + if (commandName == ClippingCommand::class.java.simpleName) { + val clippingCommand = undoCommandList.pop() + undoCommandList.pop() + undoCommandList.addFirst(clippingCommand) + } } } override fun undoInClippingTool() { - val command = undoCommandList.pop() - handleUndo(command) - notifyCommandExecuted() + if (isUndoAvailable) { + val command = undoCommandList.pop() + handleUndo(command) + notifyCommandExecuted() + } } override fun popFirstCommandInUndo() { - undoCommandList.pop() + if (isUndoAvailable) { + undoCommandList.pop() + } } override fun popFirstCommandInRedo() { - redoCommandList.pop() + if (isRedoAvailable) { + redoCommandList.pop() + } } override fun setInitialStateCommand(command: Command) { initialStateCommand = command } + override fun getUndoCommandCount(): Int = undoCommandList.size + private fun notifyCommandExecuted() { for (listener in commandListeners) { listener.commandPostExecute() diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/FillCommand.kt b/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/FillCommand.kt index 5e95d2df7d..d3a9bc1328 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/FillCommand.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/FillCommand.kt @@ -35,15 +35,15 @@ class FillCommand(private val fillAlgorithmFactory: FillAlgorithmFactory, clicke override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { val currentLayer = layerModel.currentLayer currentLayer ?: return - currentLayer.bitmap?.let { bitmap -> - val replacementColor = bitmap.getPixel(clickedPixel.x, clickedPixel.y) + currentLayer.bitmap.let { bitmap -> + val colorToBeReplaced = bitmap.getPixel(clickedPixel.x, clickedPixel.y) val fillAlgorithm = fillAlgorithmFactory.createFillAlgorithm() fillAlgorithm.setParameters( - bitmap, - clickedPixel, - paint.color, - replacementColor, - colorTolerance + bitmap, + clickedPixel, + paint.color, + colorToBeReplaced, + colorTolerance ) fillAlgorithm.performFilling() } diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/command/serialization/CommandSerializer.kt b/Paintroid/src/main/java/org/catrobat/paintroid/command/serialization/CommandSerializer.kt index 257f1a4c59..13a0df7452 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/command/serialization/CommandSerializer.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/command/serialization/CommandSerializer.kt @@ -32,9 +32,7 @@ import android.net.Uri import android.os.Build import android.os.Environment import android.provider.MediaStore -import android.util.Log import com.esotericsoftware.kryo.Kryo -import com.esotericsoftware.kryo.KryoException import com.esotericsoftware.kryo.io.Input import com.esotericsoftware.kryo.io.Output import org.catrobat.paintroid.colorpicker.ColorHistory @@ -76,7 +74,6 @@ import org.catrobat.paintroid.tools.drawable.OvalDrawable import org.catrobat.paintroid.tools.drawable.RectangleDrawable import org.catrobat.paintroid.tools.drawable.ShapeDrawable import org.catrobat.paintroid.tools.drawable.StarDrawable -import org.catrobat.paintroid.ui.DrawingSurfaceThread import java.io.File import java.io.FileInputStream import java.io.FileOutputStream @@ -87,7 +84,6 @@ open class CommandSerializer(private val activityContext: Context, private val c companion object { const val CURRENT_IMAGE_VERSION = 2 const val MAGIC_VALUE = "CATROBAT" - private val TAG = DrawingSurfaceThread::class.java.simpleName } val kryo = Kryo() @@ -185,10 +181,10 @@ open class CommandSerializer(private val activityContext: Context, private val c returnUri = Uri.fromFile(imageFile) } - val downloadManager = OpenRasterFileFormatConversion.mainActivity.baseContext.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager + val downloadManager = OpenRasterFileFormatConversion.mainActivity?.baseContext?.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager val id = downloadManager.addCompletedDownload(fileName, fileName, true, "application/zip", imageFile.absolutePath, imageFile.length(), true) - val sharedPreferences = OpenRasterFileFormatConversion.mainActivity.getSharedPreferences(SPECIFIC_FILETYPE_SHARED_PREFERENCES_NAME, 0) - sharedPreferences.edit().putLong(imageFile.absolutePath, id).apply() + val sharedPreferences = OpenRasterFileFormatConversion.mainActivity?.getSharedPreferences(SPECIFIC_FILETYPE_SHARED_PREFERENCES_NAME, 0) + sharedPreferences?.edit()?.putLong(imageFile.absolutePath, id)?.apply() } return returnUri @@ -211,10 +207,10 @@ open class CommandSerializer(private val activityContext: Context, private val c } else { val file = File(uri.path.toString()) val isDeleted = file.delete() - val sharedPreferences = OpenRasterFileFormatConversion.mainActivity.getSharedPreferences(SPECIFIC_FILETYPE_SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE) - val id = sharedPreferences.getLong(uri.path, -1) - if (id > -1) { - val downloadManager = OpenRasterFileFormatConversion.mainActivity.baseContext.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager + val sharedPreferences = OpenRasterFileFormatConversion.mainActivity?.getSharedPreferences(SPECIFIC_FILETYPE_SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE) + val id = sharedPreferences?.getLong(uri.path, -1) + if (id != null && id > -1) { + val downloadManager = OpenRasterFileFormatConversion.mainActivity?.baseContext?.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager downloadManager.remove(id) } if (!isDeleted) { @@ -234,21 +230,19 @@ open class CommandSerializer(private val activityContext: Context, private val c var commandModel: CommandManagerModel? = null var colorHistory: ColorHistory? = null - try { - Input(stream).use { input -> - if (!input.readString().equals(MAGIC_VALUE)) { - throw NotCatrobatImageException("Magic Value doesn't exist.") - } - val imageVersion = input.readInt() - if (CURRENT_IMAGE_VERSION != imageVersion) { - setRegisterMapVersion(imageVersion) - registerClasses() - } - commandModel = kryo.readObject(input, CommandManagerModel::class.java) + Input(stream).use { input -> + if (!input.readString().equals(MAGIC_VALUE)) { + throw NotCatrobatImageException("Magic Value doesn't exist.") + } + val imageVersion = input.readInt() + if (CURRENT_IMAGE_VERSION != imageVersion) { + setRegisterMapVersion(imageVersion) + registerClasses() + } + commandModel = kryo.readObject(input, CommandManagerModel::class.java) + if (input.available() != 0) { colorHistory = kryo.readObject(input, ColorHistory::class.java) } - } catch (ex: KryoException) { - Log.d(TAG, "KryoException while reading autosave: " + ex.message) } commandModel?.commands?.reverse() diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/contract/LayerContracts.kt b/Paintroid/src/main/java/org/catrobat/paintroid/contract/LayerContracts.kt index 0d1f105979..5b743dcec0 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/contract/LayerContracts.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/contract/LayerContracts.kt @@ -20,6 +20,7 @@ package org.catrobat.paintroid.contract import android.graphics.Bitmap import android.view.View +import android.widget.LinearLayout import androidx.annotation.StringRes import org.catrobat.paintroid.controller.DefaultToolController import org.catrobat.paintroid.ui.DrawingSurface @@ -47,7 +48,7 @@ interface LayerContracts { fun disableVisibilityAndOpacityButtons() - fun getLayerItem(position: Int): Layer + fun getLayerItem(position: Int): Layer? fun getLayerItemId(position: Int): Long @@ -94,6 +95,8 @@ interface LayerContracts { fun isSelected(): Boolean + fun getViewLayout(): LinearLayout + fun bindView() fun setLayerVisibilityCheckbox(setTo: Boolean) diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/contract/MainActivityContracts.kt b/Paintroid/src/main/java/org/catrobat/paintroid/contract/MainActivityContracts.kt index cb3670335f..84fef55bab 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/contract/MainActivityContracts.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/contract/MainActivityContracts.kt @@ -44,6 +44,7 @@ interface MainActivityContracts { interface Navigator { val isSdkAboveOrEqualM: Boolean val isSdkAboveOrEqualQ: Boolean + val isSdkAboveOrEqualT: Boolean fun showColorPickerDialog() @@ -401,6 +402,8 @@ interface MainActivityContracts { fun showCurrentTool(toolType: ToolType?) + fun enableColorItemView(show: Boolean) + fun setColorButtonColor(@ColorInt color: Int) } diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/controller/DefaultToolController.kt b/Paintroid/src/main/java/org/catrobat/paintroid/controller/DefaultToolController.kt index 525afe2307..3b3cd1e8b9 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/controller/DefaultToolController.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/controller/DefaultToolController.kt @@ -34,8 +34,10 @@ import org.catrobat.paintroid.tools.ToolReference import org.catrobat.paintroid.tools.ToolType import org.catrobat.paintroid.tools.Workspace import org.catrobat.paintroid.tools.implementation.ClippingTool +import org.catrobat.paintroid.tools.implementation.EraserTool import org.catrobat.paintroid.tools.implementation.ImportTool import org.catrobat.paintroid.tools.implementation.LineTool +import org.catrobat.paintroid.tools.implementation.SprayTool import org.catrobat.paintroid.tools.implementation.TextTool import org.catrobat.paintroid.tools.options.ToolOptionsViewController @@ -74,6 +76,9 @@ class DefaultToolController( } else { toolOptionsViewController.hideCheckmark() } + if (toolReference.tool?.toolType == ToolType.SPRAY) { + (currentTool as SprayTool).resetRadiusToStrokeWidth() + } val tool: Tool = toolFactory.createTool( toolType, toolOptionsViewController, @@ -126,6 +131,9 @@ class DefaultToolController( private fun switchTool(tool: Tool) { val currentTool = toolReference.tool val currentToolType = currentTool?.toolType + if (currentToolType == ToolType.ERASER) { + (currentTool as EraserTool).setSavedColor() + } currentToolType?.let { hidePlusIfShown(it) } if (currentToolType == tool.toolType) { diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/iotasks/LoadImage.kt b/Paintroid/src/main/java/org/catrobat/paintroid/iotasks/LoadImage.kt index 45840c45c6..1d5562001e 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/iotasks/LoadImage.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/iotasks/LoadImage.kt @@ -47,14 +47,16 @@ class LoadImage( private val callbackRef: WeakReference = WeakReference(callback) private val context: WeakReference = WeakReference(context) - private fun getMimeType(uri: Uri, resolver: ContentResolver): String? = + private fun getMimeType(uri: Uri, resolver: ContentResolver): String? { if (uri.scheme == ContentResolver.SCHEME_CONTENT) { - resolver.getType(uri) - } else { - val fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri.toString()) - MimeTypeMap.getSingleton() - .getMimeTypeFromExtension(fileExtension.toLowerCase(Locale.US)) + return resolver.getType(uri) + } + val fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri.toString()) + if (fileExtension.equals("catrobat-image")) { + return "application/octet-stream" } + return MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension.toLowerCase(Locale.US)) + } private fun getBitmapReturnValue( uri: Uri, diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/iotasks/OpenRasterFileFormatConversion.java b/Paintroid/src/main/java/org/catrobat/paintroid/iotasks/OpenRasterFileFormatConversion.java deleted file mode 100644 index 8838385f2a..0000000000 --- a/Paintroid/src/main/java/org/catrobat/paintroid/iotasks/OpenRasterFileFormatConversion.java +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Paintroid: An image manipulation application for Android. - * Copyright (C) 2010-2022 The Catrobat Team - * () - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.catrobat.paintroid.iotasks; - -import android.app.DownloadManager; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.net.Uri; -import android.os.Build; -import android.os.Environment; -import android.provider.MediaStore; -import android.util.Log; - -import org.catrobat.paintroid.FileIO; -import org.catrobat.paintroid.MainActivity; -import org.catrobat.paintroid.contract.LayerContracts; -import org.catrobat.paintroid.model.Layer; -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; -import java.util.zip.ZipOutputStream; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; - -import static android.content.Context.DOWNLOAD_SERVICE; - -import static org.catrobat.paintroid.common.ConstantsKt.MAX_LAYERS; -import static org.catrobat.paintroid.common.ConstantsKt.SPECIFIC_FILETYPE_SHARED_PREFERENCES_NAME; - -// -//Source: https://www.openraster.org/baseline/file-layout-spec.html -// -//Layout: -//example.ora [considered as a folder-like object] -// ├ mimetype -// ├ stack.xml -// ├ data/ -// │ ├ [image data files referenced by stack.xml, typically layer*.png] -// │ └ [other data files, indexed elsewhere] -// ├ Thumbnails/ -// │ └ thumbnail.png -// └ mergedimage.png -public final class OpenRasterFileFormatConversion { - private static final String TAG = OpenRasterFileFormatConversion.class.getSimpleName(); - private static final int COMPRESS_QUALITY = 100; - private static final int THUMBNAIL_WIDTH = 256; - private static final int THUMBNAIL_HEIGHT = 256; - private static final String ORA_VERSION = "0.0.2"; - public static MainActivity mainActivity = null; - - private OpenRasterFileFormatConversion() { - throw new AssertionError(); - } - - public static void setContext(MainActivity toSet) { - mainActivity = toSet; - } - - public static Uri exportToOraFile(List layers, String fileName, Bitmap bitmapAllLayers, ContentResolver resolver) throws IOException { - Uri imageUri; - OutputStream outputStream; - ContentValues contentValues = new ContentValues(); - float wholeSize = 0; - - String mimeType = "image/openraster"; - byte[] mimeByteArray = mimeType.getBytes(); - byte[] xmlByteArray = getXmlStack(layers); - - for (LayerContracts.Layer current: layers) { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - current.getBitmap().compress(Bitmap.CompressFormat.PNG, COMPRESS_QUALITY, bos); - byte[] byteArray = bos.toByteArray(); - wholeSize += byteArray.length; - - byte[] alphaByteArray = BigInteger.valueOf(current.getOpacityPercentage()).toByteArray(); - wholeSize += alphaByteArray.length; - } - - ByteArrayOutputStream bosMerged = new ByteArrayOutputStream(); - bitmapAllLayers.compress(Bitmap.CompressFormat.PNG, COMPRESS_QUALITY, bosMerged); - byte[] bitmapByteArray = bosMerged.toByteArray(); - - Bitmap bitmapThumb = Bitmap.createScaledBitmap(bitmapAllLayers, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, false); - ByteArrayOutputStream bosThumb = new ByteArrayOutputStream(); - bitmapThumb.compress(Bitmap.CompressFormat.PNG, COMPRESS_QUALITY, bosThumb); - byte[] bitmapThumbArray = bosThumb.toByteArray(); - File imageRoot = null; - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - //applefile has no file ending. which is important for api level 30. - // we can't save an application file in media directory so we have to save it in downloads. - contentValues.put(MediaStore.Files.FileColumns.DISPLAY_NAME, fileName); - contentValues.put(MediaStore.Files.FileColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS); - contentValues.put(MediaStore.Files.FileColumns.MIME_TYPE, "application/applefile"); - - imageUri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues); - } else { - - imageRoot = Environment.getExternalStoragePublicDirectory( - Environment.DIRECTORY_DOWNLOADS); - - if (!imageRoot.exists() && !imageRoot.mkdirs()) { - imageRoot.mkdirs(); - } - - Uri uri = MediaStore.Files.getContentUri("external"); - - contentValues.put(MediaStore.Files.FileColumns.DATA, imageRoot.getAbsolutePath() + "/" + fileName); - contentValues.put(MediaStore.Files.FileColumns.DISPLAY_NAME, fileName); - contentValues.put(MediaStore.Files.FileColumns.MIME_TYPE, "application/zip"); - contentValues.put(MediaStore.Files.FileColumns.MEDIA_TYPE, MediaStore.Files.FileColumns.MEDIA_TYPE_NONE); - - long date = System.currentTimeMillis(); - contentValues.put(MediaStore.MediaColumns.DATE_MODIFIED, date / 1000); - - wholeSize += xmlByteArray.length; - wholeSize += mimeByteArray.length; - wholeSize += bitmapByteArray.length; - wholeSize += bitmapThumbArray.length; - contentValues.put(MediaStore.Images.Media.SIZE, wholeSize); - - DownloadManager downloadManager = (DownloadManager) mainActivity.getBaseContext().getSystemService(DOWNLOAD_SERVICE); - long id = downloadManager.addCompletedDownload(fileName, fileName, true, "application/zip", imageRoot.getAbsolutePath() + "/" + fileName, (long) wholeSize, true); - - imageUri = resolver.insert(uri, contentValues); - - SharedPreferences sharedPreferences = mainActivity.getSharedPreferences(SPECIFIC_FILETYPE_SHARED_PREFERENCES_NAME, 0); - sharedPreferences.edit().putLong(imageRoot.getAbsolutePath() + "/" + fileName, id).apply(); - } - - outputStream = resolver.openOutputStream(Objects.requireNonNull(imageUri)); - - ZipOutputStream streamZip = new ZipOutputStream(outputStream); - ZipEntry mimetypeEntry = new ZipEntry("mimetype"); - mimetypeEntry.setMethod(ZipEntry.DEFLATED); - - streamZip.putNextEntry(mimetypeEntry); - streamZip.write(mimeByteArray, 0, mimeByteArray.length); - streamZip.closeEntry(); - - streamZip.putNextEntry(new ZipEntry("stack.xml")); - streamZip.write(xmlByteArray, 0, Objects.requireNonNull(xmlByteArray).length); - streamZip.closeEntry(); - - int counter = 0; - for (LayerContracts.Layer current: layers) { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - current.getBitmap().compress(Bitmap.CompressFormat.PNG, COMPRESS_QUALITY, bos); - byte[] byteArray = bos.toByteArray(); - - streamZip.putNextEntry(new ZipEntry("data/layer" + counter + ".png")); - streamZip.write(byteArray, 0, byteArray.length); - streamZip.closeEntry(); - - streamZip.putNextEntry(new ZipEntry("alpha/" + counter)); - byte[] alphaByteArray = BigInteger.valueOf(current.getOpacityPercentage()).toByteArray(); - streamZip.write(alphaByteArray, 0, alphaByteArray.length); - streamZip.closeEntry(); - counter++; - } - - streamZip.putNextEntry(new ZipEntry("Thumbnails/thumbnail.png")); - streamZip.write(bitmapThumbArray, 0, bitmapThumbArray.length); - streamZip.closeEntry(); - - streamZip.putNextEntry(new ZipEntry("mergedimage.png")); - streamZip.write(bitmapByteArray, 0, bitmapByteArray.length); - streamZip.closeEntry(); - streamZip.close(); - outputStream.close(); - - return imageUri; - } - - private static byte[] getXmlStack(List bitmapList) { - - DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); - try { - DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); - - Document doc = docBuilder.newDocument(); - Element rootElement = doc.createElement("image"); - Attr attr1 = doc.createAttribute("version"); - Attr attr2 = doc.createAttribute("w"); - Attr attr3 = doc.createAttribute("h"); - - attr1.setValue(ORA_VERSION); - attr2.setValue(String.valueOf(bitmapList.get(0).getBitmap().getWidth())); - attr3.setValue(String.valueOf(bitmapList.get(0).getBitmap().getHeight())); - - rootElement.setAttributeNode(attr1); - rootElement.setAttributeNode(attr2); - rootElement.setAttributeNode(attr3); - doc.appendChild(rootElement); - - Element stack = doc.createElement("stack"); - rootElement.appendChild(stack); - - for (int i = bitmapList.size() - 1; i >= 0; i--) { - Element layer = doc.createElement("layer"); - - layer.setAttribute("name", "layer" + i); - layer.setAttribute("src", "data/layer" + i + ".png"); - stack.appendChild(layer); - } - - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - StreamResult result = new StreamResult(stream); - - TransformerFactory transformerFactory = TransformerFactory.newInstance(); - Transformer transformer = transformerFactory.newTransformer(); - DOMSource source = new DOMSource(doc); - - transformer.transform(source, result); - - return stream.toByteArray(); - } catch (ParserConfigurationException e) { - Log.e(TAG, "Could not create document."); - return null; - } catch (TransformerException e) { - Log.e(TAG, "Could not transform Xml file."); - return null; - } - } - - public static Uri saveOraFileToUri(List layers, Uri uri, String fileName, Bitmap bitmapAllLayers, ContentResolver resolver) throws IOException { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - - String[] projection = {MediaStore.Images.Media._ID}; - Cursor c = resolver.query(uri, projection, null, null, null); - if (c.moveToFirst()) { - long id = c.getLong(c.getColumnIndexOrThrow(MediaStore.Images.Media._ID)); - Uri deleteUri = ContentUris.withAppendedId(MediaStore.Downloads.EXTERNAL_CONTENT_URI, id); - resolver.delete(deleteUri, null, null); - } else { - throw new AssertionError("No file to delete was found!"); - } - c.close(); - } else { - File file = new File(uri.getPath()); - boolean isDeleted = file.delete(); - SharedPreferences sharedPreferences = mainActivity.getSharedPreferences(SPECIFIC_FILETYPE_SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); - long id = sharedPreferences.getLong(uri.getPath(), -1); - if (id > -1) { - DownloadManager downloadManager = (DownloadManager) mainActivity.getBaseContext().getSystemService(DOWNLOAD_SERVICE); - downloadManager.remove(id); - } - if (!isDeleted) { - throw new AssertionError("No file to delete was found!"); - } - } - - return exportToOraFile(layers, fileName, bitmapAllLayers, resolver); - } - - public static BitmapReturnValue importOraFile(ContentResolver resolver, Uri uri) throws IOException { - BitmapFactory.Options options = new BitmapFactory.Options(); - InputStream inputStream = resolver.openInputStream(uri); - ZipInputStream zipInput = new ZipInputStream(inputStream); - List layers = new ArrayList<>(); - ZipEntry current = zipInput.getNextEntry(); - - while (current != null) { - if (current.getName().matches("data/(.*).png")) { - Bitmap layerBitmap = FileIO.enableAlpha(BitmapFactory.decodeStream(zipInput, null, options)); - - if (layerBitmap == null) { - throw new IOException("Cannot decode stream to bitmap!"); - } - - LayerContracts.Layer layer = new Layer(layerBitmap); - - current = zipInput.getNextEntry(); - if (current != null && current.getName().matches("alpha/(.*)")) { - int opacityPercentage = zipInput.read(); - layer.setOpacityPercentage(opacityPercentage); - current = zipInput.getNextEntry(); - } - - layers.add(layer); - } else { - current = zipInput.getNextEntry(); - } - } - - if (layers.isEmpty() || layers.size() > MAX_LAYERS) { - throw new IOException("Bitmap list is wrong!"); - } - return new BitmapReturnValue(layers, null, false); - } -} diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/iotasks/OpenRasterFileFormatConversion.kt b/Paintroid/src/main/java/org/catrobat/paintroid/iotasks/OpenRasterFileFormatConversion.kt new file mode 100644 index 0000000000..f197ee83df --- /dev/null +++ b/Paintroid/src/main/java/org/catrobat/paintroid/iotasks/OpenRasterFileFormatConversion.kt @@ -0,0 +1,256 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.iotasks + +import android.app.DownloadManager +import android.content.ContentResolver +import android.content.ContentUris +import android.content.ContentValues +import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.net.Uri +import android.os.Build +import android.os.Environment +import android.provider.MediaStore +import android.util.Log +import org.catrobat.paintroid.FileIO.enableAlpha +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.contract.LayerContracts +import org.catrobat.paintroid.model.Layer +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.IOException +import java.io.OutputStream +import java.math.BigInteger +import java.util.zip.ZipEntry +import java.util.zip.ZipInputStream +import java.util.zip.ZipOutputStream +import javax.xml.parsers.DocumentBuilderFactory +import javax.xml.parsers.ParserConfigurationException +import javax.xml.transform.TransformerException +import javax.xml.transform.TransformerFactory +import javax.xml.transform.dom.DOMSource +import javax.xml.transform.stream.StreamResult + +import org.catrobat.paintroid.common.MAX_LAYERS +import org.catrobat.paintroid.common.SPECIFIC_FILETYPE_SHARED_PREFERENCES_NAME +import java.util.* +import kotlin.collections.ArrayList + +// Source: https://www.openraster.org/baseline/file-layout-spec.html + +class OpenRasterFileFormatConversion private constructor() { + init { + throw AssertionError() + } + + companion object { + private val TAG = OpenRasterFileFormatConversion::class.java.simpleName + private const val COMPRESS_QUALITY = 100 + private const val THUMBNAIL_WIDTH = 256 + private const val THUMBNAIL_HEIGHT = 256 + private const val ORA_VERSION = "0.0.2" + private const val DATE_DIVIDER = 1000 + var mainActivity: MainActivity? = null + + @Throws(IOException::class) + fun exportToOraFile(layers: List, fileName: String, bitmapAllLayers: Bitmap?, resolver: ContentResolver?): Uri? { + val imageUri: Uri? + val contentValues = ContentValues() + var wholeSize: Float? = 0f + val mimeByteArray = "image/openraster".toByteArray() + val xmlByteArray = getXmlStack(layers) + for (current in layers) { + val byteArray = ByteArrayOutputStream().apply { current.bitmap.compress(Bitmap.CompressFormat.PNG, COMPRESS_QUALITY, this) + }.toByteArray() + wholeSize = wholeSize?.plus(byteArray.size.toFloat()) + val alphaByteArray = BigInteger.valueOf(current.opacityPercentage.toLong()).toByteArray() + wholeSize = wholeSize?.plus(alphaByteArray.size.toFloat()) + } + val bosMerged = ByteArrayOutputStream() + bitmapAllLayers?.compress(Bitmap.CompressFormat.PNG, COMPRESS_QUALITY, bosMerged) + val bitmapByteArray = bosMerged.toByteArray() + val bitmapThumb = bitmapAllLayers?.let { Bitmap.createScaledBitmap(it, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, false) } + val bosThumb = ByteArrayOutputStream() + bitmapThumb?.compress(Bitmap.CompressFormat.PNG, COMPRESS_QUALITY, bosThumb) + val bitmapThumbArray = bosThumb.toByteArray() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + contentValues.put(MediaStore.Files.FileColumns.DISPLAY_NAME, fileName) + contentValues.put(MediaStore.Files.FileColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS) + contentValues.put(MediaStore.Files.FileColumns.MIME_TYPE, "application/applefile") + imageUri = resolver?.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues) + } else { + val imageRoot = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + if (!imageRoot.exists() && !imageRoot.mkdirs()) { + imageRoot.mkdirs() + } + val uri = MediaStore.Files.getContentUri("external") + contentValues.put(MediaStore.Files.FileColumns.DATA, imageRoot.absolutePath + "/" + fileName) + contentValues.put(MediaStore.Files.FileColumns.DISPLAY_NAME, fileName) + contentValues.put(MediaStore.Files.FileColumns.MIME_TYPE, "application/zip") + contentValues.put(MediaStore.Files.FileColumns.MEDIA_TYPE, MediaStore.Files.FileColumns.MEDIA_TYPE_NONE) + val date = System.currentTimeMillis() + contentValues.put(MediaStore.MediaColumns.DATE_MODIFIED, date / DATE_DIVIDER) + wholeSize = xmlByteArray?.size?.toFloat()?.let { wholeSize?.plus(it) } + ?.plus(mimeByteArray.size.toFloat())?.plus(bitmapByteArray.size.toFloat())?.plus(bitmapThumbArray.size.toFloat()) + contentValues.put(MediaStore.Images.Media.SIZE, wholeSize) + val downloadManager = mainActivity?.baseContext?.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager + val id = wholeSize?.let { downloadManager.addCompletedDownload( + fileName, fileName, true, "application/zip", + imageRoot.absolutePath + "/" + fileName, it.toLong(), true) } + imageUri = resolver?.insert(uri, contentValues) + val sharedPreferences = mainActivity?.getSharedPreferences(SPECIFIC_FILETYPE_SHARED_PREFERENCES_NAME, 0) + id?.let { sharedPreferences?.edit()?.putLong(imageRoot.absolutePath + "/" + fileName, it)?.apply() } + } + val outputStream: OutputStream? = Objects.requireNonNull(imageUri)?.let { resolver?.openOutputStream(it) } + val streamZip = ZipOutputStream(outputStream) + val mimetypeEntry = ZipEntry("mimetype").apply { method = ZipEntry.DEFLATED } + streamZip.putNextEntry(mimetypeEntry) + streamZip.write(mimeByteArray, 0, mimeByteArray.size) + streamZip.closeEntry() + streamZip.putNextEntry(ZipEntry("stack.xml")) + Objects.requireNonNull(xmlByteArray)?.let { streamZip.write(xmlByteArray, 0, it.size) } + streamZip.closeEntry() + for ((counter, current) in layers.withIndex()) { + val bos = ByteArrayOutputStream() + current.bitmap.compress(Bitmap.CompressFormat.PNG, COMPRESS_QUALITY, bos) + val byteArray = bos.toByteArray() + streamZip.putNextEntry(ZipEntry("data/layer$counter.png")) + streamZip.write(byteArray, 0, byteArray.size) + streamZip.closeEntry() + streamZip.putNextEntry(ZipEntry("alpha/$counter")) + val alphaByteArray = BigInteger.valueOf(current.opacityPercentage.toLong()).toByteArray() + streamZip.write(alphaByteArray, 0, alphaByteArray.size) + streamZip.closeEntry() + } + streamZip.putNextEntry(ZipEntry("Thumbnails/thumbnail.png")) + streamZip.write(bitmapThumbArray, 0, bitmapThumbArray.size) + streamZip.closeEntry() + streamZip.putNextEntry(ZipEntry("mergedimage.png")) + streamZip.write(bitmapByteArray, 0, bitmapByteArray.size) + streamZip.closeEntry() + streamZip.close() + outputStream?.close() + return imageUri + } + + private fun getXmlStack(bitmapList: List): ByteArray? { + val docFactory = DocumentBuilderFactory.newInstance() + return try { + val docBuilder = docFactory.newDocumentBuilder() + val doc = docBuilder.newDocument() + val rootElement = doc.createElement("image") + val attr1 = doc.createAttribute("version") + val attr2 = doc.createAttribute("w") + val attr3 = doc.createAttribute("h") + attr1.value = ORA_VERSION + attr2.value = bitmapList[0].bitmap.width.toString() + attr3.value = bitmapList[0].bitmap.height.toString() + rootElement.setAttributeNode(attr1) + rootElement.setAttributeNode(attr2) + rootElement.setAttributeNode(attr3) + doc.appendChild(rootElement) + val stack = doc.createElement("stack") + rootElement.appendChild(stack) + for (i in bitmapList.indices.reversed()) { + val layer = doc.createElement("layer") + layer.setAttribute("name", "layer$i") + layer.setAttribute("src", "data/layer$i.png") + stack.appendChild(layer) + } + val stream = ByteArrayOutputStream() + val result = StreamResult(stream) + val transformerFactory = TransformerFactory.newInstance() + val transformer = transformerFactory.newTransformer() + val source = DOMSource(doc) + transformer.transform(source, result) + stream.toByteArray() + } catch (e: ParserConfigurationException) { + Log.e(TAG, "Could not create document.") + null + } catch (e: TransformerException) { + Log.e(TAG, "Could not transform Xml file.") + null + } + } + + @Throws(IOException::class) + fun saveOraFileToUri(layers: List, + uri: Uri, + fileName: String, + bitmapAllLayers: Bitmap?, + resolver: ContentResolver?): Uri? { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + val projection = arrayOf(MediaStore.Images.Media._ID) + val c = resolver?.query(uri, projection, null, null, null) + if (c?.moveToFirst() == true) { + val id = c.getLong(c.getColumnIndexOrThrow(MediaStore.Images.Media._ID)) + val deleteUri = ContentUris.withAppendedId(MediaStore.Downloads.EXTERNAL_CONTENT_URI, id) + resolver.delete(deleteUri, null, null) + } else { + throw AssertionError("No file to delete was found!") + } + c.close() + } else { + val file = File(uri.path.toString()) + val isDeleted = file.delete() + val sharedPreferences = mainActivity?.getSharedPreferences(SPECIFIC_FILETYPE_SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE) + val id = sharedPreferences?.getLong(uri.path, -1) + if (id != null && id > -1) { + val downloadManager = mainActivity?.baseContext?.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager + downloadManager.remove(id) + } + if (!isDeleted) { + throw AssertionError("No file to delete was found!") + } + } + return exportToOraFile(layers, fileName, bitmapAllLayers, resolver) + } + + @Throws(IOException::class) + fun importOraFile(resolver: ContentResolver, uri: Uri?): BitmapReturnValue { + val options = BitmapFactory.Options() + val inputStream = uri?.let { resolver.openInputStream(it) } + val zipInput = ZipInputStream(inputStream) + val layers: MutableList = ArrayList() + var current = zipInput.nextEntry + while (current != null) { + if (current.name.matches("data/(.*).png".toRegex())) { + val layerBitmap = enableAlpha(BitmapFactory.decodeStream(zipInput, null, options)) + ?: throw IOException("Cannot decode stream to bitmap!") + val layer: LayerContracts.Layer = Layer(layerBitmap) + current = zipInput.nextEntry + if (current != null && current.name.matches("alpha/(.*)".toRegex())) { + val opacityPercentage = zipInput.read() + layer.opacityPercentage = opacityPercentage + current = zipInput.nextEntry + } + layers.add(layer) + } else { + current = zipInput.nextEntry + } + } + if (layers.isEmpty() || layers.size > MAX_LAYERS) { + throw IOException("Bitmap list is wrong!") + } + return BitmapReturnValue(layers, null, false) + } + } +} diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/model/LayerModel.kt b/Paintroid/src/main/java/org/catrobat/paintroid/model/LayerModel.kt index aea2ac33a7..5940115640 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/model/LayerModel.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/model/LayerModel.kt @@ -62,17 +62,20 @@ open class LayerModel : LayerContracts.Model { false } + @Synchronized override fun getBitmapOfAllLayers(): Bitmap? { - if (layers.isEmpty()) { - return null - } - val referenceBitmap = layers[0].bitmap - val bitmap = Bitmap.createBitmap(referenceBitmap.width, referenceBitmap.height, Bitmap.Config.ARGB_8888) - val canvas = bitmap?.let { Canvas(it) } + synchronized(this) { + if (layers.isEmpty()) { + return null + } + val referenceBitmap = layers[0].bitmap + val bitmap = Bitmap.createBitmap(referenceBitmap.width, referenceBitmap.height, Bitmap.Config.ARGB_8888) + val canvas = bitmap?.let { Canvas(it) } - drawLayersOntoCanvas(canvas) + drawLayersOntoCanvas(canvas) - return bitmap + return bitmap + } } override fun getBitmapListOfAllLayers(): List = layers.map { it.bitmap } diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/presenter/LayerPresenter.kt b/Paintroid/src/main/java/org/catrobat/paintroid/presenter/LayerPresenter.kt index 937f8b61c2..2e5ae04488 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/presenter/LayerPresenter.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/presenter/LayerPresenter.kt @@ -160,7 +160,14 @@ class LayerPresenter( override fun isShown(): Boolean = layerMenuViewHolder.isShown() - override fun getLayerItem(position: Int): LayerContracts.Layer = layers[position] + override fun getLayerItem(position: Int): LayerContracts.Layer? { + if (isPositionValid(position)) { + return layers[position] + } else { + Log.w("LayerPresenter.kt", "LayerPresenter.getLayerItem(position) - tried to access position out of range of the layers array!") + return null + } + } override fun getLayerItemId(position: Int): Long = position.toLong() diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/presenter/MainActivityPresenter.kt b/Paintroid/src/main/java/org/catrobat/paintroid/presenter/MainActivityPresenter.kt index 229b3333d3..929a6f2cc5 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/presenter/MainActivityPresenter.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/presenter/MainActivityPresenter.kt @@ -19,6 +19,7 @@ package org.catrobat.paintroid.presenter import android.Manifest +import android.annotation.TargetApi import android.app.Activity import android.content.ContentResolver import android.content.ContentUris @@ -386,7 +387,22 @@ open class MainActivityPresenter( ) return } - if (navigator.isSdkAboveOrEqualQ) { + + @TargetApi(Build.VERSION_CODES.TIRAMISU) + if (navigator.isSdkAboveOrEqualT) { + if (!navigator.doIHavePermission(Manifest.permission.READ_MEDIA_IMAGES)) { + navigator.askForPermission( + arrayOf(Manifest.permission.READ_MEDIA_IMAGES), + requestCode + ) + } else { + handleRequestPermissionsResult( + requestCode, + arrayOf(Manifest.permission.READ_MEDIA_IMAGES), + intArrayOf(PackageManager.PERMISSION_GRANTED) + ) + } + } else if (navigator.isSdkAboveOrEqualQ) { if (!navigator.doIHavePermission(Manifest.permission.READ_EXTERNAL_STORAGE)) { navigator.askForPermission( arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), @@ -472,7 +488,9 @@ open class MainActivityPresenter( permissions: Array, grantResults: IntArray ) { - if (permissions.size == 1 && (permissions[0] == Manifest.permission.READ_EXTERNAL_STORAGE || permissions[0] == Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + if (permissions.size == 1 && (permissions[0] == Manifest.permission.READ_EXTERNAL_STORAGE || + permissions[0] == Manifest.permission.WRITE_EXTERNAL_STORAGE || + permissions[0] == Manifest.permission.READ_MEDIA_IMAGES)) { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { when (requestCode) { PERMISSION_EXTERNAL_STORAGE_SAVE -> { @@ -573,17 +591,19 @@ open class MainActivityPresenter( if (view.isKeyboardShown) { view.hideKeyboard() } else { - setBottomNavigationColor(Color.BLACK) - if (toolController.currentTool is LineTool) { - (toolController.currentTool as LineTool).undoChangePaintColor(Color.BLACK) + if (commandManager.isLastColorCommandOnTop() || commandManager.getColorCommandCount() == 0) { + toolController.currentTool?.changePaintColor(Color.BLACK) + setBottomNavigationColor(Color.BLACK) + } + if (toolController.currentTool is ClippingTool) { + val clippingTool = toolController.currentTool as ClippingTool + clippingToolPaint = clippingTool.drawPaint + commandManager.undo() + clippingToolInUseAndUndoRedoClicked = true } else { - if (toolController.currentTool is ClippingTool) { - val clippingTool = toolController.currentTool as ClippingTool - clippingToolPaint = clippingTool.drawPaint - commandManager.undo() - clippingToolInUseAndUndoRedoClicked = true + if (toolController.currentTool is LineTool) { + (toolController.currentTool as LineTool).undoChangePaintColor(Color.BLACK, false) } else { - toolController.currentTool?.changePaintColor(Color.BLACK) commandManager.undo() } } @@ -615,11 +635,12 @@ open class MainActivityPresenter( override fun showLayerMenuClicked() { idlingResource.increment() layerAdapter?.apply { - for (i in 0 until itemCount) { - val currentHolder = getViewHolderAt(i) + for (position in 0 until itemCount) { + val currentHolder = getViewHolderAt(position) currentHolder?.let { - if (it.bitmap != null) { - it.updateImageView(presenter.getLayerItem(i)) + val layer = presenter.getLayerItem(position) + if (it.bitmap != null && layer != null) { + it.updateImageView(layer) } } } @@ -1019,7 +1040,8 @@ open class MainActivityPresenter( if (bottomBarViewHolder.isVisible) { bottomBarViewHolder.hide() } else { - if (layerAdapter?.presenter?.getLayerItem(workspace.currentLayerIndex)?.isVisible == false) { + val layer = layerAdapter?.presenter?.getLayerItem(workspace.currentLayerIndex) + if (layer != null && !layer.isVisible) { navigator.showToast(R.string.no_tools_on_hidden_layer, Toast.LENGTH_SHORT) return } @@ -1050,7 +1072,7 @@ open class MainActivityPresenter( val cursor = fileActivity?.contentResolver?.query(uri, null, null, null, null) cursor?.use { if (it.moveToFirst()) { - result = it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME)) + result = it.getString(it.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)) } } } diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/tools/Tool.kt b/Paintroid/src/main/java/org/catrobat/paintroid/tools/Tool.kt index 5c320e929b..68680b6a37 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/tools/Tool.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/tools/Tool.kt @@ -40,7 +40,7 @@ interface Tool { fun handleUp(coordinate: PointF?): Boolean - fun changePaintColor(color: Int) + fun changePaintColor(color: Int, invalidate: Boolean = true) fun changePaintStrokeWidth(strokeWidth: Int) diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/tools/helper/JavaFillAlgorithm.kt b/Paintroid/src/main/java/org/catrobat/paintroid/tools/helper/JavaFillAlgorithm.kt index d716b25e70..9b461ba1ab 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/tools/helper/JavaFillAlgorithm.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/tools/helper/JavaFillAlgorithm.kt @@ -42,7 +42,7 @@ class JavaFillAlgorithm : FillAlgorithm { var targetColor = 0 @VisibleForTesting - var replacementColor = 0 + var colorToBeReplaced = 0 @VisibleForTesting var colorToleranceThresholdSquared = 0 @@ -70,7 +70,7 @@ class JavaFillAlgorithm : FillAlgorithm { filledPixels = Array(bitmap.height) { BooleanArray(bitmap.width) } this.clickedPixel = clickedPixel this.targetColor = targetColor - this.replacementColor = replacementColor + this.colorToBeReplaced = replacementColor colorToleranceThresholdSquared = square(colorToleranceThreshold.toInt()) considerTolerance = colorToleranceThreshold > 0 } @@ -97,9 +97,9 @@ class JavaFillAlgorithm : FillAlgorithm { private fun shouldCellBeFilled(row: Int, col: Int): Boolean = !filledPixels[row][col] && ( - pixels[row][col] == replacementColor || considerTolerance && isPixelWithinColorTolerance( + pixels[row][col] == colorToBeReplaced || considerTolerance && isPixelWithinColorTolerance( pixels[row][col], - replacementColor + colorToBeReplaced ) ) diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/BaseTool.kt b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/BaseTool.kt index f42992ef91..4a4d87416a 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/BaseTool.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/BaseTool.kt @@ -77,7 +77,7 @@ abstract class BaseTool( override fun onRestoreInstanceState(bundle: Bundle?) = Unit - override fun changePaintColor(@ColorInt color: Int) { + override fun changePaintColor(@ColorInt color: Int, invalidate: Boolean) { toolPaint.color = color } diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/BrushTool.kt b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/BrushTool.kt index ed45272653..b87e4b39a8 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/BrushTool.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/BrushTool.kt @@ -213,9 +213,9 @@ open class BrushTool( previousEventCoordinate = null } - override fun changePaintColor(color: Int) { - super.changePaintColor(color) - brushToolOptionsView.invalidate() + override fun changePaintColor(color: Int, invalidate: Boolean) { + super.changePaintColor(color, invalidate) + if (invalidate) brushToolOptionsView.invalidate() } private fun eventCoordinatesAreNull(): Boolean = @@ -243,6 +243,7 @@ open class BrushTool( val pathNew = AdvancedSettingsAlgorithms.smoothingAlgorithm(pointArray) val command = commandFactory.createPathCommand(bitmapPaint, pathNew) commandManager.addCommand(command) + commandManager.executeAllCommands() } pointArray.clear() diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/CursorTool.kt b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/CursorTool.kt index 7a8d62969f..6b044c6eb6 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/CursorTool.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/CursorTool.kt @@ -113,12 +113,12 @@ open class CursorTool( brushToolOptionsView.setStrokeCapButtonChecked(toolPaint.strokeCap) } - override fun changePaintColor(color: Int) { - super.changePaintColor(color) + override fun changePaintColor(color: Int, invalidate: Boolean) { + super.changePaintColor(color, invalidate) if (toolInDrawMode) { cursorToolSecondaryShapeColor = toolPaint.color } - brushToolOptionsView.invalidate() + if (invalidate) brushToolOptionsView.invalidate() } private fun hideToolOptions() { diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/DefaultToolFactory.kt b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/DefaultToolFactory.kt index d1f5771db0..ec89b04ace 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/DefaultToolFactory.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/DefaultToolFactory.kt @@ -131,6 +131,7 @@ class DefaultToolFactory(mainActivity: MainActivity) : ToolFactory { workspace, idlingResource, commandManager, + mainActivity.bottomNavigationViewHolder, DRAW_TIME_INIT ) ToolType.LINE -> LineTool( diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/EraserTool.kt b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/EraserTool.kt index e7f99fa095..356150109e 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/EraserTool.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/EraserTool.kt @@ -28,16 +28,18 @@ import org.catrobat.paintroid.tools.ToolType import org.catrobat.paintroid.tools.Workspace import org.catrobat.paintroid.tools.options.BrushToolOptionsView import org.catrobat.paintroid.tools.options.ToolOptionsViewController +import org.catrobat.paintroid.ui.viewholder.BottomNavigationViewHolder class EraserTool( - brushToolOptionsView: BrushToolOptionsView, - contextCallback: ContextCallback, - toolOptionsViewController: ToolOptionsViewController, - toolPaint: ToolPaint, - workspace: Workspace, - idlingResource: CountingIdlingResource, - commandManager: CommandManager, - drawTime: Long + brushToolOptionsView: BrushToolOptionsView, + contextCallback: ContextCallback, + toolOptionsViewController: ToolOptionsViewController, + toolPaint: ToolPaint, + workspace: Workspace, + idlingResource: CountingIdlingResource, + commandManager: CommandManager, + bottomNavigationViewHolder: BottomNavigationViewHolder, + drawTime: Long ) : BrushTool( brushToolOptionsView, contextCallback, @@ -48,10 +50,22 @@ class EraserTool( commandManager, drawTime ) { + + private var savedColor: Int + private var bottomNavigationViewHolder: BottomNavigationViewHolder + + init { + this.bottomNavigationViewHolder = bottomNavigationViewHolder + bottomNavigationViewHolder.enableColorItemView(false) + bottomNavigationViewHolder.setColorButtonColor(Color.TRANSPARENT) + savedColor = toolPaint.color + toolPaint.color = Color.TRANSPARENT + brushToolOptionsView.setCurrentPaint(toolPaint.paint) + } override val previewPaint: Paint get() = Paint().apply { set(super.previewPaint) - color = Color.BLACK + color = Color.TRANSPARENT shader = toolPaint.checkeredShader } @@ -64,4 +78,11 @@ class EraserTool( override val toolType: ToolType get() = ToolType.ERASER + + fun setSavedColor() { + bottomNavigationViewHolder.enableColorItemView(true) + bottomNavigationViewHolder.setColorButtonColor(savedColor) + toolPaint.color = savedColor + brushToolOptionsView.setCurrentPaint(toolPaint.paint) + } } diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/LineTool.kt b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/LineTool.kt index 8b1e3096e9..321777ecf0 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/LineTool.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/LineTool.kt @@ -393,8 +393,8 @@ class LineTool( } } - override fun changePaintColor(color: Int) { - super.changePaintColor(color) + override fun changePaintColor(color: Int, invalidate: Boolean) { + super.changePaintColor(color, invalidate) if (startpointSet && endpointSet) { val startX = startPointToDraw?.x val startY = startPointToDraw?.y @@ -418,13 +418,13 @@ class LineTool( } } } - brushToolOptionsView.invalidate() + if (invalidate) brushToolOptionsView.invalidate() } - fun undoChangePaintColor(color: Int) { + fun undoChangePaintColor(color: Int, invalidate: Boolean) { handleStateBeforeUndo() - super.changePaintColor(color) - brushToolOptionsView.invalidate() + super.changePaintColor(color, invalidate) + if (invalidate) brushToolOptionsView.invalidate() if (connectedLines) { commandManager.undoInConnectedLinesMode() } else { @@ -441,9 +441,9 @@ class LineTool( } } - fun undoColorChangedCommand(color: Int) { - super.changePaintColor(color) - brushToolOptionsView.invalidate() + fun undoColorChangedCommand(color: Int, invalidate: Boolean = true) { + super.changePaintColor(color, invalidate) + if (invalidate) brushToolOptionsView.invalidate() } override fun changePaintStrokeWidth(strokeWidth: Int) { diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/ShapeTool.kt b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/ShapeTool.kt index 51c6dc78da..7e772f11be 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/ShapeTool.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/ShapeTool.kt @@ -108,9 +108,9 @@ class ShapeTool( shapeDrawable = drawableFactory.createDrawable(shape) } - override fun changePaintColor(color: Int) { - super.changePaintColor(color) - workspace.invalidate() + override fun changePaintColor(color: Int, invalidate: Boolean) { + super.changePaintColor(color, invalidate) + if (invalidate) workspace.invalidate() } override fun onSaveInstanceState(bundle: Bundle?) { diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/SprayTool.kt b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/SprayTool.kt index 6180a0fd73..5aff0d38a5 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/SprayTool.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/SprayTool.kt @@ -50,7 +50,7 @@ private const val STROKE_WIDTH = 5f private const val CONSTANT_1 = 0.5f class SprayTool( - var stampToolOptionsView: SprayToolOptionsView, + var sprayToolOptionsView: SprayToolOptionsView, override var contextCallback: ContextCallback, toolOptionsViewController: ToolOptionsViewController, toolPaint: ToolPaint, @@ -86,15 +86,14 @@ class SprayTool( private val previewCanvas = Canvas(previewBitmap) init { - toolPaint.strokeWidth = STROKE_WIDTH - - stampToolOptionsView.setCallback(object : SprayToolOptionsView.Callback { + sprayToolOptionsView.setCallback(object : SprayToolOptionsView.Callback { override fun radiusChanged(radius: Int) { sprayRadius = DEFAULT_RADIUS + radius * 2 } }) - stampToolOptionsView.setCurrentPaint(toolPaint.paint) + sprayToolOptionsView.setCurrentPaint(toolPaint.paint) + toolPaint.strokeWidth = STROKE_WIDTH toolOptionsViewController.showDelayed() } @@ -168,7 +167,7 @@ class SprayTool( super.onRestoreInstanceState(bundle) bundle?.getInt(BUNDLE_RADIUS)?.let { radius -> sprayRadius = radius - stampToolOptionsView.setRadius(radius) + sprayToolOptionsView.setRadius(radius) } } @@ -193,6 +192,13 @@ class SprayTool( sprayToolScope.launch { while (true) { repeat(sprayRadius / DEFAULT_RADIUS) { + if (sprayedPoints.isEmpty()) { + val midPoint = createMidPoint() + if (workspace.contains(midPoint)) { + previewCanvas.drawPoint(midPoint.x, midPoint.y, drawPaint) + sprayedPoints.add(midPoint) + } + } val point = createRandomPointInCircle() if (workspace.contains(point)) { previewCanvas.drawPoint(point.x, point.y, drawPaint) @@ -221,4 +227,16 @@ class SprayTool( point.y = radius * sin(theta).toFloat() + (currentCoordinate?.y ?: 0f) return point } + + private fun createMidPoint(): PointF { + val pointM = PointF() + + pointM.x = currentCoordinate?.x ?: 0f + pointM.y = currentCoordinate?.y ?: 0f + return pointM + } + + fun resetRadiusToStrokeWidth() { + toolPaint.strokeWidth = sprayToolOptionsView.getRadius() + } } diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/TextTool.kt b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/TextTool.kt index d1ae411221..0377b2a677 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/TextTool.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/TextTool.kt @@ -443,8 +443,8 @@ class TextTool( } } - override fun changePaintColor(color: Int) { - super.changePaintColor(color) + override fun changePaintColor(color: Int, invalidate: Boolean) { + super.changePaintColor(color, invalidate) changeTextColor() } } diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/WatercolorTool.kt b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/WatercolorTool.kt index 4d7490f5fb..764eba62fe 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/WatercolorTool.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/WatercolorTool.kt @@ -59,10 +59,10 @@ class WatercolorTool( previewPaint.maskFilter = BlurMaskFilter(calcRange(previewPaint.alpha), BlurMaskFilter.Blur.INNER) } - override fun changePaintColor(color: Int) { - super.changePaintColor(color) + override fun changePaintColor(color: Int, invalidate: Boolean) { + super.changePaintColor(color, invalidate) - brushToolOptionsView.invalidate() + if (invalidate) brushToolOptionsView.invalidate() bitmapPaint.maskFilter = BlurMaskFilter(calcRange(bitmapPaint.alpha), BlurMaskFilter.Blur.INNER) previewPaint.maskFilter = BlurMaskFilter(calcRange(previewPaint.alpha), BlurMaskFilter.Blur.INNER) } diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/tools/options/SprayToolOptionsView.kt b/Paintroid/src/main/java/org/catrobat/paintroid/tools/options/SprayToolOptionsView.kt index 432f35302a..5095c96078 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/tools/options/SprayToolOptionsView.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/tools/options/SprayToolOptionsView.kt @@ -27,6 +27,8 @@ interface SprayToolOptionsView { fun setCurrentPaint(paint: Paint) + fun getRadius(): Float + interface Callback { fun radiusChanged(radius: Int) } diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/ui/LayerAdapter.kt b/Paintroid/src/main/java/org/catrobat/paintroid/ui/LayerAdapter.kt index 4e37085e80..f28cd9a47c 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/ui/LayerAdapter.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/ui/LayerAdapter.kt @@ -19,6 +19,8 @@ package org.catrobat.paintroid.ui import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.graphics.drawable.GradientDrawable import android.text.Editable import android.text.InputFilter import android.text.TextWatcher @@ -33,18 +35,20 @@ import android.widget.ImageView import android.widget.LinearLayout import android.widget.SeekBar import androidx.appcompat.widget.AppCompatEditText +import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.catrobat.paintroid.MainActivity import org.catrobat.paintroid.R import org.catrobat.paintroid.contract.LayerContracts +import org.catrobat.paintroid.iotasks.OpenRasterFileFormatConversion.Companion.mainActivity import org.catrobat.paintroid.model.MAX_LAYER_OPACITY_PERCENTAGE import org.catrobat.paintroid.tools.helper.DefaultNumberRangeFilter private const val MIN_VAL = 0 private const val MAX_VAL = 100 - +private const val CORNER_RADIUS = 20f private const val RESIZE_LENGTH = 400f class LayerAdapter( @@ -93,6 +97,9 @@ class LayerAdapter( override fun bindView() { val layer = layerPresenter.getLayerItem(position) + if (layer == null) { + return + } val isSelected = layer === layerPresenter.getSelectedLayer() setSelected(isSelected) setLayerVisibilityCheckbox(layer.isVisible) @@ -116,9 +123,9 @@ class LayerAdapter( true } - opacitySeekBar.progress = layerPresenter.getLayerItem(position).opacityPercentage + opacitySeekBar.progress = layer.opacityPercentage opacityEditText.filters = arrayOf(DefaultNumberRangeFilter(MIN_VAL, MAX_VAL)) - opacityEditText.setText(layerPresenter.getLayerItem(position).opacityPercentage.toString()) + opacityEditText.setText(layer.opacityPercentage.toString()) opacityEditText.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit @@ -139,7 +146,7 @@ class LayerAdapter( layerPresenter.changeLayerOpacity(position, opacityPercentage) } - layerPresenter.getLayerItem(position).opacityPercentage = opacityPercentage + layer.opacityPercentage = opacityPercentage layerPresenter.refreshDrawingSurface() } }) @@ -167,21 +174,20 @@ class LayerAdapter( } override fun setSelected(isSelected: Boolean) { - when (getBackgroundType(isSelected)) { - BackgroundType.TOP_SELECTED -> layerBackground.setBackgroundResource(R.drawable.layer_item_top_selected) - BackgroundType.TOP_UNSELECTED -> layerBackground.setBackgroundResource(R.drawable.layer_item_top_unselected) - BackgroundType.BTM_SELECTED -> layerBackground.setBackgroundResource(R.drawable.layer_item_btm_selected) - BackgroundType.BTM_UNSELECTED -> layerBackground.setBackgroundResource(R.drawable.layer_item_btm_unselected) - BackgroundType.CENTER_SELECTED -> layerBackground.setBackgroundResource(R.drawable.layer_item_center_selected) - BackgroundType.CENTER_UNSELECTED -> layerBackground.setBackgroundResource(R.drawable.layer_item_center_unselected) - BackgroundType.SINGLE_SELECTED -> layerBackground.setBackgroundResource(R.drawable.layer_item_single_selected) - BackgroundType.SINGLE_UNSELECTED -> layerBackground.setBackgroundResource(R.drawable.layer_item_single_unselected) + val background = when (getBackgroundType()) { + BackgroundType.SINGLE -> getSingleBackground() + BackgroundType.TOP -> getTopBackground(isSelected) + BackgroundType.BOTTOM -> getBottomBackground(isSelected) + BackgroundType.CENTER -> getCenterBackground(isSelected) } + layerBackground.background = background this.isSelected = isSelected } override fun isSelected(): Boolean = isSelected + override fun getViewLayout(): LinearLayout = layerBackground + override fun updateImageView(layer: LayerContracts.Layer) { runBlocking { launch { @@ -211,35 +217,86 @@ class LayerAdapter( override fun setMergable() = layerBackground.setBackgroundResource(R.color.pocketpaint_color_merge_layer) - private fun getBackgroundType(isSelected: Boolean): BackgroundType { + private fun getBackgroundType(): BackgroundType { if (presenter.layerCount > 2 && this.adapterPosition > 0 && this.adapterPosition < presenter.layerCount - 1) { - return if (isSelected) BackgroundType.CENTER_SELECTED else BackgroundType.CENTER_UNSELECTED + return BackgroundType.CENTER } - if (presenter.layerCount == 1) { - return if (isSelected) BackgroundType.SINGLE_SELECTED else BackgroundType.SINGLE_UNSELECTED + return BackgroundType.SINGLE } - if (this.adapterPosition == presenter.layerCount - 1) { - return if (isSelected) BackgroundType.BTM_SELECTED else BackgroundType.BTM_UNSELECTED + return BackgroundType.BOTTOM } - - return if (isSelected) BackgroundType.TOP_SELECTED else BackgroundType.TOP_UNSELECTED + return BackgroundType.TOP } } companion object { private val TAG = LayerAdapter::class.java.simpleName + + fun getSingleBackground(): Drawable { + val background = mainActivity?.let { ContextCompat.getDrawable(it, R.drawable.layer_item_single_selected) } + (background as GradientDrawable).cornerRadii = getRadius(BackgroundType.SINGLE) + return background + } + + fun getTopBackground(isSelected: Boolean): Drawable { + val background = if (isSelected) { + mainActivity?.let { ContextCompat.getDrawable(it, R.drawable.layer_item_top_selected) } + } else { + mainActivity?.let { ContextCompat.getDrawable(it, R.drawable.layer_item_top_unselected) } + } + (background as GradientDrawable).cornerRadii = getRadius(BackgroundType.TOP) + return background + } + + fun getBottomBackground(isSelected: Boolean): Drawable { + val background = if (isSelected) { + mainActivity?.let { ContextCompat.getDrawable(it, R.drawable.layer_item_bottom_selected) } + } else { + mainActivity?.let { ContextCompat.getDrawable(it, R.drawable.layer_item_bottom_unselected) } + } + (background as GradientDrawable).cornerRadii = getRadius(BackgroundType.BOTTOM) + return background + } + + fun getCenterBackground(isSelected: Boolean): Drawable? { + return if (isSelected) { + mainActivity?.let { ContextCompat.getDrawable(it, R.drawable.layer_item_center_selected) } + } else { + mainActivity?.let { ContextCompat.getDrawable(it, R.drawable.layer_item_center_unselected) } + } + } + + private fun getRadius(backgroundType: BackgroundType): FloatArray { + val isRTL = mainActivity?.resources?.configuration?.layoutDirection == View.LAYOUT_DIRECTION_RTL + val cornerRadius = mainActivity?.let { CORNER_RADIUS * it.resources.displayMetrics.density } ?: 0f + + return when (backgroundType) { + BackgroundType.TOP -> if (isRTL) { + floatArrayOf(0f, 0f, cornerRadius, cornerRadius, 0f, 0f, 0f, 0f) + } else { + floatArrayOf(cornerRadius, cornerRadius, 0f, 0f, 0f, 0f, 0f, 0f) + } + BackgroundType.BOTTOM -> if (isRTL) { + floatArrayOf(0f, 0f, 0f, 0f, cornerRadius, cornerRadius, 0f, 0f) + } else { + floatArrayOf(0f, 0f, 0f, 0f, 0f, 0f, cornerRadius, cornerRadius) + } + BackgroundType.SINGLE -> if (isRTL) { + floatArrayOf(0f, 0f, cornerRadius, cornerRadius, cornerRadius, cornerRadius, 0f, 0f) + } else { + floatArrayOf(cornerRadius, cornerRadius, 0f, 0f, 0f, 0f, cornerRadius, cornerRadius) + } + else -> floatArrayOf(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f) + } + } } enum class BackgroundType { - TOP_SELECTED, - TOP_UNSELECTED, - BTM_SELECTED, - BTM_UNSELECTED, - CENTER_SELECTED, - CENTER_UNSELECTED, - SINGLE_SELECTED, - SINGLE_UNSELECTED + TOP, + BOTTOM, + CENTER, + SINGLE } } diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/ui/MainActivityNavigator.kt b/Paintroid/src/main/java/org/catrobat/paintroid/ui/MainActivityNavigator.kt index 3136f1cc98..8dee39775e 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/ui/MainActivityNavigator.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/ui/MainActivityNavigator.kt @@ -44,51 +44,51 @@ import org.catrobat.paintroid.colorpicker.OnColorPickedListener import org.catrobat.paintroid.command.CommandFactory import org.catrobat.paintroid.command.implementation.DefaultCommandFactory import org.catrobat.paintroid.common.ABOUT_DIALOG_FRAGMENT_TAG -import org.catrobat.paintroid.common.ADVANCED_SETTINGS_DIALOG_FRAGMENT_TAG -import org.catrobat.paintroid.common.CATROBAT_INFORMATION_DIALOG_TAG import org.catrobat.paintroid.common.CATROID_MEDIA_GALLERY_FRAGMENT_TAG import org.catrobat.paintroid.common.COLOR_PICKER_DIALOG_TAG +import org.catrobat.paintroid.common.LIKE_US_DIALOG_FRAGMENT_TAG +import org.catrobat.paintroid.common.RATE_US_DIALOG_FRAGMENT_TAG import org.catrobat.paintroid.common.FEEDBACK_DIALOG_FRAGMENT_TAG -import org.catrobat.paintroid.common.INDETERMINATE_PROGRESS_DIALOG_TAG +import org.catrobat.paintroid.common.ZOOM_WINDOW_SETTINGS_DIALOG_FRAGMENT_TAG +import org.catrobat.paintroid.common.ADVANCED_SETTINGS_DIALOG_FRAGMENT_TAG +import org.catrobat.paintroid.common.OVERWRITE_INFORMATION_DIALOG_TAG +import org.catrobat.paintroid.common.PNG_INFORMATION_DIALOG_TAG import org.catrobat.paintroid.common.JPG_INFORMATION_DIALOG_TAG -import org.catrobat.paintroid.common.LIKE_US_DIALOG_FRAGMENT_TAG -import org.catrobat.paintroid.common.LOAD_DIALOG_FRAGMENT_TAG -import org.catrobat.paintroid.common.MainActivityConstants.ActivityRequestCode import org.catrobat.paintroid.common.ORA_INFORMATION_DIALOG_TAG -import org.catrobat.paintroid.common.OVERWRITE_INFORMATION_DIALOG_TAG +import org.catrobat.paintroid.common.CATROBAT_INFORMATION_DIALOG_TAG +import org.catrobat.paintroid.common.INDETERMINATE_PROGRESS_DIALOG_TAG import org.catrobat.paintroid.common.PAINTROID_PICTURE_PATH -import org.catrobat.paintroid.common.PERMISSION_DIALOG_FRAGMENT_TAG -import org.catrobat.paintroid.common.PERMISSION_EXTERNAL_STORAGE_SAVE_COPY -import org.catrobat.paintroid.common.PNG_INFORMATION_DIALOG_TAG -import org.catrobat.paintroid.common.RATE_US_DIALOG_FRAGMENT_TAG import org.catrobat.paintroid.common.SAVE_DIALOG_FRAGMENT_TAG -import org.catrobat.paintroid.common.SAVE_INFORMATION_DIALOG_TAG +import org.catrobat.paintroid.common.LOAD_DIALOG_FRAGMENT_TAG +import org.catrobat.paintroid.common.PERMISSION_DIALOG_FRAGMENT_TAG import org.catrobat.paintroid.common.SAVE_QUESTION_FRAGMENT_TAG import org.catrobat.paintroid.common.SCALE_IMAGE_FRAGMENT_TAG -import org.catrobat.paintroid.common.ZOOM_WINDOW_SETTINGS_DIALOG_FRAGMENT_TAG +import org.catrobat.paintroid.common.PERMISSION_EXTERNAL_STORAGE_SAVE_COPY +import org.catrobat.paintroid.common.SAVE_INFORMATION_DIALOG_TAG +import org.catrobat.paintroid.common.MainActivityConstants.ActivityRequestCode import org.catrobat.paintroid.contract.MainActivityContracts -import org.catrobat.paintroid.dialog.AboutDialog +import org.catrobat.paintroid.dialog.FeedbackDialog +import org.catrobat.paintroid.dialog.ZoomWindowSettingsDialog import org.catrobat.paintroid.dialog.AdvancedSettingsDialog +import org.catrobat.paintroid.dialog.OverwriteDialog +import org.catrobat.paintroid.dialog.PngInfoDialog +import org.catrobat.paintroid.dialog.JpgInfoDialog +import org.catrobat.paintroid.dialog.OraInfoDialog import org.catrobat.paintroid.dialog.CatrobatImageInfoDialog -import org.catrobat.paintroid.dialog.FeedbackDialog import org.catrobat.paintroid.dialog.ImportImageDialog import org.catrobat.paintroid.dialog.IndeterminateProgressDialog import org.catrobat.paintroid.dialog.InfoDialog -import org.catrobat.paintroid.dialog.JpgInfoDialog -import org.catrobat.paintroid.dialog.LikeUsDialog -import org.catrobat.paintroid.dialog.OraInfoDialog -import org.catrobat.paintroid.dialog.OverwriteDialog -import org.catrobat.paintroid.dialog.PermanentDenialPermissionInfoDialog import org.catrobat.paintroid.dialog.PermissionInfoDialog -import org.catrobat.paintroid.dialog.PermissionInfoDialog.PermissionType -import org.catrobat.paintroid.dialog.PngInfoDialog -import org.catrobat.paintroid.dialog.RateUsDialog +import org.catrobat.paintroid.dialog.PermanentDenialPermissionInfoDialog import org.catrobat.paintroid.dialog.SaveBeforeFinishDialog -import org.catrobat.paintroid.dialog.SaveBeforeLoadImageDialog import org.catrobat.paintroid.dialog.SaveBeforeNewImageDialog -import org.catrobat.paintroid.dialog.SaveInformationDialog +import org.catrobat.paintroid.dialog.SaveBeforeLoadImageDialog import org.catrobat.paintroid.dialog.ScaleImageOnLoadDialog -import org.catrobat.paintroid.dialog.ZoomWindowSettingsDialog +import org.catrobat.paintroid.dialog.AboutDialog +import org.catrobat.paintroid.dialog.LikeUsDialog +import org.catrobat.paintroid.dialog.PermissionInfoDialog.PermissionType +import org.catrobat.paintroid.dialog.RateUsDialog +import org.catrobat.paintroid.dialog.SaveInformationDialog import org.catrobat.paintroid.tools.ToolReference import org.catrobat.paintroid.tools.ToolType import org.catrobat.paintroid.ui.fragments.CatroidMediaGalleryFragment @@ -105,6 +105,10 @@ class MainActivityNavigator( @SuppressLint("AnnotateVersionCheck") get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q + override val isSdkAboveOrEqualT: Boolean + @SuppressLint("AnnotateVersionCheck") + get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU + private var commandFactory: CommandFactory = DefaultCommandFactory() private fun showFragment( @@ -190,7 +194,7 @@ class MainActivityNavigator( val queryCursor = mainActivity.contentResolver.query(uri, null, null, null, null) queryCursor.use { cursor -> if (cursor != null && cursor.moveToFirst()) { - result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)) + result = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)) } } } diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/ui/Perspective.kt b/Paintroid/src/main/java/org/catrobat/paintroid/ui/Perspective.kt index 0b3395142f..a04254d40a 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/ui/Perspective.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/ui/Perspective.kt @@ -104,7 +104,7 @@ open class Perspective(private var bitmapWidth: Int, private var bitmapHeight: I surfaceWidth = right surfaceCenterX = exactCenterX() surfaceHeight = bottom - surfaceCenterY = exactCenterY() + surfaceCenterY = getExactCenterYIgnoreWindowResize(surfaceFrame.exactCenterY()) } } @@ -211,4 +211,13 @@ open class Perspective(private var bitmapWidth: Int, private var bitmapHeight: I canvas.scale(surfaceScale, surfaceScale, surfaceCenterX, surfaceCenterY) canvas.translate(surfaceTranslationX, surfaceTranslationY) } + + private fun getExactCenterYIgnoreWindowResize(actualExactCenterY: Float): Float { + var exactCenterYIgnoreWindowResize = if (surfaceCenterY != 0.0f && surfaceCenterY > actualExactCenterY) { + surfaceCenterY + } else { + actualExactCenterY + } + return exactCenterYIgnoreWindowResize + } } diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/ui/tools/DefaultSprayToolOptionsView.kt b/Paintroid/src/main/java/org/catrobat/paintroid/ui/tools/DefaultSprayToolOptionsView.kt index e3bc3d6ad0..b4c42cf184 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/ui/tools/DefaultSprayToolOptionsView.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/ui/tools/DefaultSprayToolOptionsView.kt @@ -31,6 +31,7 @@ import com.google.android.material.textfield.TextInputEditText import org.catrobat.paintroid.R import org.catrobat.paintroid.tools.helper.DefaultNumberRangeFilter import org.catrobat.paintroid.tools.options.SprayToolOptionsView +import java.util.Locale const val MIN_RADIUS = 1 private const val DEFAULT_RADIUS = 5 @@ -107,9 +108,17 @@ class DefaultSprayToolOptionsView(rootView: ViewGroup) : SprayToolOptionsView { override fun setRadius(radius: Int) { radiusSeekBar.progress = radius + radiusText.setText( + String.format( + Locale.getDefault(), "%d", + radius + ) + ) } override fun setCurrentPaint(paint: Paint) { setRadius(paint.strokeWidth.toInt()) } + + override fun getRadius(): Float = radiusSeekBar.progress.toFloat() } diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/ui/viewholder/BottomNavigationViewHolder.kt b/Paintroid/src/main/java/org/catrobat/paintroid/ui/viewholder/BottomNavigationViewHolder.kt index 3b71859cb0..7c10be5ccc 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/ui/viewholder/BottomNavigationViewHolder.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/ui/viewholder/BottomNavigationViewHolder.kt @@ -43,13 +43,14 @@ class BottomNavigationViewHolder( layout.findViewById(R.id.pocketpaint_bottom_navigation) private val bottomNavigation: BottomNavigationAppearance private val colorButton: ImageView + private val colorItemView: BottomNavigationItemView init { bottomNavigation = setAppearance(context) val bottomNavigationMenuView = bottomNavigationView.getChildAt(0) as BottomNavigationMenuView - val item = bottomNavigationMenuView.getChildAt(2) as BottomNavigationItemView - colorButton = item.findViewById(R.id.icon) + colorItemView = bottomNavigationMenuView.getChildAt(2) as BottomNavigationItemView + colorButton = colorItemView.findViewById(R.id.icon) initColorButton() } @@ -65,6 +66,10 @@ class BottomNavigationViewHolder( toolType?.let { bottomNavigation.showCurrentTool(it) } } + override fun enableColorItemView(show: Boolean) { + colorItemView.isClickable = show + } + override fun setColorButtonColor(color: Int) { colorButton.setColorFilter(color) } diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/ui/zoomwindow/DefaultZoomWindowController.kt b/Paintroid/src/main/java/org/catrobat/paintroid/ui/zoomwindow/DefaultZoomWindowController.kt index ca2cc76f44..4f2f3d8e9c 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/ui/zoomwindow/DefaultZoomWindowController.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/ui/zoomwindow/DefaultZoomWindowController.kt @@ -221,27 +221,20 @@ class DefaultZoomWindowController } override fun checkIfToolCompatibleWithZoomWindow(tool: Tool?): Constants { - if ( - tool?.toolType?.name.equals(ToolType.HAND.name) || - tool?.toolType?.name.equals(ToolType.FILL.name) || - tool?.toolType?.name.equals(ToolType.CLIPBOARD.name) || - tool?.toolType?.name.equals(ToolType.TRANSFORM.name) - ) { - return Constants.NOT_COMPATIBLE - } else if ( - tool?.toolType?.name.equals(ToolType.IMPORTPNG.name) || - tool?.toolType?.name.equals(ToolType.SHAPE.name) || - tool?.toolType?.name.equals(ToolType.TEXT.name) - ) { - return Constants.NOT_COMPATIBLE - } else if ( - tool?.toolType?.name.equals(ToolType.LINE.name) || - tool?.toolType?.name.equals(ToolType.CURSOR.name) || - tool?.toolType?.name.equals(ToolType.WATERCOLOR.name) - ) { - return Constants.COMPATIBLE_NEW - } else { - return Constants.COMPATIBLE_ALL + return when (tool?.toolType?.name) { + ToolType.HAND.name, + ToolType.FILL.name, + ToolType.CLIPBOARD.name, + ToolType.TRANSFORM.name, + ToolType.IMPORTPNG.name, + ToolType.SHAPE.name, + ToolType.TEXT.name -> Constants.NOT_COMPATIBLE + ToolType.LINE.name, + ToolType.CURSOR.name, + ToolType.WATERCOLOR.name, + ToolType.SPRAY.name, + ToolType.BRUSH.name -> Constants.COMPATIBLE_NEW + else -> Constants.COMPATIBLE_ALL } } diff --git a/Paintroid/src/main/res/drawable/layer_item_bottom_selected.xml b/Paintroid/src/main/res/drawable/layer_item_bottom_selected.xml new file mode 100644 index 0000000000..07cd7deb00 --- /dev/null +++ b/Paintroid/src/main/res/drawable/layer_item_bottom_selected.xml @@ -0,0 +1,6 @@ + + + + diff --git a/Paintroid/src/main/res/drawable/layer_item_btm_unselected.xml b/Paintroid/src/main/res/drawable/layer_item_bottom_unselected.xml similarity index 79% rename from Paintroid/src/main/res/drawable/layer_item_btm_unselected.xml rename to Paintroid/src/main/res/drawable/layer_item_bottom_unselected.xml index 8fa80b0b07..5ddb3983d1 100644 --- a/Paintroid/src/main/res/drawable/layer_item_btm_unselected.xml +++ b/Paintroid/src/main/res/drawable/layer_item_bottom_unselected.xml @@ -3,6 +3,4 @@ android:shape="rectangle"> - diff --git a/Paintroid/src/main/res/drawable/layer_item_single_selected.xml b/Paintroid/src/main/res/drawable/layer_item_single_selected.xml index 1212b2cc28..07cd7deb00 100644 --- a/Paintroid/src/main/res/drawable/layer_item_single_selected.xml +++ b/Paintroid/src/main/res/drawable/layer_item_single_selected.xml @@ -3,7 +3,4 @@ android:shape="rectangle"> - diff --git a/Paintroid/src/main/res/drawable/layer_item_top_selected.xml b/Paintroid/src/main/res/drawable/layer_item_top_selected.xml index ad0b9c60dc..07cd7deb00 100644 --- a/Paintroid/src/main/res/drawable/layer_item_top_selected.xml +++ b/Paintroid/src/main/res/drawable/layer_item_top_selected.xml @@ -3,6 +3,4 @@ android:shape="rectangle"> - diff --git a/Paintroid/src/main/res/drawable/layer_item_top_unselected.xml b/Paintroid/src/main/res/drawable/layer_item_top_unselected.xml index 161d0325a8..5ddb3983d1 100644 --- a/Paintroid/src/main/res/drawable/layer_item_top_unselected.xml +++ b/Paintroid/src/main/res/drawable/layer_item_top_unselected.xml @@ -3,6 +3,4 @@ android:shape="rectangle"> - diff --git a/Paintroid/src/main/res/drawable/layer_nav_view_background.xml b/Paintroid/src/main/res/drawable/layer_nav_view_background_ltr.xml similarity index 100% rename from Paintroid/src/main/res/drawable/layer_nav_view_background.xml rename to Paintroid/src/main/res/drawable/layer_nav_view_background_ltr.xml diff --git a/Paintroid/src/main/res/drawable/layer_item_single_unselected.xml b/Paintroid/src/main/res/drawable/layer_nav_view_background_rtl.xml similarity index 54% rename from Paintroid/src/main/res/drawable/layer_item_single_unselected.xml rename to Paintroid/src/main/res/drawable/layer_nav_view_background_rtl.xml index b02ec9dc83..8734a0c54d 100644 --- a/Paintroid/src/main/res/drawable/layer_item_single_unselected.xml +++ b/Paintroid/src/main/res/drawable/layer_nav_view_background_rtl.xml @@ -2,8 +2,8 @@ + android:color="@color/cardview_dark_background" /> + android:topRightRadius="20dp" + android:bottomRightRadius="20dp"/> diff --git a/Paintroid/src/main/res/drawable/rounded_corner_top_left.xml b/Paintroid/src/main/res/drawable/rounded_corner_top_ltr.xml similarity index 100% rename from Paintroid/src/main/res/drawable/rounded_corner_top_left.xml rename to Paintroid/src/main/res/drawable/rounded_corner_top_ltr.xml diff --git a/Paintroid/src/main/res/drawable/layer_item_btm_selected.xml b/Paintroid/src/main/res/drawable/rounded_corner_top_rtl.xml similarity index 84% rename from Paintroid/src/main/res/drawable/layer_item_btm_selected.xml rename to Paintroid/src/main/res/drawable/rounded_corner_top_rtl.xml index 571b79d5e2..37651060d3 100644 --- a/Paintroid/src/main/res/drawable/layer_item_btm_selected.xml +++ b/Paintroid/src/main/res/drawable/rounded_corner_top_rtl.xml @@ -4,5 +4,5 @@ + android:topRightRadius="20dp"/> diff --git a/Paintroid/src/main/res/layout-land/activity_pocketpaint_main.xml b/Paintroid/src/main/res/layout-land/activity_pocketpaint_main.xml index 20dbeca2a9..60982a4c19 100644 --- a/Paintroid/src/main/res/layout-land/activity_pocketpaint_main.xml +++ b/Paintroid/src/main/res/layout-land/activity_pocketpaint_main.xml @@ -101,7 +101,7 @@ diff --git a/Paintroid/src/main/res/layout/activity_pocketpaint_main.xml b/Paintroid/src/main/res/layout/activity_pocketpaint_main.xml index 745fecc086..06947a46fe 100644 --- a/Paintroid/src/main/res/layout/activity_pocketpaint_main.xml +++ b/Paintroid/src/main/res/layout/activity_pocketpaint_main.xml @@ -97,7 +97,7 @@ (interactor).saveImage( presenter!!, SAVE_IMAGE_LOAD_NEW, workspace!!.layerModel, commandSerializer!!, FileIO.storeImageUri, @@ -1445,20 +1446,20 @@ class MainActivityPresenterTest { @Test fun testSaveAndLoadImagePermissionNotGranted() { Mockito.`when`(navigator!!.doIHavePermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) - .thenReturn(false) + .thenReturn(false) Mockito.`when`(navigator.isSdkAboveOrEqualM).thenReturn(false).thenReturn(true) presenter!!.saveBeforeLoadImage() Mockito.verify(navigator) - .showSaveImageInformationDialogWhenStandalone( - PERMISSION_EXTERNAL_STORAGE_SAVE_CONFIRMED_LOAD_NEW, - sharedPreferences!!.preferenceImageNumber, - false - ) + .showSaveImageInformationDialogWhenStandalone( + PERMISSION_EXTERNAL_STORAGE_SAVE_CONFIRMED_LOAD_NEW, + sharedPreferences!!.preferenceImageNumber, + false + ) presenter!!.switchBetweenVersions(PERMISSION_EXTERNAL_STORAGE_SAVE_CONFIRMED_LOAD_NEW) Mockito.verify(navigator).askForPermission( - arrayOf( - Manifest.permission.WRITE_EXTERNAL_STORAGE - ), PERMISSION_EXTERNAL_STORAGE_SAVE_CONFIRMED_LOAD_NEW + arrayOf( + Manifest.permission.WRITE_EXTERNAL_STORAGE + ), PERMISSION_EXTERNAL_STORAGE_SAVE_CONFIRMED_LOAD_NEW ) } diff --git a/Paintroid/src/test/java/org/catrobat/paintroid/test/ui/PerspectiveTests.kt b/Paintroid/src/test/java/org/catrobat/paintroid/test/ui/PerspectiveTests.kt index dffda15475..81d7c2aa2b 100644 --- a/Paintroid/src/test/java/org/catrobat/paintroid/test/ui/PerspectiveTests.kt +++ b/Paintroid/src/test/java/org/catrobat/paintroid/test/ui/PerspectiveTests.kt @@ -142,6 +142,12 @@ class PerspectiveTests { perspective?.scale?.let { Assert.assertEquals(maxScale, it, Float.MIN_VALUE) } } + @Test + fun testSurfaceCenterYNotChangedWhenSurfaceFrameCenterYIsLower() { + perspective?.setSurfaceFrame(Rect(0, 0, SURFACE_WIDTH, SURFACE_HEIGHT - 20)) + Assert.assertEquals(50.0f, perspective?.surfaceCenterY) + } + companion object { private const val SURFACE_WIDTH = 10 private const val SURFACE_HEIGHT = 100 diff --git a/app/build.gradle b/app/build.gradle index 670b1f7dff..2c7e76738b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -84,6 +84,12 @@ android { signingConfig signingConfigs.signedRelease } } + + publishing { + singleVariant('release') { + withSourcesJar() + } + } } tasks.withType(Detekt).configureEach { diff --git a/build.gradle b/build.gradle index 9734277b42..ab728a00a2 100644 --- a/build.gradle +++ b/build.gradle @@ -41,14 +41,14 @@ plugins { } ext { - androidCompileSdkVersion = 30 + androidCompileSdkVersion = 33 androidMinSdkVersion = 21 - androidTargetSdkVersion = 31 + androidTargetSdkVersion = 33 androidSupportLibraryVersion = '28.0.0' - androidVersionCode = 50 - androidVersionName = '2.10.1' + androidVersionCode = 51 + androidVersionName = '2.11.0' } if(project.hasProperty("snapshot")) { diff --git a/colorpicker/src/main/res/drawable-hdpi/ic_color_picker_tab_hsv.png b/colorpicker/src/main/res/drawable-hdpi/ic_color_picker_tab_hsv.png deleted file mode 100644 index b16683e48f..0000000000 Binary files a/colorpicker/src/main/res/drawable-hdpi/ic_color_picker_tab_hsv.png and /dev/null differ diff --git a/colorpicker/src/main/res/drawable-mdpi/ic_color_picker_tab_hsv.png b/colorpicker/src/main/res/drawable-mdpi/ic_color_picker_tab_hsv.png deleted file mode 100644 index e02e22cc64..0000000000 Binary files a/colorpicker/src/main/res/drawable-mdpi/ic_color_picker_tab_hsv.png and /dev/null differ diff --git a/colorpicker/src/main/res/drawable-xhdpi/ic_color_picker_tab_hsv.png b/colorpicker/src/main/res/drawable-xhdpi/ic_color_picker_tab_hsv.png deleted file mode 100644 index 65d94d408d..0000000000 Binary files a/colorpicker/src/main/res/drawable-xhdpi/ic_color_picker_tab_hsv.png and /dev/null differ diff --git a/colorpicker/src/main/res/drawable-xxhdpi/ic_color_picker_tab_hsv.png b/colorpicker/src/main/res/drawable-xxhdpi/ic_color_picker_tab_hsv.png deleted file mode 100644 index 40593b21ed..0000000000 Binary files a/colorpicker/src/main/res/drawable-xxhdpi/ic_color_picker_tab_hsv.png and /dev/null differ diff --git a/colorpicker/src/main/res/drawable/ic_color_picker_tab_hsv.xml b/colorpicker/src/main/res/drawable/ic_color_picker_tab_hsv.xml new file mode 100644 index 0000000000..3eb1dfbb9b --- /dev/null +++ b/colorpicker/src/main/res/drawable/ic_color_picker_tab_hsv.xml @@ -0,0 +1,2323 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index a6d3061fad..5e9758afa7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,4 @@ android.enableJetifier=true android.useAndroidX=true -org.gradle.jvmargs=-XX:MaxPermSize=1024m -Xmx4096m \ No newline at end of file +org.gradle.jvmargs=-XX:MaxPermSize=1024m -Xmx4096m +android.disableAutomaticComponentCreation=true \ No newline at end of file