diff --git a/Jenkinsfile b/Jenkinsfile index e3bec1ca885..01ca2dc3b20 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -34,7 +34,7 @@ def webTestUrlParameter() { def allFlavoursParameters() { return env.BUILD_ALL_FLAVOURS?.toBoolean() ? 'assembleCreateAtSchoolDebug ' + - 'assembleLunaAndCatDebug assemblePhiroDebug assembleArduinoDebug' : '' + 'assembleLunaAndCatDebug assemblePhiroDebug assembleEmbroideryDesignerDebug' : '' } def useDebugLabelParameter(defaultLabel) { diff --git a/Jenkinsfile.OutgoingNetworkCallsTests b/Jenkinsfile.OutgoingNetworkCallsTests index aab8bb3531d..f332983ae57 100644 --- a/Jenkinsfile.OutgoingNetworkCallsTests +++ b/Jenkinsfile.OutgoingNetworkCallsTests @@ -38,7 +38,7 @@ pipeline { step([$class: 'LogParserPublisher', failBuildOnError: true, projectRulePath: 'buildScripts/log_parser_rules', unstableOnWarning: true, useProjectRule: true]) } unsuccessful { - notifyChat(['#s2cc', '#catroweb-deployment']) + notifyChat(['#system-tests']) } } } diff --git a/Jenkinsfile.releaseAPK b/Jenkinsfile.releaseAPK index 6e8195ac423..9326a48ccde 100644 --- a/Jenkinsfile.releaseAPK +++ b/Jenkinsfile.releaseAPK @@ -96,8 +96,8 @@ pipeline { sh ''' fastlane android upload_APK_Catroid fastlane android upload_APK_CreateAtSchool + fastlane android upload_APK_EmbroideryDesigner fastlane android upload_APK_LunaAndCat - fastlane android upload_APK_Arduino fastlane android upload_APK_Phiro ''' } else { diff --git a/README.md b/README.md index c110d8122a6..342a44bf1dc 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ Catroid, also known as **Pocket Code**, is an on-device visual programming system for Android devices. -**Catrobat** is a visual programming language and a set of creativity tools for smartphones, tablets, and mobile browsers. -Catrobat programs can be written by using the Catroid programming system on Android phones and tablets. +**Catrobat** is a visual programming language and a set of creativity tools for smartphones. +Catrobat programs can be written by using Catrobat's Android and iOS apps. -For more information [oriented towards developers], check out our [developers page](http://developer.catrobat.org/). +For more information [oriented towards developers], check out our [developers page](https://developer.catrobat.org/). # Issues # @@ -17,7 +17,7 @@ For reporting issues use our [JIRA Bugtracking System](https://jira.catrob.at/se If you want to contribute we suggest that you start with [forking](https://help.github.com/articles/fork-a-repo/) our repository and browse the code. Then you can look at our [Issue-Tracker](https://jira.catrob.at/secure/RapidBoard.jspa?rapidView=60) and start with fixing one ticket. We strictly use [Test-Driven Development](http://c2.com/cgi/wiki?TestDrivenDevelopment) and [Clean Code](http://www.planetgeek.ch/wp-content/uploads/2013/06/Clean-Code-V2.2.pdf), so first read everything you can about these development methods. Code developed in a different style will not be accepted. After you've created a pull request we will review your code and do a full testrun on your branch. -If you want to implement a new feature, please ask about the details on http://catrob.at/mailinglist +If you want to implement a new feature, please ask about the details on https://catrob.at/mailinglist Start setting up the working environment by following the instructions: https://github.com/Catrobat/Catroid/wiki/Setup-working-environment @@ -25,15 +25,15 @@ Also make sure to read our guidelines for [creating a pull request](https://gith # Resources and links # -* [Google Play Store Download](https://play.google.com/store/apps/details?id=org.catrobat.catroid) -* [Community website with sample programs](https://pocketcode.org/) +* [Google Play Store Download](https://catrob.at/gp) +* [Community website with sample programs](https://share.catrob.at/) * [Installation Instructions](https://github.com/Catrobat/Catroid/wiki/Installation-Instructions) * [Frequently Asked Questions](https://github.com/Catrobat/Catroid/wiki/Frequently-Asked-Questions) -* [Credits](http://developer.catrobat.org/credits) +* [Credits](https://developer.catrobat.org/credits) * [Statistics on OpenHub](https://www.openhub.net/p/catrobat/) -* [Twitter](http://twitter.com/Catroid) +* [Twitter](https://twitter.com/Catroid) * [Google+](https://plus.google.com/u/0/+CatrobatOrgAdmin/posts) * [Our Google group](https://groups.google.com/forum/?fromgroups#!forum/catrobat) # License # -[License](http://developer.catrobat.org/licenses) of our project (mainly AGPL v3). +[License](https://catrob.at/licenses) of our project (mainly AGPL v3). diff --git a/catroid/build.gradle b/catroid/build.gradle index b3977fb9233..278cc36bc2c 100644 --- a/catroid/build.gradle +++ b/catroid/build.gradle @@ -53,7 +53,9 @@ ext { gdxVersion = "1.9.10" mockitoVersion = "2.8.47" espressoVersion = "3.1.0" - paintroidVersion = "2.5.0" + paintroidVersion = "2.5.1" + playServicesVersion = "17.0.0" + cameraXVersion = "1.0.0-beta06" } apply plugin: 'com.android.application' @@ -138,8 +140,8 @@ android { targetSdkVersion 28 applicationId appId testInstrumentationRunner 'org.catrobat.catroid.runner.UiTestApplicationRunner' - versionCode 78 - versionName "0.9.73" + versionCode 79 + versionName "0.9.74" println "VersionCode is $versionCode" println "VersionName is $versionName" multiDexEnabled true @@ -177,15 +179,17 @@ android { buildConfigField "boolean", "FEATURE_USERBRICKS_ENABLED", "true" buildConfigField "boolean", "FEATURE_WEBREQUEST_BRICK_ENABLED", "true" buildConfigField "boolean", "FEATURE_MULTIPLAYER_VARIABLES_ENABLED", "true" - resValue "string", "FEATURE_ARDUINO_PREFERENCES_ENABLED", "false" + buildConfigField "boolean", "FEATURE_TESTBRICK_ENABLED", "true" + resValue "string", "FEATURE_EMBROIDERY_PREFERENCES_ENABLED", "false" resValue "string", "FEATURE_PHIRO_PREFERENCES_ENABLED", "false" - resValue "string", "FEATURE_RASPI_PREFERENCES_ENABLED", "false" resValue "string", "SNACKBAR_HINTS_ENABLED", "false" + resValue "string", "DEBUG_MODE", "false" } lintOptions { lintConfig file('config/lint.xml') - ignore 'GradleDependency', 'OldTargetApi', 'ExtraTranslation', 'MissingTranslation', 'LintBaseline' + ignore 'GradleDependency', 'OldTargetApi', 'ExtraTranslation', 'MissingTranslation', + 'LintBaseline', 'ObsoleteLintCustomCheck' abortOnError false @@ -251,6 +255,7 @@ android { buildTypes { debug { buildConfigField "boolean", "USE_ANDROID_LOCALES_FOR_SCREENSHOTS", useAndroidLocales() + resValue "string", "DEBUG_MODE", "true" ext.enableCrashlytics = false testCoverageEnabled = rootProject.hasProperty('enableCoverage') signingConfig signingConfigs.debug @@ -262,6 +267,7 @@ android { buildConfigField "boolean", "FEATURE_USERBRICKS_ENABLED", "false" buildConfigField "boolean", "FEATURE_MULTIPLAYER_VARIABLES_ENABLED", "false" resValue "string", "SNACKBAR_HINTS_ENABLED", "true" + signingConfig signingConfigs.debug } signedRelease { @@ -283,6 +289,10 @@ android { createAtSchool { applicationIdSuffix '.createatschool' } + embroideryDesigner { + applicationIdSuffix '.embroiderydesigner' + resValue "string", "FEATURE_EMBROIDERY_PREFERENCES_ENABLED", "true" + } lunaAndCat { applicationIdSuffix '.lunaandcat' } @@ -290,13 +300,10 @@ android { applicationIdSuffix '.phiro' resValue "string", "FEATURE_PHIRO_PREFERENCES_ENABLED", "true" } - arduino { - applicationIdSuffix '.arduino' - resValue "string", "FEATURE_ARDUINO_PREFERENCES_ENABLED", "true" - resValue "string", "FEATURE_RASPI_PREFERENCES_ENABLED", "true" - } standalone { - applicationIdSuffix '.standalone' + if (!project.hasProperty('packageName')) { + applicationIdSuffix '.standalone' + } versionCode 1 versionName '1.0' @@ -373,10 +380,10 @@ dependencies { implementation 'com.parrot:arsdk:3.12.6' // CAST - implementation 'com.google.android.gms:play-services-cast:12.0.0' + implementation "com.google.android.gms:play-services-cast:$playServicesVersion" // Analytics - implementation 'com.google.android.gms:play-services-analytics:12.0.0' + implementation "com.google.android.gms:play-services-analytics:$playServicesVersion" implementation 'com.google.guava:guava:28.2-android' implementation 'com.google.code.gson:gson:2.8.0' @@ -396,11 +403,19 @@ dependencies { // Pocket Paint catroidImplementation "org.catrobat.paintroid:paintroid:$paintroidVersion" createAtSchoolImplementation "org.catrobat.paintroid:paintroid:$paintroidVersion" + embroideryDesignerImplementation "org.catrobat.paintroid:paintroid:$paintroidVersion" lunaAndCatImplementation "org.catrobat.paintroid:paintroid:$paintroidVersion" phiroImplementation "org.catrobat.paintroid:paintroid:$paintroidVersion" - arduinoImplementation "org.catrobat.paintroid:paintroid:$paintroidVersion" implementation "org.catrobat.paintroid:colorpicker:$paintroidVersion" + // CameraX + implementation "androidx.camera:camera-camera2:$cameraXVersion" + implementation "androidx.camera:camera-lifecycle:$cameraXVersion" + implementation 'androidx.camera:camera-view:1.0.0-alpha13' + + // ML Kit + implementation 'com.google.android.gms:play-services-mlkit-face-detection:16.0.0' + // libGDX implementation "com.badlogicgames.gdx:gdx:$gdxVersion" implementation "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion" @@ -414,7 +429,7 @@ dependencies { natives "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-armeabi-v7a" natives "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-arm64-v8a" - implementation 'com.google.android.gms:play-services-auth:12.0.0' + implementation "com.google.android.gms:play-services-auth:$playServicesVersion" androidTestImplementation('tools.fastlane:screengrab:1.2.0') { // https://issuetracker.google.com/issues/123060356 diff --git a/catroid/gradle/standalone_apk_tasks.gradle b/catroid/gradle/standalone_apk_tasks.gradle index af5472ef93b..cf259a75550 100644 --- a/catroid/gradle/standalone_apk_tasks.gradle +++ b/catroid/gradle/standalone_apk_tasks.gradle @@ -52,6 +52,9 @@ task standalonePreparation() { */ if (project.hasProperty('download')) { project.ext.projectId = project['suffix'] + if (project.hasProperty('packageName')) { + project.ext.appId = project['packageName'] + } project.ext.appId += '.' + project['suffix'] project.ext.manifestAppIcon = '@drawable/icon' project.ext.appZipFile = new File(makeAppsCache().absolutePath + '/' + project.ext.projectId + '.zip') @@ -221,7 +224,7 @@ def messUpIntentFilters() { */ tasks.whenTaskAdded { task -> - if (task.name == 'preStandaloneDebugBuild') { + if (task.name == 'preStandaloneDebugBuild' || task.name == 'preStandaloneSignedReleaseBuild') { task.dependsOn 'standalonePreparation' } else if (task.name == 'assembleStandaloneDebug') { task.finalizedBy 'standaloneCleanup' diff --git a/catroid/src/androidTest/assets/catrobatTestRunnerTests/fail/testFailParamEmptyInput.catrobat b/catroid/src/androidTest/assets/catrobatTestRunnerTests/fail/testFailParamEmptyInput.catrobat new file mode 100644 index 00000000000..a63510f888e Binary files /dev/null and b/catroid/src/androidTest/assets/catrobatTestRunnerTests/fail/testFailParamEmptyInput.catrobat differ diff --git a/catroid/src/androidTest/assets/catrobatTestRunnerTests/fail/testFailParamMismatch.catrobat b/catroid/src/androidTest/assets/catrobatTestRunnerTests/fail/testFailParamMismatch.catrobat new file mode 100644 index 00000000000..bbcbd7246f9 Binary files /dev/null and b/catroid/src/androidTest/assets/catrobatTestRunnerTests/fail/testFailParamMismatch.catrobat differ diff --git a/catroid/src/androidTest/assets/catrobatTestRunnerTests/fail/testFailParamMissingInput.catrobat b/catroid/src/androidTest/assets/catrobatTestRunnerTests/fail/testFailParamMissingInput.catrobat new file mode 100644 index 00000000000..cfefbc7860e Binary files /dev/null and b/catroid/src/androidTest/assets/catrobatTestRunnerTests/fail/testFailParamMissingInput.catrobat differ diff --git a/catroid/src/androidTest/assets/catrobatTestRunnerTests/fail/testFailParamStringNotEqual.catrobat b/catroid/src/androidTest/assets/catrobatTestRunnerTests/fail/testFailParamStringNotEqual.catrobat new file mode 100644 index 00000000000..55e49bc7a82 Binary files /dev/null and b/catroid/src/androidTest/assets/catrobatTestRunnerTests/fail/testFailParamStringNotEqual.catrobat differ diff --git a/catroid/src/androidTest/assets/catrobatTestRunnerTests/success/testSuccessParamCalculations.catrobat b/catroid/src/androidTest/assets/catrobatTestRunnerTests/success/testSuccessParamCalculations.catrobat new file mode 100644 index 00000000000..cf99894aa1e Binary files /dev/null and b/catroid/src/androidTest/assets/catrobatTestRunnerTests/success/testSuccessParamCalculations.catrobat differ diff --git a/catroid/src/androidTest/assets/catrobatTests/formula/testArccosineFunction.catrobat b/catroid/src/androidTest/assets/catrobatTests/formula/testArccosineFunction.catrobat new file mode 100644 index 00000000000..6c52fe7b1f3 Binary files /dev/null and b/catroid/src/androidTest/assets/catrobatTests/formula/testArccosineFunction.catrobat differ diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/catrobattestrunner/CatrobatTestRunnerTest.java b/catroid/src/androidTest/java/org/catrobat/catroid/catrobattestrunner/CatrobatTestRunnerTest.java index 41b72ee338c..039bf013e37 100644 --- a/catroid/src/androidTest/java/org/catrobat/catroid/catrobattestrunner/CatrobatTestRunnerTest.java +++ b/catroid/src/androidTest/java/org/catrobat/catroid/catrobattestrunner/CatrobatTestRunnerTest.java @@ -138,6 +138,70 @@ public void testFailListMismatchingTypes() throws Exception { testAsset("testFailListMismatchingTypes.catrobat", "catrobatTestRunnerTests/fail"); } + @Test + public void testFailParamMismatch() throws Exception { + exception.expectMessage("Failed Tests:\n\n" + + "[2] actual = 1.0\n" + + "expected: <1.1>\n" + + "actual: <1.0>\n" + + "deviation: --^\n\n" + + "[3] actual = String\n" + + "expected: <123>\n" + + "actual: \n" + + "deviation: ^\n\n" + + "[4] actual = 345\n" + + "expected: \n" + + "actual: <345>\n" + + "deviation: ^\n\n" + + "[5] actual = Actual\n" + + "expected: \n" + + "actual: \n" + + "deviation: ^\n\n\n" + + "Succeeded Tests:\n\n" + + "[0] actual = 5.0\n" + + "5.0 == 5\n\n" + + "[1] actual = 3\n" + + "3 == 3.0"); + testAsset("testFailParamMismatch.catrobat", "catrobatTestRunnerTests/fail"); + } + + @Test + public void testFailParamStringNotEqual() throws Exception { + exception.expectMessage("ParameterizedAssertError\n" + + "Failed Tests:\n\n" + + "[1] firstPart = puppy | secondPart = naughty\n" + + "expected: \n" + + "actual: \n" + + "deviation: ----------^\n\n\n" + + "Succeeded Tests:\n\n" + + "[0] firstPart = kitty | secondPart = cute\n" + + "kitty is cute == kitty is cute\n\n" + + "[2] firstPart = octopus | secondPart = intelligent\n" + + "octopus is intelligent == octopus is intelligent"); + testAsset("testFailParamStringNotEqual.catrobat", "catrobatTestRunnerTests/fail"); + } + + @Test + public void testFailParamMissingInput() throws Exception { + exception.expectMessage("ParameterizedInitialisationError\n" + + "Input was not selected"); + testAsset("testFailParamMissingInput.catrobat", "catrobatTestRunnerTests/fail"); + } + + @Test + public void testFailParamEmptyInput() throws Exception { + exception.expectMessage("ParameterizedInitialisationError\n" + + "Input list is missing elements\n" + + "Failed Tests:\n\n\n" + + "Succeeded Tests:"); + testAsset("testFailParamEmptyInput.catrobat", "catrobatTestRunnerTests/fail"); + } + + @Test + public void testSuccessParamCalculations() throws Exception { + testAsset("testSuccessParamCalculations.catrobat", "catrobatTestRunnerTests/success"); + } + private void testAsset(String assetName, String assetPath) throws Exception { catrobatTestRunner.assetName = assetName; catrobatTestRunner.assetPath = assetPath; diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/test/BricksHelpUrlTest.java b/catroid/src/androidTest/java/org/catrobat/catroid/test/BricksHelpUrlTest.java index f2d652da9f9..6caea1b17bb 100644 --- a/catroid/src/androidTest/java/org/catrobat/catroid/test/BricksHelpUrlTest.java +++ b/catroid/src/androidTest/java/org/catrobat/catroid/test/BricksHelpUrlTest.java @@ -408,6 +408,12 @@ public class BricksHelpUrlTest { "https://wiki.catrobat.org/bin/view/Documentation/Brick%20Documentation/Data%20Bricks/#StoreCSVIntoUserListBrick"); brickToHelpUrlMapping.put("org.catrobat.catroid.content.bricks.AssertUserListsBrick", "https://wiki.catrobat.org/bin/view/Documentation/Brick%20Documentation/Testing%20Bricks/#AssertUserListsBrick"); + brickToHelpUrlMapping.put("org.catrobat.catroid.content.bricks.ExitStageBrick", + "https://wiki.catrobat.org/bin/view/Documentation/Brick%20Documentation/Control%20Bricks/#ExitStageBrick"); + brickToHelpUrlMapping.put("org.catrobat.catroid.content.bricks.ParameterizedBrick", + "https://wiki.catrobat.org/bin/view/Documentation/Brick%20Documentation/Testing%20Bricks/#ParameterizedBrick"); + brickToHelpUrlMapping.put("org.catrobat.catroid.content.bricks.ParameterizedEndBrick", + "https://wiki.catrobat.org/bin/view/Documentation/Brick%20Documentation/Testing%20Bricks/#ParameterizedEndBrick"); } @Parameterized.Parameters(name = "{0}") diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/test/common/DefaultProjectHandlerTest.java b/catroid/src/androidTest/java/org/catrobat/catroid/test/common/DefaultProjectHandlerTest.java index ed991203bbc..2a81a1da0b7 100644 --- a/catroid/src/androidTest/java/org/catrobat/catroid/test/common/DefaultProjectHandlerTest.java +++ b/catroid/src/androidTest/java/org/catrobat/catroid/test/common/DefaultProjectHandlerTest.java @@ -43,8 +43,8 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; -import static org.catrobat.catroid.stage.StageListener.SCREENSHOT_AUTOMATIC_FILE_NAME; -import static org.catrobat.catroid.stage.StageListener.SCREENSHOT_MANUAL_FILE_NAME; +import static org.catrobat.catroid.common.Constants.SCREENSHOT_AUTOMATIC_FILE_NAME; +import static org.catrobat.catroid.common.Constants.SCREENSHOT_MANUAL_FILE_NAME; @RunWith(AndroidJUnit4.class) public class DefaultProjectHandlerTest { diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/test/content/bricks/BrickCategoryTest.java b/catroid/src/androidTest/java/org/catrobat/catroid/test/content/bricks/BrickCategoryTest.java index 87c8030f6b0..89d8e601f54 100644 --- a/catroid/src/androidTest/java/org/catrobat/catroid/test/content/bricks/BrickCategoryTest.java +++ b/catroid/src/androidTest/java/org/catrobat/catroid/test/content/bricks/BrickCategoryTest.java @@ -72,6 +72,7 @@ import org.catrobat.catroid.content.bricks.DroneTakeOffLandBrick; import org.catrobat.catroid.content.bricks.DroneTurnLeftBrick; import org.catrobat.catroid.content.bricks.DroneTurnRightBrick; +import org.catrobat.catroid.content.bricks.ExitStageBrick; import org.catrobat.catroid.content.bricks.FinishStageBrick; import org.catrobat.catroid.content.bricks.FlashBrick; import org.catrobat.catroid.content.bricks.ForVariableFromToBrick; @@ -109,6 +110,7 @@ import org.catrobat.catroid.content.bricks.MoveNStepsBrick; import org.catrobat.catroid.content.bricks.NextLookBrick; import org.catrobat.catroid.content.bricks.NoteBrick; +import org.catrobat.catroid.content.bricks.ParameterizedBrick; import org.catrobat.catroid.content.bricks.PenDownBrick; import org.catrobat.catroid.content.bricks.PenUpBrick; import org.catrobat.catroid.content.bricks.PhiroIfLogicBeginBrick; @@ -317,6 +319,7 @@ public static Collection data() { ForVariableFromToBrick.class, SceneTransitionBrick.class, SceneStartBrick.class, + ExitStageBrick.class, StopScriptBrick.class, CloneBrick.class, DeleteThisCloneBrick.class, @@ -396,6 +399,7 @@ public static Collection data() { RaspiPwmBrick.class)}, {"Testing", Arrays.asList(AssertEqualsBrick.class, AssertUserListsBrick.class, + ParameterizedBrick.class, WaitTillIdleBrick.class, TapAtBrick.class, FinishStageBrick.class, diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/test/facedetection/FaceDetectionHandlerTest.java b/catroid/src/androidTest/java/org/catrobat/catroid/test/facedetection/FaceDetectionHandlerTest.java deleted file mode 100644 index a3fd139b3ab..00000000000 --- a/catroid/src/androidTest/java/org/catrobat/catroid/test/facedetection/FaceDetectionHandlerTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2018 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.test.facedetection; - -import org.catrobat.catroid.camera.CameraManager; -import org.catrobat.catroid.facedetection.FaceDetectionHandler; -import org.catrobat.catroid.facedetection.FaceDetector; -import org.junit.After; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InOrder; -import org.mockito.Mockito; - -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -@RunWith(AndroidJUnit4.class) -public class FaceDetectionHandlerTest { - - @After - public void tearDown() { - FaceDetectionHandler.setFaceDetector(null); - } - - @Test - public void testResume() { - CameraManager.makeInstance(); - FaceDetector detector = Mockito.mock(FaceDetector.class); - when(detector.startFaceDetection()).thenReturn(true); - InOrder inOrder = inOrder(detector); - FaceDetectionHandler.setFaceDetector(detector); - - FaceDetectionHandler.resumeFaceDetection(); - verifyNoMoreInteractions(detector); - - FaceDetectionHandler.startFaceDetection(); - inOrder.verify(detector).startFaceDetection(); - verifyNoMoreInteractions(detector); - - FaceDetectionHandler.pauseFaceDetection(); - inOrder.verify(detector).stopFaceDetection(); - verifyNoMoreInteractions(detector); - - FaceDetectionHandler.resumeFaceDetection(); - inOrder.verify(detector).startFaceDetection(); - verifyNoMoreInteractions(detector); - - FaceDetectionHandler.stopFaceDetection(); - inOrder.verify(detector).stopFaceDetection(); - verifyNoMoreInteractions(detector); - - FaceDetectionHandler.resumeFaceDetection(); - verifyNoMoreInteractions(detector); - } -} diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/test/facedetection/FaceDetectorTest.java b/catroid/src/androidTest/java/org/catrobat/catroid/test/facedetection/FaceDetectorTest.java deleted file mode 100644 index 027dabf1b2d..00000000000 --- a/catroid/src/androidTest/java/org/catrobat/catroid/test/facedetection/FaceDetectorTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2018 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.test.facedetection; - -import org.catrobat.catroid.facedetection.FaceDetector; -import org.catrobat.catroid.formulaeditor.SensorCustomEvent; -import org.catrobat.catroid.formulaeditor.SensorCustomEventListener; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import static junit.framework.Assert.assertEquals; - -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -@RunWith(AndroidJUnit4.class) -public class FaceDetectorTest { - @Rule - public MockitoRule rule = MockitoJUnit.rule(); - - @Captor - private ArgumentCaptor captor; - - @Test - public void testStatusListenerCallback() { - FaceDetector detector = new FaceDetector() { - @Override - public boolean startFaceDetection() { - return true; - } - - @Override - public void stopFaceDetection() { - } - }; - - SensorCustomEventListener onFaceDetectionStatusListener = Mockito.mock(SensorCustomEventListener.class); - - detector.addOnFaceDetectionStatusListener(onFaceDetectionStatusListener); - verifyNoMoreInteractions(onFaceDetectionStatusListener); - - detector.callOnFaceDetected(false); - verifyNoMoreInteractions(onFaceDetectionStatusListener); - - detector.callOnFaceDetected(true); - verify(onFaceDetectionStatusListener).onCustomSensorChanged(captor.capture()); - assertEquals(1.0f, captor.getValue().values[0]); - verifyNoMoreInteractions(onFaceDetectionStatusListener); - - detector.callOnFaceDetected(true); - verify(onFaceDetectionStatusListener).onCustomSensorChanged(captor.capture()); - assertEquals(1.0f, captor.getValue().values[0]); - verifyNoMoreInteractions(onFaceDetectionStatusListener); - - detector.callOnFaceDetected(false); - verify(onFaceDetectionStatusListener, times(2)).onCustomSensorChanged(captor.capture()); - assertEquals(0.0f, captor.getValue().values[0]); - verifyNoMoreInteractions(onFaceDetectionStatusListener); - } -} diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/test/facedetection/IcsFaceDetectorTest.java b/catroid/src/androidTest/java/org/catrobat/catroid/test/facedetection/IcsFaceDetectorTest.java deleted file mode 100644 index c6adbbb2d9b..00000000000 --- a/catroid/src/androidTest/java/org/catrobat/catroid/test/facedetection/IcsFaceDetectorTest.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2018 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.test.facedetection; - -import android.graphics.Rect; -import android.hardware.Camera; -import android.hardware.Camera.Face; - -import org.catrobat.catroid.camera.CameraManager; -import org.catrobat.catroid.common.ScreenValues; -import org.catrobat.catroid.facedetection.IcsFaceDetector; -import org.catrobat.catroid.formulaeditor.SensorCustomEvent; -import org.catrobat.catroid.formulaeditor.SensorCustomEventListener; -import org.catrobat.catroid.uiespresso.annotations.Device; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -import java.util.List; -import java.util.Random; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.GrantPermissionRule; - -import static junit.framework.Assert.assertEquals; - -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.core.IsNot.not; -import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo; -import static org.hamcrest.number.OrderingComparison.lessThanOrEqualTo; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.verify; -import static org.mockito.internal.verification.VerificationModeFactory.times; - -@RunWith(AndroidJUnit4.class) -public class IcsFaceDetectorTest { - - @Rule - public MockitoRule rule = MockitoJUnit.rule(); - - @Rule - public GrantPermissionRule runtimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.CAMERA); - - @Captor - private ArgumentCaptor captor; - - private static final int FACE_RECT_SIZE = 2000; // see reference of Camera.Face.rect (1000 - -1000 = 2000) - - private static final int FACE_LEFT = -20; - private static final int FACE_RIGHT = 200; - private static final int FACE_TOP = -100; - private static final int FACE_BOTTOM = 300; - private static final int LOW_SCORE_FACE_WIDTH = 10; - private Camera camera; - private IcsFaceDetector detector; - - @Before - public void setUp() throws Exception { - CameraManager.makeInstance(); - ScreenValues.SCREEN_WIDTH = 720; - ScreenValues.SCREEN_HEIGHT = 1080; - camera = Camera.open(); - detector = new IcsFaceDetector(); - } - - @After - public void tearDown() { - detector.stopFaceDetection(); - - if (camera != null) { - camera.release(); - } - } - - @Device - @Test - public void testOnFaceDetectionStatusListener() { - final float[] detected = new float[1]; - SensorCustomEventListener listener = new SensorCustomEventListener() { - public void onCustomSensorChanged(SensorCustomEvent event) { - detected[0] = event.values[0]; - } - }; - detector.addOnFaceDetectionStatusListener(listener); - assertEquals(0f, detected[0]); - - detector.onFaceDetection(new Face[0], null); - assertEquals(0f, detected[0]); - Face[] faces = new Face[1]; - faces[0] = new Face(); - faces[0].rect = new Rect(); - detector.onFaceDetection(faces, null); - assertEquals(1f, detected[0]); - detector.onFaceDetection(new Face[0], null); - assertEquals(0f, detected[0]); - } - - @Device - @Test - public void testOnFaceDetectedListener() { - SensorCustomEventListener mockedListener = Mockito.mock(SensorCustomEventListener.class); - detector.addOnFaceDetectedListener(mockedListener); - - Rect faceBounds = new Rect(FACE_LEFT, FACE_TOP, FACE_RIGHT, FACE_BOTTOM); - Face[] faces = new Face[2]; - faces[0] = new Face(); - faces[0].rect = new Rect(0, 0, LOW_SCORE_FACE_WIDTH, 0); - faces[0].score = 60; - faces[1] = new Face(); - faces[1].rect = faceBounds; - faces[1].score = 80; - - detector.onFaceDetection(faces, null); - verify(mockedListener, times(3)).onCustomSensorChanged(captor.capture()); - List capturedEvents = captor.getAllValues(); - - assertThat(LOW_SCORE_FACE_WIDTH * 100 * 2 / FACE_RECT_SIZE, is(not((int) capturedEvents.get(2).values[0]))); - - float expectedSize = (FACE_RIGHT - FACE_LEFT) * 100 * 2 / FACE_RECT_SIZE; - assertEquals(expectedSize, capturedEvents.get(2).values[0]); - - float expectedXPosition = Math.abs((FACE_TOP + (FACE_BOTTOM - FACE_TOP) / 2) * ScreenValues.SCREEN_WIDTH - / FACE_RECT_SIZE); - assertEquals(expectedXPosition, Math.abs(capturedEvents.get(0).values[0])); - - float expectedYPosition = Math.abs((FACE_LEFT + (FACE_RIGHT - FACE_LEFT) / 2) * ScreenValues.SCREEN_HEIGHT - / FACE_RECT_SIZE); - assertEquals(expectedYPosition, Math.abs(capturedEvents.get(1).values[0])); - - detector.onFaceDetection(faces, null); - verify(mockedListener, times(6)).onCustomSensorChanged(captor.capture()); - - detector.removeOnFaceDetectedListener(mockedListener); - } - - @Device - @Test - public void testFaceSizeBounds() { - SensorCustomEventListener mockedListener = Mockito.mock(SensorCustomEventListener.class); - detector.addOnFaceDetectedListener(mockedListener); - - Rect faceBounds = new Rect(FACE_LEFT, FACE_TOP, FACE_RIGHT, FACE_BOTTOM); - Face[] faces = new Face[1]; - faces[0] = new Face(); - faces[0].rect = faceBounds; - - detector.onFaceDetection(faces, null); - verify(mockedListener, times(3)).onCustomSensorChanged(captor.capture()); - List capturedEvents = captor.getAllValues(); - - assertThat(capturedEvents.get(1).values[0], allOf(greaterThanOrEqualTo(0f), lessThanOrEqualTo(100f))); - - Random random = new Random(); - int left = random.nextInt(FACE_RECT_SIZE - 1); - int top = random.nextInt(FACE_RECT_SIZE - 1); - int right = left + random.nextInt(FACE_RECT_SIZE - left); - int bottom = top + random.nextInt(FACE_RECT_SIZE - top); - faceBounds = new Rect(left - FACE_RECT_SIZE / 2, top - FACE_RECT_SIZE / 2, right - FACE_RECT_SIZE / 2, bottom - - FACE_RECT_SIZE / 2); - - faces[0].rect = faceBounds; - - detector.onFaceDetection(faces, null); - verify(mockedListener, times(6)).onCustomSensorChanged(captor.capture()); - capturedEvents = captor.getAllValues(); - - assertThat(capturedEvents.get(1).values[0], allOf(greaterThanOrEqualTo(0f), lessThanOrEqualTo(100f))); - - detector.removeOnFaceDetectedListener(mockedListener); - } -} diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/test/facedetection/SlowFaceDetectorCameraTest.java b/catroid/src/androidTest/java/org/catrobat/catroid/test/facedetection/SlowFaceDetectorCameraTest.java deleted file mode 100644 index 6c11cfe9b7e..00000000000 --- a/catroid/src/androidTest/java/org/catrobat/catroid/test/facedetection/SlowFaceDetectorCameraTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2018 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.test.facedetection; - -import android.hardware.Camera; - -import org.catrobat.catroid.ProjectManager; -import org.catrobat.catroid.camera.CameraManager; -import org.catrobat.catroid.common.ScreenValues; -import org.catrobat.catroid.content.Project; -import org.catrobat.catroid.facedetection.SlowFaceDetector; -import org.junit.After; -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.GrantPermissionRule; - -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertTrue; - -@RunWith(AndroidJUnit4.class) -public class SlowFaceDetectorCameraTest { - private Camera camera; - - @Rule - public GrantPermissionRule runtimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.CAMERA); - - @Before - public void setUp() throws Exception { - CameraManager.makeInstance(); - ScreenValues.SCREEN_WIDTH = 720; - ScreenValues.SCREEN_HEIGHT = 1080; - ProjectManager.getInstance().setCurrentProject(new Project()); - } - - @After - public void tearDown() { - if (camera != null) { - camera.release(); - } - } - - @Test - public void testStartAndStop() { - SlowFaceDetector detector = new SlowFaceDetector(); - assertNotNull(detector); - - assertTrue(detector.startFaceDetection()); - - detector.stopFaceDetection(); - - camera = Camera.open(); - assertNotNull(Camera.open()); - } -} diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/test/facedetection/SlowFaceDetectorTest.java b/catroid/src/androidTest/java/org/catrobat/catroid/test/facedetection/SlowFaceDetectorTest.java deleted file mode 100644 index ec32f458931..00000000000 --- a/catroid/src/androidTest/java/org/catrobat/catroid/test/facedetection/SlowFaceDetectorTest.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2018 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.test.facedetection; - -import android.graphics.PointF; - -import org.catrobat.catroid.camera.CameraManager; -import org.catrobat.catroid.common.ScreenValues; -import org.catrobat.catroid.facedetection.SlowFaceDetector; -import org.catrobat.catroid.formulaeditor.SensorCustomEvent; -import org.catrobat.catroid.formulaeditor.SensorCustomEventListener; -import org.catrobat.catroid.formulaeditor.Sensors; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -import java.util.List; -import java.util.Random; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.GrantPermissionRule; - -import static junit.framework.Assert.assertEquals; - -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo; -import static org.hamcrest.number.OrderingComparison.lessThanOrEqualTo; -import static org.junit.Assert.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.verify; -import static org.mockito.internal.verification.VerificationModeFactory.times; - -@RunWith(AndroidJUnit4.class) -public class SlowFaceDetectorTest { - - @Rule - public MockitoRule rule = MockitoJUnit.rule(); - - @Rule - public GrantPermissionRule runtimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.CAMERA); - - @Captor - private ArgumentCaptor captor; - - private static final int DETECTION_WIDTH = 400; - private static final int DETECTION_HEIGHT = 300; - private static final float EYE_DISTANCE = 4.0f; - - private SlowFaceDetector detector; - - @Before - public void setUp() throws Exception { - CameraManager.makeInstance(); - ScreenValues.SCREEN_WIDTH = 720; - ScreenValues.SCREEN_HEIGHT = 1080; - detector = new SlowFaceDetector(); - } - - @After - public void tearDown() { - detector.stopFaceDetection(); - } - - @Test - public void testDoubleStart() { - detector.startFaceDetection(); - detector.startFaceDetection(); - } - - @Test - public void testOnFaceDetectedListener() { - SensorCustomEventListener mockedListener = Mockito.mock(SensorCustomEventListener.class); - detector.addOnFaceDetectedListener(mockedListener); - PointF centerPoint = new PointF(DETECTION_WIDTH / 2, DETECTION_HEIGHT / 2); - detector.callOnFaceFound(centerPoint, EYE_DISTANCE, DETECTION_WIDTH, DETECTION_HEIGHT); - - verify(mockedListener, times(3)).onCustomSensorChanged(captor.capture()); - List capturedEvents = captor.getAllValues(); - - float expectedSize = (int) (EYE_DISTANCE * 400 / DETECTION_WIDTH); - assertEquals(expectedSize, capturedEvents.get(2).values[0]); - assertEquals(Sensors.FACE_SIZE, capturedEvents.get(2).sensor); - - float expectedXPosition = (int) (centerPoint.x / DETECTION_WIDTH * (-1) * ScreenValues.SCREEN_WIDTH) - + ScreenValues.SCREEN_WIDTH / 2; - assertEquals(expectedXPosition, capturedEvents.get(0).values[0]); - assertEquals(Sensors.FACE_X_POSITION, capturedEvents.get(0).sensor); - - float expectedYPosition = (int) (centerPoint.y / DETECTION_HEIGHT * (-1) * ScreenValues.SCREEN_HEIGHT) - + ScreenValues.SCREEN_HEIGHT / 2; - assertEquals(expectedYPosition, capturedEvents.get(1).values[0]); - assertEquals(Sensors.FACE_Y_POSITION, capturedEvents.get(1).sensor); - - detector.callOnFaceFound(centerPoint, EYE_DISTANCE, DETECTION_WIDTH, DETECTION_HEIGHT); - verify(mockedListener, times(6)).onCustomSensorChanged(any(SensorCustomEvent.class)); - - detector.removeOnFaceDetectedListener(mockedListener); - } - - @Test - public void testFaceSizeBounds() { - SensorCustomEventListener mockedListener = Mockito.mock(SensorCustomEventListener.class); - detector.addOnFaceDetectedListener(mockedListener); - - detector.callOnFaceFound(new PointF(), EYE_DISTANCE, - DETECTION_WIDTH, DETECTION_HEIGHT); - - verify(mockedListener, times(3)).onCustomSensorChanged(captor.capture()); - List capturedEvents = captor.getAllValues(); - - assertThat(capturedEvents.get(2).values[0], allOf(greaterThanOrEqualTo(0f), lessThanOrEqualTo(100f))); - - Random random = new Random(); - detector.callOnFaceFound(new PointF(), random.nextInt(DETECTION_WIDTH), - DETECTION_WIDTH, DETECTION_HEIGHT); - verify(mockedListener, times(6)).onCustomSensorChanged(captor.capture()); - capturedEvents = captor.getAllValues(); - assertThat(capturedEvents.get(2).values[0], allOf(greaterThanOrEqualTo(0f), lessThanOrEqualTo(100f))); - - detector.removeOnFaceDetectedListener(mockedListener); - } -} diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/test/formulaeditor/ParserTestSensors.java b/catroid/src/androidTest/java/org/catrobat/catroid/test/formulaeditor/ParserTestSensors.java deleted file mode 100644 index c77c00f7707..00000000000 --- a/catroid/src/androidTest/java/org/catrobat/catroid/test/formulaeditor/ParserTestSensors.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2020 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.test.formulaeditor; - -import android.Manifest; -import android.graphics.Point; - -import org.catrobat.catroid.ProjectManager; -import org.catrobat.catroid.camera.CameraManager; -import org.catrobat.catroid.content.Project; -import org.catrobat.catroid.content.Script; -import org.catrobat.catroid.content.SingleSprite; -import org.catrobat.catroid.content.Sprite; -import org.catrobat.catroid.content.StartScript; -import org.catrobat.catroid.content.bricks.Brick; -import org.catrobat.catroid.content.bricks.ChangeSizeByNBrick; -import org.catrobat.catroid.facedetection.FaceDetectionHandler; -import org.catrobat.catroid.facedetection.FaceDetector; -import org.catrobat.catroid.formulaeditor.Formula; -import org.catrobat.catroid.formulaeditor.FormulaElement; -import org.catrobat.catroid.formulaeditor.InternFormulaParser; -import org.catrobat.catroid.formulaeditor.InternToken; -import org.catrobat.catroid.formulaeditor.InternTokenType; -import org.catrobat.catroid.formulaeditor.SensorHandler; -import org.catrobat.catroid.formulaeditor.SensorLoudness; -import org.catrobat.catroid.formulaeditor.Sensors; -import org.catrobat.catroid.soundrecorder.SoundRecorder; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InOrder; -import org.mockito.Mockito; - -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; - -import androidx.test.annotation.UiThreadTest; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.GrantPermissionRule; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotNull; - -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.when; - -@RunWith(AndroidJUnit4.class) -public class ParserTestSensors { - - @Rule - public GrantPermissionRule runtimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO); - - private Project project; - private Sprite firstSprite; - private Script startScript1; - private float delta = 0.001f; - - @Before - @UiThreadTest - public void setUp() throws Exception { - createProject(); - ProjectManager.getInstance().setCurrentProject(project); - ProjectManager.getInstance().setCurrentSprite(firstSprite); - } - - @After - @UiThreadTest - public void tearDown() throws Exception { - SensorHandler.stopSensorListeners(); - } - - @Test - @UiThreadTest - public void testSensorManagerNotInitialized() { - SensorHandler.registerListener(null); - SensorHandler.unregisterListener(null); - SensorHandler.startSensorListener(ApplicationProvider.getApplicationContext()); - - assertEquals(0d, Math.abs((Double) SensorHandler.getSensorValue(Sensors.X_ACCELERATION))); - } - - @Test - @UiThreadTest - public void testSensorHandlerWithLookSensorValue() { - SensorHandler.startSensorListener(ApplicationProvider.getApplicationContext()); - assertEquals(0d, SensorHandler.getSensorValue(Sensors.OBJECT_BRIGHTNESS)); - SensorHandler.stopSensorListeners(); - } - - @Test - @UiThreadTest - public void testFaceDetection() throws Exception { - CameraManager.makeInstance(); - SensorHandler.startSensorListener(ApplicationProvider.getApplicationContext()); - FaceDetector faceDetector = FaceDetectionHandler.getFaceDetector(); - - assertNotNull(faceDetector); - - assertEquals(0d, SensorHandler.getSensorValue(Sensors.FACE_DETECTED)); - assertEquals(0d, SensorHandler.getSensorValue(Sensors.FACE_SIZE)); - - faceDetector.callOnFaceDetected(true); - - int expectedFaceSize = (int) (Math.random() * 100); - int exampleScreenWidth = 320; - int exampleScreenHeight = 480; - int expectedFaceXPosition = (int) (-exampleScreenWidth / 2 + (Math.random() * exampleScreenWidth)); - int expectedFaceYPosition = (int) (-exampleScreenHeight / 2 + (Math.random() * exampleScreenHeight)); - - faceDetector.callOnFaceDetected(new Point(expectedFaceXPosition, expectedFaceYPosition), expectedFaceSize); - - Formula formula6 = createFormulaWithSensor(Sensors.FACE_DETECTED); - ChangeSizeByNBrick faceDetectionStatusBrick = new ChangeSizeByNBrick(formula6); - startScript1.addBrick(faceDetectionStatusBrick); - - Formula formula7 = createFormulaWithSensor(Sensors.FACE_SIZE); - ChangeSizeByNBrick faceSizeBrick = new ChangeSizeByNBrick(formula7); - startScript1.addBrick(faceSizeBrick); - - Formula formula8 = createFormulaWithSensor(Sensors.FACE_X_POSITION); - ChangeSizeByNBrick faceXPositionBrick = new ChangeSizeByNBrick(formula8); - startScript1.addBrick(faceXPositionBrick); - - Formula formula9 = createFormulaWithSensor(Sensors.FACE_Y_POSITION); - ChangeSizeByNBrick faceYPositionBrick = new ChangeSizeByNBrick(formula9); - startScript1.addBrick(faceYPositionBrick); - - assertEquals(1d, formula6.interpretFloat(firstSprite), delta); - - assertEquals(expectedFaceSize, formula7.interpretFloat(firstSprite), delta); - - assertEquals(expectedFaceXPosition, formula8.interpretFloat(firstSprite), delta); - - assertEquals(expectedFaceYPosition, -formula9.interpretFloat(firstSprite), delta); - - SensorHandler.stopSensorListeners(); - } - - @Test - @UiThreadTest - public void testMicRelease() throws IOException { - SensorLoudness loudnessSensor = new SensorLoudness(); - SoundRecorder soundRecorder = Mockito.mock(SoundRecorder.class); - loudnessSensor.setSoundRecorder(soundRecorder); - InOrder inOrder = inOrder(soundRecorder); - - when(soundRecorder.isRecording()).thenReturn(false); - SensorHandler.getInstance(ApplicationProvider.getApplicationContext()).setSensorLoudness(loudnessSensor); - SensorHandler.startSensorListener(ApplicationProvider.getApplicationContext()); - inOrder.verify(soundRecorder).start(); - - when(soundRecorder.isRecording()).thenReturn(true); - SensorHandler.stopSensorListeners(); - inOrder.verify(soundRecorder).stop(); - } - - private Formula createFormulaWithSensor(Sensors sensor) { - List internTokenList = new LinkedList(); - internTokenList.add(new InternToken(InternTokenType.SENSOR, sensor.name())); - InternFormulaParser internFormulaParser = new InternFormulaParser(internTokenList); - FormulaElement root = internFormulaParser.parseFormula(); - return new Formula(root); - } - - private void createProject() { - this.project = new Project(ApplicationProvider.getApplicationContext(), "testProject"); - firstSprite = new SingleSprite("zwoosh"); - startScript1 = new StartScript(); - firstSprite.addScript(startScript1); - Brick changeBrick = new ChangeSizeByNBrick(10); - startScript1.addBrick(changeBrick); - project.getDefaultScene().addSprite(firstSprite); - } -} diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/test/formulaeditor/SensorHandlerTest.kt b/catroid/src/androidTest/java/org/catrobat/catroid/test/formulaeditor/SensorHandlerTest.kt new file mode 100644 index 00000000000..d7335c6bacc --- /dev/null +++ b/catroid/src/androidTest/java/org/catrobat/catroid/test/formulaeditor/SensorHandlerTest.kt @@ -0,0 +1,120 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2020 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.test.formulaeditor + +import android.Manifest +import android.graphics.Point +import androidx.test.annotation.UiThreadTest +import androidx.test.core.app.ApplicationProvider +import androidx.test.rule.GrantPermissionRule +import org.catrobat.catroid.ProjectManager +import org.catrobat.catroid.camera.FaceDetector +import org.catrobat.catroid.content.Project +import org.catrobat.catroid.formulaeditor.SensorHandler +import org.catrobat.catroid.formulaeditor.SensorLoudness +import org.catrobat.catroid.formulaeditor.Sensors +import org.catrobat.catroid.soundrecorder.SoundRecorder +import org.catrobat.catroid.test.utils.TestUtils +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mockito + +class SensorHandlerTest { + @get:Rule + val runtimePermissionRule: GrantPermissionRule = GrantPermissionRule.grant( + Manifest.permission.CAMERA, + Manifest.permission.RECORD_AUDIO + ) + + @Before + fun setUp() { + ProjectManager.getInstance().currentProject = Project( + ApplicationProvider.getApplicationContext(), + TestUtils.DEFAULT_TEST_PROJECT_NAME + ) + } + + @Test + fun testSensorManagerNotInitialized() { + SensorHandler.registerListener(null) + SensorHandler.unregisterListener(null) + SensorHandler.startSensorListener(ApplicationProvider.getApplicationContext()) + compareToSensor(0, Sensors.X_ACCELERATION) + } + + @Test + fun testSensorHandlerWithLookSensorValue() { + compareToSensor(0, Sensors.OBJECT_BRIGHTNESS) + } + + @Test + fun testFaceDetection() { + SensorHandler.startSensorListener(ApplicationProvider.getApplicationContext()) + compareToSensor(0, Sensors.FACE_DETECTED) + compareToSensor(0, Sensors.FACE_SIZE) + + val size = 50 + val position = Point(15, -15) + FaceDetector.updateDetectionStatus(true) + FaceDetector.onFaceDetected(position, size) + + compareToSensor(1, Sensors.FACE_DETECTED) + compareToSensor(size, Sensors.FACE_SIZE) + compareToSensor(position.x, Sensors.FACE_X_POSITION) + compareToSensor(position.y, Sensors.FACE_Y_POSITION) + } + + @Test + @UiThreadTest + fun testMicRelease() { + val loudnessSensor = SensorLoudness() + val soundRecorder = Mockito.mock(SoundRecorder::class.java) + loudnessSensor.soundRecorder = soundRecorder + + Mockito.`when`(soundRecorder.isRecording).thenReturn(false) + SensorHandler.getInstance(ApplicationProvider.getApplicationContext()).setSensorLoudness(loudnessSensor) + + SensorHandler.startSensorListener(ApplicationProvider.getApplicationContext()) + Mockito.`when`(soundRecorder.isRecording).thenReturn(true) + Mockito.verify(soundRecorder).start() + + SensorHandler.stopSensorListeners() + Mockito.verify(soundRecorder).stop() + } + + @After + fun tearDown() { + SensorHandler.stopSensorListeners() + } + + private fun compareToSensor(value: Int, sensor: Sensors) { + assertEquals(value.toDouble(), SensorHandler.getSensorValue(sensor) as Double, DELTA) + } + + companion object { + private const val DELTA = 0.01 + } +} diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/test/utils/TestUtils.java b/catroid/src/androidTest/java/org/catrobat/catroid/test/utils/TestUtils.java index fc35c6d7870..6c844329d37 100644 --- a/catroid/src/androidTest/java/org/catrobat/catroid/test/utils/TestUtils.java +++ b/catroid/src/androidTest/java/org/catrobat/catroid/test/utils/TestUtils.java @@ -52,7 +52,7 @@ public final class TestUtils { public static final String DEFAULT_TEST_PROJECT_NAME = "testProject"; - public static final String DEFAULT_TEST_SPRITE_NAME = "testProject"; + public static final String DEFAULT_TEST_SPRITE_NAME = "testSprite"; public static final double DELTA = 0.00001; diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/content/brick/app/DeleteUserDefinedReceiverBrickTest.java b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/content/brick/app/DeleteUserDefinedReceiverBrickTest.java new file mode 100644 index 00000000000..a4552df309d --- /dev/null +++ b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/content/brick/app/DeleteUserDefinedReceiverBrickTest.java @@ -0,0 +1,163 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2020 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.uiespresso.content.brick.app; + +import org.catrobat.catroid.ProjectManager; +import org.catrobat.catroid.content.Project; +import org.catrobat.catroid.content.Scene; +import org.catrobat.catroid.content.Script; +import org.catrobat.catroid.content.Sprite; +import org.catrobat.catroid.content.StartScript; +import org.catrobat.catroid.content.UserDefinedScript; +import org.catrobat.catroid.content.bricks.IfLogicBeginBrick; +import org.catrobat.catroid.content.bricks.SetXBrick; +import org.catrobat.catroid.content.bricks.UserDefinedBrick; +import org.catrobat.catroid.content.bricks.UserDefinedReceiverBrick; +import org.catrobat.catroid.formulaeditor.Formula; +import org.catrobat.catroid.test.utils.TestUtils; +import org.catrobat.catroid.ui.SpriteActivity; +import org.catrobat.catroid.ui.recyclerview.controller.SpriteController; +import org.catrobat.catroid.uiespresso.util.rules.FragmentActivityTestRule; +import org.catrobat.catroid.userbrick.UserDefinedBrickInput; +import org.catrobat.catroid.userbrick.UserDefinedBrickLabel; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.util.Collections; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import static org.catrobat.catroid.uiespresso.content.brick.utils.BrickDataInteractionWrapper.onBrickAtPosition; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +@RunWith(AndroidJUnit4.class) +public class DeleteUserDefinedReceiverBrickTest { + + @Rule + public FragmentActivityTestRule baseActivityTestRule = new + FragmentActivityTestRule<>(SpriteActivity.class, SpriteActivity.EXTRA_FRAGMENT_POSITION, + SpriteActivity.FRAGMENT_SCRIPTS); + + private Sprite sprite; + private Sprite copiedSprite; + + private UserDefinedBrick userDefinedBrickToDelete; + private UserDefinedBrick differentUserDefinedBrick; + private SetXBrick setXBrick; + private IfLogicBeginBrick ifLogicBrick; + + private int indexStartScript = 2; + private int indexBrickToDelete = 0; + + @After + public void tearDown() throws IOException { + TestUtils.deleteProjects(DeleteUserDefinedReceiverBrickTest.class.getSimpleName()); + } + + @Before + public void setUp() throws IOException, CloneNotSupportedException { + createProject(DeleteUserDefinedReceiverBrickTest.class.getSimpleName()); + baseActivityTestRule.launchActivity(); + onBrickAtPosition(0).performDeleteBrick(); + } + + @Test + public void testRemoveFromYourBrickCategory() { + assertFalse(sprite.getUserDefinedBrickList().contains(userDefinedBrickToDelete)); + } + @Test + public void testDeletionInCurrentSpriteOnly() { + assertTrue(copiedSprite.containsUserDefinedBrickWithSameUserData(userDefinedBrickToDelete)); + UserDefinedBrick userDefinedBrick = (UserDefinedBrick) copiedSprite.getScript(indexStartScript).getBrickList().get(indexBrickToDelete); + assertTrue(userDefinedBrick.isUserDefinedBrickDataEqual(userDefinedBrickToDelete)); + } + @Test + public void testDeletionInNestedBricks() { + assertTrue(ifLogicBrick.getNestedBricks().isEmpty()); + assertTrue(ifLogicBrick.getSecondaryNestedBricks().isEmpty()); + } + @Test + public void testDeletionOfUserDefinedReceiverBrick() { + assertFalse(sprite.getScript(indexStartScript).getBrickList().contains(userDefinedBrickToDelete)); + } + @Test + public void testDifferentUserDefinedBrickNotDeleted() { + assertTrue(sprite.getScript(indexStartScript).getBrickList().contains(differentUserDefinedBrick)); + } + + private void createProject(String projectName) throws IOException, CloneNotSupportedException { + Project project = new Project(ApplicationProvider.getApplicationContext(), projectName); + ProjectManager projectManager = ProjectManager.getInstance(); + + SpriteController controller = new SpriteController(); + + sprite = new Sprite("Sprite1"); + userDefinedBrickToDelete = new UserDefinedBrick(Collections.singletonList(new UserDefinedBrickLabel("Label"))); + differentUserDefinedBrick = new UserDefinedBrick(Collections.singletonList(new UserDefinedBrickInput("Input"))); + setXBrick = new SetXBrick(new Formula(0)); + ifLogicBrick = new IfLogicBeginBrick(); + ifLogicBrick.addBrickToIfBranch(userDefinedBrickToDelete); + ifLogicBrick.addBrickToElseBranch(userDefinedBrickToDelete); + + createUserDefinedScripts(); + createStartScript(); + + Scene scene = project.getDefaultScene(); + project.getDefaultScene().addSprite(sprite); + + copiedSprite = controller.copy(sprite, project, scene); + + projectManager.setCurrentProject(project); + projectManager.setCurrentSprite(sprite); + } + + private void createStartScript() { + Script startScript = new StartScript(); + startScript.addBrick(userDefinedBrickToDelete); + startScript.addBrick(differentUserDefinedBrick); + startScript.addBrick(ifLogicBrick); + sprite.addScript(startScript); + } + + private void createUserDefinedScripts() throws CloneNotSupportedException { + Script scriptToDelete = new UserDefinedScript(); + scriptToDelete.setScriptBrick(new UserDefinedReceiverBrick(userDefinedBrickToDelete)); + sprite.addUserDefinedBrick(userDefinedBrickToDelete); + scriptToDelete.addBrick(setXBrick); + + Script secondScript = new UserDefinedScript(); + secondScript.setScriptBrick(new UserDefinedReceiverBrick(differentUserDefinedBrick)); + sprite.addUserDefinedBrick(differentUserDefinedBrick); + secondScript.addBrick(setXBrick.clone()); + + sprite.addScript(scriptToDelete); + sprite.addScript(secondScript); + } +} diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/content/brick/stage/CameraResourceTest.java b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/content/brick/stage/CameraResourceTest.java deleted file mode 100644 index e86351bf417..00000000000 --- a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/content/brick/stage/CameraResourceTest.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2018 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.uiespresso.content.brick.stage; - -import org.catrobat.catroid.R; -import org.catrobat.catroid.camera.CameraManager; -import org.catrobat.catroid.content.Script; -import org.catrobat.catroid.content.bricks.CameraBrick; -import org.catrobat.catroid.content.bricks.ChooseCameraBrick; -import org.catrobat.catroid.rules.FlakyTestRule; -import org.catrobat.catroid.runner.Flaky; -import org.catrobat.catroid.testsuites.annotations.Cat; -import org.catrobat.catroid.testsuites.annotations.Level; -import org.catrobat.catroid.ui.SpriteActivity; -import org.catrobat.catroid.uiespresso.content.brick.utils.BrickTestUtils; -import org.catrobat.catroid.uiespresso.stage.utils.ScriptEvaluationGateBrick; -import org.catrobat.catroid.uiespresso.util.actions.CustomActions; -import org.catrobat.catroid.uiespresso.util.rules.FragmentActivityTestRule; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runner.RunWith; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.GrantPermissionRule; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertTrue; - -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.matcher.ViewMatchers.isRoot; -import static androidx.test.espresso.matcher.ViewMatchers.withId; - -@RunWith(AndroidJUnit4.class) -public class CameraResourceTest { - - private static final int BACK = 0; - private static final int FRONT = 1; - private static final int OFF = 0; - private static final int ON = 1; - - @Rule - public FragmentActivityTestRule baseActivityTestRule = new - FragmentActivityTestRule<>(SpriteActivity.class, SpriteActivity.EXTRA_FRAGMENT_POSITION, SpriteActivity.FRAGMENT_SCRIPTS); - - @Rule - public GrantPermissionRule runtimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.CAMERA); - - @Rule - public FlakyTestRule flakyTestRule = new FlakyTestRule(); - - @Before - public void setUp() throws Exception { - } - - @Category({Cat.AppUi.class, Level.Functional.class, Cat.Quarantine.class}) - @Test - public void cameraResourceNotUsedTest() { - Script script = BrickTestUtils.createProjectAndGetStartScript("cameraResourceNotUsed"); - ScriptEvaluationGateBrick lastBrickInScript = ScriptEvaluationGateBrick.appendToScript(script); - - baseActivityTestRule.launchActivity(); - onView(withId(R.id.button_play)).perform(click()); - - lastBrickInScript.waitUntilEvaluated(3000); - - assertEquals(CameraManager.CameraState.notUsed, CameraManager.getInstance().getState()); - } - - @Category({Cat.AppUi.class, Level.Functional.class, Cat.Quarantine.class}) - @Test - public void cameraOnTest() { - Script script = BrickTestUtils.createProjectAndGetStartScript("cameraOnTest"); - script.addBrick(new CameraBrick(ON)); - ScriptEvaluationGateBrick lastBrickInScript = ScriptEvaluationGateBrick.appendToScript(script); - - baseActivityTestRule.launchActivity(); - onView(withId(R.id.button_play)).perform(click()); - - lastBrickInScript.waitUntilEvaluated(3000); - - assertEquals(CameraManager.CameraState.previewRunning, CameraManager.getInstance().getState()); - assertTrue(CameraManager.getInstance().isCurrentCameraFacingFront()); - } - - @Category({Cat.AppUi.class, Level.Functional.class, Cat.Quarantine.class}) - @Test - public void cameraStagePausedTest() { - Script script = BrickTestUtils.createProjectAndGetStartScript("cameraStagePausedTest"); - script.addBrick(new CameraBrick(ON)); - ScriptEvaluationGateBrick lastBrickInScript = ScriptEvaluationGateBrick.appendToScript(script); - - baseActivityTestRule.launchActivity(); - onView(withId(R.id.button_play)).perform(click()); - - lastBrickInScript.waitUntilEvaluated(3000); - - pressBack(); - assertTrue(CameraManager.getInstance().getState() == CameraManager.CameraState.previewPaused - || CameraManager.getInstance().getState() == CameraManager.CameraState.notUsed); - } - - @Category({Cat.AppUi.class, Level.Functional.class, Cat.Quarantine.class}) - @Test - public void cameraOffTest() { - Script script = BrickTestUtils.createProjectAndGetStartScript("cameraOffTest"); - script.addBrick(new CameraBrick(OFF)); - ScriptEvaluationGateBrick lastBrickInScript = ScriptEvaluationGateBrick.appendToScript(script); - - baseActivityTestRule.launchActivity(); - onView(withId(R.id.button_play)).perform(click()); - - lastBrickInScript.waitUntilEvaluated(3000); - - assertEquals(CameraManager.CameraState.notUsed, CameraManager.getInstance().getState()); - } - - @Category({Cat.AppUi.class, Level.Functional.class, Cat.Quarantine.class}) - @Flaky - @Test - public void cameraFacingFrontTest() { - Script script = BrickTestUtils.createProjectAndGetStartScript("cameraFacingFrontTest"); - script.addBrick(new ChooseCameraBrick(FRONT)); - script.addBrick(new CameraBrick(ON)); - ScriptEvaluationGateBrick lastBrickInScript = ScriptEvaluationGateBrick.appendToScript(script); - - baseActivityTestRule.launchActivity(); - onView(withId(R.id.button_play)).perform(click()); - - lastBrickInScript.waitUntilEvaluated(3000); - - onView(isRoot()).perform(CustomActions.wait(500)); - assertEquals(CameraManager.CameraState.previewRunning, CameraManager.getInstance().getState()); - assertTrue(CameraManager.getInstance().isCurrentCameraFacingFront()); - } - - @Category({Cat.AppUi.class, Level.Functional.class, Cat.Quarantine.class}) - @Flaky - @Test - public void cameraFacingBackTest() { - Script script = BrickTestUtils.createProjectAndGetStartScript("cameraFacingBackTest"); - script.addBrick(new ChooseCameraBrick(BACK)); - script.addBrick(new CameraBrick(ON)); - ScriptEvaluationGateBrick lastBrickInScript = ScriptEvaluationGateBrick.appendToScript(script); - - baseActivityTestRule.launchActivity(); - onView(withId(R.id.button_play)).perform(click()); - - lastBrickInScript.waitUntilEvaluated(3000); - - onView(isRoot()).perform(CustomActions.wait(500)); - assertEquals(CameraManager.CameraState.previewRunning, CameraManager.getInstance().getState()); - assertTrue(CameraManager.getInstance().isCurrentCameraFacingBack()); - } -} diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/content/brick/stage/CameraResourceTest.kt b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/content/brick/stage/CameraResourceTest.kt new file mode 100644 index 00000000000..0248b20b53e --- /dev/null +++ b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/content/brick/stage/CameraResourceTest.kt @@ -0,0 +1,160 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2018 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.uiespresso.content.brick.stage + +import android.Manifest +import androidx.test.espresso.Espresso +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.rule.GrantPermissionRule +import org.catrobat.catroid.R +import org.catrobat.catroid.content.bricks.CameraBrick +import org.catrobat.catroid.content.bricks.ChooseCameraBrick +import org.catrobat.catroid.stage.StageActivity +import org.catrobat.catroid.testsuites.annotations.Cat.AppUi +import org.catrobat.catroid.testsuites.annotations.Cat.Quarantine +import org.catrobat.catroid.testsuites.annotations.Level.Functional +import org.catrobat.catroid.ui.SpriteActivity +import org.catrobat.catroid.uiespresso.content.brick.utils.BrickTestUtils +import org.catrobat.catroid.uiespresso.stage.utils.ScriptEvaluationGateBrick +import org.catrobat.catroid.uiespresso.util.rules.FragmentActivityTestRule +import org.junit.Assert +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Rule +import org.junit.Test +import org.junit.experimental.categories.Category + +class CameraResourceTest { + @get:Rule + val baseActivityTestRule = FragmentActivityTestRule( + SpriteActivity::class.java, + SpriteActivity.EXTRA_FRAGMENT_POSITION, + SpriteActivity.FRAGMENT_SCRIPTS + ) + + @get:Rule + val runtimePermissionRule: GrantPermissionRule = GrantPermissionRule.grant(Manifest.permission.CAMERA) + + @Category(AppUi::class, Functional::class, Quarantine::class) + @Test + fun cameraResourceNotUsedTest() { + val script = BrickTestUtils.createProjectAndGetStartScript("cameraResourceNotUsed") + val lastBrickInScript = ScriptEvaluationGateBrick.appendToScript(script) + + baseActivityTestRule.launchActivity() + onView(ViewMatchers.withId(R.id.button_play)).perform(ViewActions.click()) + lastBrickInScript.waitUntilEvaluated(3000) + + Assert.assertNull(StageActivity.getActiveCameraManager()) + } + + @Category(AppUi::class, Functional::class, Quarantine::class) + @Test + fun cameraOnTest() { + val script = BrickTestUtils.createProjectAndGetStartScript("cameraOnTest").also { + it.addBrick(CameraBrick(ON)) + } + val lastBrickInScript = ScriptEvaluationGateBrick.appendToScript(script) + + baseActivityTestRule.launchActivity() + onView(ViewMatchers.withId(R.id.button_play)).perform(ViewActions.click()) + lastBrickInScript.waitUntilEvaluated(3000) + + assertTrue(StageActivity.getActiveCameraManager().isCameraActive) + assertTrue(StageActivity.getActiveCameraManager().isCameraFacingFront) + } + + @Category(AppUi::class, Functional::class, Quarantine::class) + @Test + fun cameraStagePausedTest() { + val script = BrickTestUtils.createProjectAndGetStartScript("cameraStagePausedTest").also { + it.addBrick(CameraBrick(ON)) + } + val lastBrickInScript = ScriptEvaluationGateBrick.appendToScript(script) + + baseActivityTestRule.launchActivity() + onView(ViewMatchers.withId(R.id.button_play)).perform(ViewActions.click()) + lastBrickInScript.waitUntilEvaluated(3000) + + Espresso.pressBack() + assertFalse(StageActivity.getActiveCameraManager().isCameraActive) + } + + @Category(AppUi::class, Functional::class, Quarantine::class) + @Test + fun cameraOffTest() { + val script = BrickTestUtils.createProjectAndGetStartScript("cameraOffTest").also { + it.addBrick(CameraBrick(OFF)) + } + val lastBrickInScript = ScriptEvaluationGateBrick.appendToScript(script) + + baseActivityTestRule.launchActivity() + onView(ViewMatchers.withId(R.id.button_play)).perform(ViewActions.click()) + lastBrickInScript.waitUntilEvaluated(3000) + + assertFalse(StageActivity.getActiveCameraManager().isCameraActive) + } + + @Category(AppUi::class, Functional::class, Quarantine::class) + @Test + fun cameraFacingFrontTest() { + val script = BrickTestUtils.createProjectAndGetStartScript("cameraFacingFrontTest").also { + it.addBrick(ChooseCameraBrick(FRONT)) + it.addBrick(CameraBrick(ON)) + } + val lastBrickInScript = ScriptEvaluationGateBrick.appendToScript(script) + + baseActivityTestRule.launchActivity() + onView(ViewMatchers.withId(R.id.button_play)).perform(ViewActions.click()) + lastBrickInScript.waitUntilEvaluated(3000) + + assertTrue(StageActivity.getActiveCameraManager().isCameraActive) + assertTrue(StageActivity.getActiveCameraManager().isCameraFacingFront) + } + + @Category(AppUi::class, Functional::class, Quarantine::class) + @Test + fun cameraFacingBackTest() { + val script = BrickTestUtils.createProjectAndGetStartScript("cameraFacingBackTest").also { + it.addBrick(ChooseCameraBrick(BACK)) + it.addBrick(CameraBrick(ON)) + } + val lastBrickInScript = ScriptEvaluationGateBrick.appendToScript(script) + + baseActivityTestRule.launchActivity() + onView(ViewMatchers.withId(R.id.button_play)).perform(ViewActions.click()) + lastBrickInScript.waitUntilEvaluated(3000) + + assertTrue(StageActivity.getActiveCameraManager().isCameraActive) + assertFalse(StageActivity.getActiveCameraManager().isCameraFacingFront) + } + + companion object { + private const val BACK = 0 + private const val FRONT = 1 + private const val OFF = 0 + private const val ON = 1 + } +} diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/content/brick/stage/FaceDetectionResourceTest.kt b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/content/brick/stage/FaceDetectionResourceTest.kt new file mode 100644 index 00000000000..426455361ad --- /dev/null +++ b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/content/brick/stage/FaceDetectionResourceTest.kt @@ -0,0 +1,111 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2020 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.uiespresso.content.brick.stage + +import androidx.test.espresso.Espresso +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.matcher.ViewMatchers +import org.catrobat.catroid.R +import org.catrobat.catroid.content.bricks.SetSizeToBrick +import org.catrobat.catroid.formulaeditor.Formula +import org.catrobat.catroid.formulaeditor.FormulaElement +import org.catrobat.catroid.formulaeditor.Sensors +import org.catrobat.catroid.stage.StageActivity +import org.catrobat.catroid.testsuites.annotations.Cat.AppUi +import org.catrobat.catroid.testsuites.annotations.Cat.Quarantine +import org.catrobat.catroid.testsuites.annotations.Level.Functional +import org.catrobat.catroid.ui.SpriteActivity +import org.catrobat.catroid.uiespresso.content.brick.utils.BrickTestUtils +import org.catrobat.catroid.uiespresso.stage.utils.ScriptEvaluationGateBrick +import org.catrobat.catroid.uiespresso.util.rules.FragmentActivityTestRule +import org.junit.Assert +import org.junit.Assert.assertFalse +import org.junit.Rule +import org.junit.Test +import org.junit.experimental.categories.Category + +class FaceDetectionResourceTest { + private lateinit var formula: Formula + private lateinit var lastBrickInScript: ScriptEvaluationGateBrick + + @get:Rule + val baseActivityTestRule = FragmentActivityTestRule( + SpriteActivity::class.java, + SpriteActivity.EXTRA_FRAGMENT_POSITION, + SpriteActivity.FRAGMENT_SCRIPTS + ) + + @Category(AppUi::class, Functional::class, Quarantine::class) + @Test + fun testFaceDetectionEnabled() { + createProject(FormulaElement.ElementType.SENSOR, Sensors.FACE_SIZE.name) + baseActivityTestRule.launchActivity() + + Espresso.onView(ViewMatchers.withId(R.id.button_play)).perform(ViewActions.click()) + lastBrickInScript.waitUntilEvaluated(3000) + + Assert.assertTrue(faceDetectionOn()) + } + + @Category(AppUi::class, Functional::class, Quarantine::class) + @Test + fun testFaceDetectionDisabled() { + createProject(FormulaElement.ElementType.NUMBER, "42") + baseActivityTestRule.launchActivity() + + Espresso.onView(ViewMatchers.withId(R.id.button_play)).perform(ViewActions.click()) + lastBrickInScript.waitUntilEvaluated(3000) + + assertFalse(faceDetectionOn()) + } + + @Category(AppUi::class, Functional::class, Quarantine::class) + @Test + fun testFaceDetectionChanged() { + createProject(FormulaElement.ElementType.SENSOR, Sensors.FACE_SIZE.name) + baseActivityTestRule.launchActivity() + + Espresso.onView(ViewMatchers.withId(R.id.button_play)).perform(ViewActions.click()) + lastBrickInScript.waitUntilEvaluated(3000) + + Assert.assertTrue(faceDetectionOn()) + + Espresso.pressBack() + Espresso.onView(ViewMatchers.withId(R.id.stage_dialog_button_back)).perform(ViewActions.click()) + formula.root = FormulaElement(FormulaElement.ElementType.NUMBER, "42", null) + Espresso.onView(ViewMatchers.withId(R.id.button_play)).perform(ViewActions.click()) + + assertFalse(faceDetectionOn()) + } + + private fun createProject(type: FormulaElement.ElementType, value: String) { + formula = Formula(FormulaElement(type, value, null)) + + val script = BrickTestUtils.createProjectAndGetStartScript("FaceDetectionResourceTest").also { + it.addBrick(SetSizeToBrick(formula)) + } + lastBrickInScript = ScriptEvaluationGateBrick.appendToScript(script) + } + + private fun faceDetectionOn() = StageActivity.getActiveCameraManager()?.faceDetectionOn ?: false +} diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/content/brick/utils/BrickDataInteractionWrapper.java b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/content/brick/utils/BrickDataInteractionWrapper.java index bf20229a74f..8349a0a438f 100644 --- a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/content/brick/utils/BrickDataInteractionWrapper.java +++ b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/content/brick/utils/BrickDataInteractionWrapper.java @@ -113,7 +113,8 @@ public void performDeleteBrick() { BrickCoordinatesProvider.UPPER_LEFT_CORNER, Press.FINGER)); onView(anyOf(withText(R.string.brick_context_dialog_delete_brick), - withText(R.string.brick_context_dialog_delete_script))) + withText(R.string.brick_context_dialog_delete_script), + withText(R.string.brick_context_dialog_delete_definition))) .perform(click()); onView(withText(R.string.yes)) .perform(click()); diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/facedetection/FaceDetectionFormulaEditorComputeDialogTest.java b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/facedetection/FaceDetectionFormulaEditorComputeDialogTest.java deleted file mode 100644 index 5f104e8e446..00000000000 --- a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/facedetection/FaceDetectionFormulaEditorComputeDialogTest.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2020 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.uiespresso.facedetection; - -import android.Manifest; - -import org.catrobat.catroid.ProjectManager; -import org.catrobat.catroid.R; -import org.catrobat.catroid.camera.CameraManager; -import org.catrobat.catroid.content.Project; -import org.catrobat.catroid.content.Script; -import org.catrobat.catroid.content.Sprite; -import org.catrobat.catroid.content.StartScript; -import org.catrobat.catroid.content.bricks.NoteBrick; -import org.catrobat.catroid.facedetection.FaceDetectionHandler; -import org.catrobat.catroid.formulaeditor.Formula; -import org.catrobat.catroid.formulaeditor.FormulaElement; -import org.catrobat.catroid.formulaeditor.Sensors; -import org.catrobat.catroid.testsuites.annotations.Cat; -import org.catrobat.catroid.testsuites.annotations.Level; -import org.catrobat.catroid.ui.SpriteActivity; -import org.catrobat.catroid.uiespresso.formulaeditor.utils.FormulaEditorWrapper; -import org.catrobat.catroid.uiespresso.util.rules.FragmentActivityTestRule; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runner.RunWith; - -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.GrantPermissionRule; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -import static org.catrobat.catroid.uiespresso.formulaeditor.utils.FormulaEditorWrapper.onFormulaEditor; - -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.matcher.ViewMatchers.withId; - -@RunWith(AndroidJUnit4.class) -public class FaceDetectionFormulaEditorComputeDialogTest { - - @Rule - public FragmentActivityTestRule baseActivityTestRule = new - FragmentActivityTestRule<>(SpriteActivity.class, SpriteActivity.EXTRA_FRAGMENT_POSITION, SpriteActivity.FRAGMENT_SCRIPTS); - - @Rule - public GrantPermissionRule runtimePermissionRule = GrantPermissionRule.grant(Manifest.permission.CAMERA); - - @Before - public void setUp() throws Exception { - createProject("FaceDetectionFormulaEditorComputeDialogTest"); - baseActivityTestRule.launchActivity(); - } - - @Category({Cat.AppUi.class, Level.Functional.class}) - @Test - public void computeDialogFacedetectionResourceTest() { - onView(withId(R.id.brick_note_edit_text)) - .perform(click()); - CameraManager.makeInstance(); - - onFormulaEditor() - .performClickOn(FormulaEditorWrapper.Control.COMPUTE); - - assertTrue(FaceDetectionHandler.isFaceDetectionRunning()); - - pressBack(); - - assertFalse(FaceDetectionHandler.isFaceDetectionRunning()); - - onFormulaEditor() - .performClickOn(FormulaEditorWrapper.NumPad.NUM0); - - onFormulaEditor() - .performClickOn(FormulaEditorWrapper.Control.COMPUTE); - - assertFalse(FaceDetectionHandler.isFaceDetectionRunning()); - } - - public Project createProject(String projectName) { - Project project = new Project(ApplicationProvider.getApplicationContext(), projectName); - Sprite sprite = new Sprite("testSprite"); - Script script = new StartScript(); - - Formula formula = new Formula(new FormulaElement(FormulaElement.ElementType.SENSOR, Sensors.FACE_SIZE.name(), null)); - script.addBrick(new NoteBrick(formula)); - - sprite.addScript(script); - project.getDefaultScene().addSprite(sprite); - ProjectManager.getInstance().setCurrentProject(project); - ProjectManager.getInstance().setCurrentSprite(sprite); - - return project; - } -} diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/facedetection/FaceDetectionResourceStartedTest.java b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/facedetection/FaceDetectionResourceStartedTest.java deleted file mode 100644 index ce4b4698a0d..00000000000 --- a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/facedetection/FaceDetectionResourceStartedTest.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2020 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.uiespresso.facedetection; - -import org.catrobat.catroid.ProjectManager; -import org.catrobat.catroid.R; -import org.catrobat.catroid.content.Project; -import org.catrobat.catroid.content.Script; -import org.catrobat.catroid.content.Sprite; -import org.catrobat.catroid.content.StartScript; -import org.catrobat.catroid.content.bricks.SetSizeToBrick; -import org.catrobat.catroid.facedetection.FaceDetectionHandler; -import org.catrobat.catroid.formulaeditor.Formula; -import org.catrobat.catroid.formulaeditor.FormulaElement; -import org.catrobat.catroid.formulaeditor.Sensors; -import org.catrobat.catroid.rules.FlakyTestRule; -import org.catrobat.catroid.runner.Flaky; -import org.catrobat.catroid.testsuites.annotations.Cat; -import org.catrobat.catroid.testsuites.annotations.Level; -import org.catrobat.catroid.ui.SpriteActivity; -import org.catrobat.catroid.uiespresso.stage.utils.ScriptEvaluationGateBrick; -import org.catrobat.catroid.uiespresso.util.rules.FragmentActivityTestRule; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runner.RunWith; - -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -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.matcher.ViewMatchers.withId; - -@RunWith(AndroidJUnit4.class) -public class FaceDetectionResourceStartedTest { - - private Formula formula; - private ScriptEvaluationGateBrick lastBrickInScript; - - @Rule - public FragmentActivityTestRule baseActivityTestRule = new - FragmentActivityTestRule<>(SpriteActivity.class, SpriteActivity.EXTRA_FRAGMENT_POSITION, SpriteActivity.FRAGMENT_SCRIPTS); - - @Rule - public FlakyTestRule flakyTestRule = new FlakyTestRule(); - - @Category({Cat.AppUi.class, Level.Functional.class, Cat.Quarantine.class}) - @Flaky - @Test - public void facedetectionResourceEnabledTest() { - formula = new Formula( - new FormulaElement(FormulaElement.ElementType.SENSOR, Sensors.FACE_SIZE.name(), null)); - createProject(); - - baseActivityTestRule.launchActivity(); - onView(withId(R.id.button_play)).perform(click()); - - lastBrickInScript.waitUntilEvaluated(3000); - - assertTrue(FaceDetectionHandler.isFaceDetectionRunning()); - - pressBack(); - onView(withId(R.id.stage_dialog_button_back)).perform(click()); - - assertFalse(FaceDetectionHandler.isFaceDetectionRunning()); - } - - @Category({Cat.AppUi.class, Level.Functional.class, Cat.Quarantine.class}) - @Test - public void facedetectionResourceNotEnabledTest() { - formula = new Formula( - new FormulaElement(FormulaElement.ElementType.NUMBER, "42", null)); - createProject(); - - baseActivityTestRule.launchActivity(); - onView(withId(R.id.button_play)).perform(click()); - - lastBrickInScript.waitUntilEvaluated(3000); - - assertFalse(FaceDetectionHandler.isFaceDetectionRunning()); - - pressBack(); - onView(withId(R.id.stage_dialog_button_back)).perform(click()); - - assertFalse(FaceDetectionHandler.isFaceDetectionRunning()); - } - - @Category({Cat.AppUi.class, Level.Functional.class, Cat.Quarantine.class}) - @Flaky - @Test - public void facedetectionResourceChangedTest() { - formula = new Formula( - new FormulaElement(FormulaElement.ElementType.SENSOR, Sensors.FACE_SIZE.name(), null)); - createProject(); - - baseActivityTestRule.launchActivity(); - onView(withId(R.id.button_play)).perform(click()); - - lastBrickInScript.waitUntilEvaluated(3000); - - assertTrue(FaceDetectionHandler.isFaceDetectionRunning()); - - pressBack(); - onView(withId(R.id.stage_dialog_button_back)).perform(click()); - - assertFalse(FaceDetectionHandler.isFaceDetectionRunning()); - - formula.setRoot(new FormulaElement(FormulaElement.ElementType.NUMBER, "42", null)); - - onView(withId(R.id.button_play)).perform(click()); - - assertFalse(FaceDetectionHandler.isFaceDetectionRunning()); - } - - private void createProject() { - Project project = new Project(ApplicationProvider.getApplicationContext(), "FaceDetectionResourceStartedTest"); - Sprite sprite = new Sprite("testSprite"); - Script startScript = new StartScript(); - SetSizeToBrick setSizeToBrick = new SetSizeToBrick(formula); - startScript.addBrick(setSizeToBrick); - sprite.addScript(startScript); - project.getDefaultScene().addSprite(sprite); - ProjectManager.getInstance().setCurrentProject(project); - ProjectManager.getInstance().setCurrentSprite(sprite); - - lastBrickInScript = ScriptEvaluationGateBrick.appendToScript(startScript); - } -} diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/ui/activity/SettingsFragmentTest.java b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/ui/activity/SettingsFragmentTest.java index aa465cfd1bc..a994b060ea9 100644 --- a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/ui/activity/SettingsFragmentTest.java +++ b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/ui/activity/SettingsFragmentTest.java @@ -52,7 +52,6 @@ import java.util.Map; import androidx.test.core.app.ApplicationProvider; -import androidx.test.espresso.action.ViewActions; import androidx.test.espresso.intent.Intents; import androidx.test.espresso.matcher.PreferenceMatchers; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -258,13 +257,10 @@ public void webAccessSettingTest() { onView(withText(R.string.preference_screen_web_access_title)).check(matches(isDisplayed())); onView(withId(android.R.id.edit)).perform(typeText("domain.net")); - onView(withId(android.R.id.button2)).perform(click()); - onData(PreferenceMatchers.withTitle(R.string.preference_title_web_access)).perform(click()); - onView(withId(android.R.id.button1)).perform(click()); + onView(withId(android.R.id.button1)).check(matches(isDisplayed())); + onView(withId(android.R.id.button2)).check(matches(isDisplayed())); - onData(PreferenceMatchers.withTitle(R.string.preference_title_web_access)).perform(click()); - ViewActions.closeSoftKeyboard(); onView(withId(android.R.id.button3)).perform(click()); intended(expectedBrowserIntent); } diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/ui/fragment/UserDefinedBrickTest.java b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/ui/fragment/UserDefinedBrickTest.java index 475dc98319b..408d74c1ebd 100644 --- a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/ui/fragment/UserDefinedBrickTest.java +++ b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/ui/fragment/UserDefinedBrickTest.java @@ -23,8 +23,6 @@ package org.catrobat.catroid.uiespresso.ui.fragment; import org.catrobat.catroid.R; -import org.catrobat.catroid.content.Script; -import org.catrobat.catroid.content.bricks.GlideToBrick; import org.catrobat.catroid.content.bricks.UserDefinedBrick; import org.catrobat.catroid.test.utils.TestUtils; import org.catrobat.catroid.ui.SpriteActivity; @@ -77,9 +75,7 @@ public void tearDown() throws IOException { @Before public void setUp() throws IOException { - Script script = - BrickTestUtils.createProjectAndGetStartScript(UserDefinedBrickTest.class.getSimpleName()); - script.addBrick(new GlideToBrick()); + BrickTestUtils.createProjectAndGetStartScript(UserDefinedBrickTest.class.getSimpleName()); baseActivityTestRule.launchActivity(); } diff --git a/catroid/src/arduino/res/drawable-hdpi/ic_launcher.png b/catroid/src/arduino/res/drawable-hdpi/ic_launcher.png deleted file mode 100644 index 8b3538cd929..00000000000 Binary files a/catroid/src/arduino/res/drawable-hdpi/ic_launcher.png and /dev/null differ diff --git a/catroid/src/arduino/res/drawable-hdpi/pc_toolbar_icon.png b/catroid/src/arduino/res/drawable-hdpi/pc_toolbar_icon.png deleted file mode 100644 index affcd2794ba..00000000000 Binary files a/catroid/src/arduino/res/drawable-hdpi/pc_toolbar_icon.png and /dev/null differ diff --git a/catroid/src/arduino/res/drawable-ldpi/ic_launcher.png b/catroid/src/arduino/res/drawable-ldpi/ic_launcher.png deleted file mode 100644 index 737888f3529..00000000000 Binary files a/catroid/src/arduino/res/drawable-ldpi/ic_launcher.png and /dev/null differ diff --git a/catroid/src/arduino/res/drawable-ldpi/pc_toolbar_icon.png b/catroid/src/arduino/res/drawable-ldpi/pc_toolbar_icon.png deleted file mode 100644 index ac2df6b6564..00000000000 Binary files a/catroid/src/arduino/res/drawable-ldpi/pc_toolbar_icon.png and /dev/null differ diff --git a/catroid/src/arduino/res/drawable-mdpi/ic_launcher.png b/catroid/src/arduino/res/drawable-mdpi/ic_launcher.png deleted file mode 100644 index b6788a7545c..00000000000 Binary files a/catroid/src/arduino/res/drawable-mdpi/ic_launcher.png and /dev/null differ diff --git a/catroid/src/arduino/res/drawable-mdpi/pc_toolbar_icon.png b/catroid/src/arduino/res/drawable-mdpi/pc_toolbar_icon.png deleted file mode 100644 index 3e099a97846..00000000000 Binary files a/catroid/src/arduino/res/drawable-mdpi/pc_toolbar_icon.png and /dev/null differ diff --git a/catroid/src/arduino/res/drawable-xhdpi/ic_launcher.png b/catroid/src/arduino/res/drawable-xhdpi/ic_launcher.png deleted file mode 100644 index ef5198b8079..00000000000 Binary files a/catroid/src/arduino/res/drawable-xhdpi/ic_launcher.png and /dev/null differ diff --git a/catroid/src/arduino/res/drawable-xhdpi/pc_toolbar_icon.png b/catroid/src/arduino/res/drawable-xhdpi/pc_toolbar_icon.png deleted file mode 100644 index 90b69f75648..00000000000 Binary files a/catroid/src/arduino/res/drawable-xhdpi/pc_toolbar_icon.png and /dev/null differ diff --git a/catroid/src/arduino/res/drawable-xxhdpi/ic_launcher.png b/catroid/src/arduino/res/drawable-xxhdpi/ic_launcher.png deleted file mode 100644 index eadcb5459de..00000000000 Binary files a/catroid/src/arduino/res/drawable-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/catroid/src/arduino/res/drawable-xxhdpi/pc_toolbar_icon.png b/catroid/src/arduino/res/drawable-xxhdpi/pc_toolbar_icon.png deleted file mode 100644 index 38b4526bc69..00000000000 Binary files a/catroid/src/arduino/res/drawable-xxhdpi/pc_toolbar_icon.png and /dev/null differ diff --git a/catroid/src/main/java/org/catrobat/catroid/camera/JpgPreviewCallback.java b/catroid/src/catroid/java/org/catrobat/catroid/common/defaultprojectcreators/DefaultExampleProject.java similarity index 88% rename from catroid/src/main/java/org/catrobat/catroid/camera/JpgPreviewCallback.java rename to catroid/src/catroid/java/org/catrobat/catroid/common/defaultprojectcreators/DefaultExampleProject.java index 74247b00337..b73c6cb4ceb 100644 --- a/catroid/src/main/java/org/catrobat/catroid/camera/JpgPreviewCallback.java +++ b/catroid/src/catroid/java/org/catrobat/catroid/common/defaultprojectcreators/DefaultExampleProject.java @@ -20,9 +20,8 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package org.catrobat.catroid.camera; -public interface JpgPreviewCallback { +package org.catrobat.catroid.common.defaultprojectcreators; - void onFrame(byte[] jpgData); +public class DefaultExampleProject extends DefaultProjectCreator { } diff --git a/catroid/src/main/java/org/catrobat/catroid/content/actions/CameraBrickAction.java b/catroid/src/createAtSchool/java/org/catrobat/catroid/common/defaultprojectcreators/DefaultExampleProject.java similarity index 66% rename from catroid/src/main/java/org/catrobat/catroid/content/actions/CameraBrickAction.java rename to catroid/src/createAtSchool/java/org/catrobat/catroid/common/defaultprojectcreators/DefaultExampleProject.java index 2ce6f73e624..b73c6cb4ceb 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/actions/CameraBrickAction.java +++ b/catroid/src/createAtSchool/java/org/catrobat/catroid/common/defaultprojectcreators/DefaultExampleProject.java @@ -21,26 +21,7 @@ * along with this program. If not, see . */ -package org.catrobat.catroid.content.actions; +package org.catrobat.catroid.common.defaultprojectcreators; -import com.badlogic.gdx.scenes.scene2d.actions.TemporalAction; - -import org.catrobat.catroid.camera.CameraManager; - -public class CameraBrickAction extends TemporalAction { - - CameraManager.CameraState state = CameraManager.CameraState.notUsed; - - @Override - protected void update(float percent) { - CameraManager.getInstance().updatePreview(state); - } - - @Override - public void reset() { - } - - public void setCameraAction(CameraManager.CameraState newState) { - state = newState; - } +public class DefaultExampleProject extends DefaultProjectCreator { } diff --git a/catroid/src/arduino/java/org/catrobat/catroid/common/FlavoredConstants.java b/catroid/src/embroideryDesigner/java/org/catrobat/catroid/common/FlavoredConstants.java similarity index 98% rename from catroid/src/arduino/java/org/catrobat/catroid/common/FlavoredConstants.java rename to catroid/src/embroideryDesigner/java/org/catrobat/catroid/common/FlavoredConstants.java index 6ec9fd47a38..a8b8761d0db 100644 --- a/catroid/src/arduino/java/org/catrobat/catroid/common/FlavoredConstants.java +++ b/catroid/src/embroideryDesigner/java/org/catrobat/catroid/common/FlavoredConstants.java @@ -33,9 +33,9 @@ public final class FlavoredConstants { // Web: - public static final String BASE_URL_HTTPS = MAIN_URL_HTTPS + "/arduino/"; + public static final String BASE_URL_HTTPS = MAIN_URL_HTTPS + "/embroidery/"; - public static final String POCKET_CODE_EXTERNAL_STORAGE_FOLDER_NAME = "Arduino"; + public static final String POCKET_CODE_EXTERNAL_STORAGE_FOLDER_NAME = "EmbroideryDesigner"; public static final File DEFAULT_ROOT_DIRECTORY = CatroidApplication.getAppContext().getFilesDir(); diff --git a/catroid/src/embroideryDesigner/java/org/catrobat/catroid/common/defaultprojectcreators/DefaultExampleProject.java b/catroid/src/embroideryDesigner/java/org/catrobat/catroid/common/defaultprojectcreators/DefaultExampleProject.java new file mode 100644 index 00000000000..dd195ddc53b --- /dev/null +++ b/catroid/src/embroideryDesigner/java/org/catrobat/catroid/common/defaultprojectcreators/DefaultExampleProject.java @@ -0,0 +1,177 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2018 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.common.defaultprojectcreators; + +import android.content.Context; +import android.graphics.BitmapFactory; + +import org.catrobat.catroid.R; +import org.catrobat.catroid.common.LookData; +import org.catrobat.catroid.content.Project; +import org.catrobat.catroid.content.Scene; +import org.catrobat.catroid.content.Script; +import org.catrobat.catroid.content.Sprite; +import org.catrobat.catroid.content.StartScript; +import org.catrobat.catroid.content.bricks.MoveNStepsBrick; +import org.catrobat.catroid.content.bricks.RepeatBrick; +import org.catrobat.catroid.content.bricks.SetVariableBrick; +import org.catrobat.catroid.content.bricks.TurnRightBrick; +import org.catrobat.catroid.content.bricks.ZigZagStitchBrick; +import org.catrobat.catroid.formulaeditor.Formula; +import org.catrobat.catroid.formulaeditor.FormulaElement; +import org.catrobat.catroid.formulaeditor.Operators; +import org.catrobat.catroid.formulaeditor.UserVariable; +import org.catrobat.catroid.io.ResourceImporter; +import org.catrobat.catroid.io.XstreamSerializer; +import org.catrobat.catroid.utils.ImageEditing; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; + +import static org.catrobat.catroid.common.Constants.DEFAULT_IMAGE_EXTENSION; +import static org.catrobat.catroid.common.Constants.IMAGE_DIRECTORY_NAME; +import static org.catrobat.catroid.common.ScreenValues.SCREEN_HEIGHT; +import static org.catrobat.catroid.common.ScreenValues.SCREEN_WIDTH; +import static org.catrobat.catroid.common.Constants.SCREENSHOT_AUTOMATIC_FILE_NAME; + +public class DefaultExampleProject extends DefaultProjectCreator { + public DefaultExampleProject() { + defaultProjectNameResourceId = R.string.default_project_name; + } + + @Override + public Project createDefaultProject(String name, Context context, boolean landscapeMode) throws IOException { + Project project = new Project(context, name, landscapeMode); + + if (project.getDirectory().exists()) { + throw new IOException("Cannot create new project at " + + project.getDirectory().getAbsolutePath() + + ", directory already exists."); + } + + XstreamSerializer.getInstance().saveProject(project); + + if (!project.getDirectory().isDirectory()) { + throw new FileNotFoundException("Cannot create project at " + project.getDirectory().getAbsolutePath()); + } + + int needleDrawableId; + int backgroundDrawableId; + int screenshotDrawableId; + + if (landscapeMode) { + backgroundDrawableId = R.drawable.default_project_background_landscape; + needleDrawableId = R.drawable.default_project_needle; + screenshotDrawableId = R.drawable.default_project_screenshot_landscape; + } else { + backgroundDrawableId = R.drawable.default_project_background_portrait; + needleDrawableId = R.drawable.default_project_needle; + screenshotDrawableId = R.drawable.default_project_screenshot; + } + + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeResource(context.getResources(), backgroundDrawableId, options); + + backgroundImageScaleFactor = ImageEditing.calculateScaleFactor( + options.outWidth, + options.outHeight, + SCREEN_WIDTH, + SCREEN_HEIGHT); + + Scene scene = project.getDefaultScene(); + + File imageDir = new File(scene.getDirectory(), IMAGE_DIRECTORY_NAME); + + String imageFileName = "img" + DEFAULT_IMAGE_EXTENSION; + + File needleFile1 = ResourceImporter.createImageFileFromResourcesInDirectory(context.getResources(), + needleDrawableId, + imageDir, + imageFileName, + backgroundImageScaleFactor); + + ResourceImporter.createImageFileFromResourcesInDirectory(context.getResources(), + screenshotDrawableId, + scene.getDirectory(), + SCREENSHOT_AUTOMATIC_FILE_NAME, + 1); + + Sprite needle = new Sprite(context.getString(R.string.default_project_needle_name)); + + scene.addSprite(needle); + + needle.getLookList() + .add(new LookData(context.getString(R.string.default_project_needle_name), needleFile1)); + + Script script = new StartScript(); + + UserVariable variableOuterLoop = new UserVariable(context.getString(R.string.default_project_outer_loop)); + UserVariable variableInnerLoop = new UserVariable(context.getString(R.string.default_project_inner_loop)); + + needle.addUserVariable(variableInnerLoop); + needle.addUserVariable(variableOuterLoop); + + script.addBrick(new SetVariableBrick(new Formula(8), variableInnerLoop)); + script.addBrick(new SetVariableBrick(new Formula(8), variableOuterLoop)); + script.addBrick(new ZigZagStitchBrick(new Formula(2), new Formula(10))); + + Formula repeatUntilFormulaOuterLoop = new Formula(1); + repeatUntilFormulaOuterLoop.setRoot(new FormulaElement(FormulaElement.ElementType.USER_VARIABLE, variableOuterLoop.getName(), + null)); + + RepeatBrick outerLoopRepeat = new RepeatBrick(repeatUntilFormulaOuterLoop); + + Formula repeatUntilFormulaInnerLoop = new Formula(1); + repeatUntilFormulaInnerLoop.setRoot(new FormulaElement(FormulaElement.ElementType.USER_VARIABLE, + variableInnerLoop.getName(), + null)); + + RepeatBrick innerLoopRepeat = new RepeatBrick(repeatUntilFormulaInnerLoop); + innerLoopRepeat.addBrick(new MoveNStepsBrick(new Formula(100))); + + FormulaElement innerLoopFormula = new FormulaElement(FormulaElement.ElementType.OPERATOR, + Operators.DIVIDE.name(), null, + new FormulaElement(FormulaElement.ElementType.NUMBER, String.valueOf(360), null), + new FormulaElement(FormulaElement.ElementType.USER_VARIABLE, variableInnerLoop.getName(), + null)); + + innerLoopRepeat.addBrick(new TurnRightBrick(new Formula(innerLoopFormula))); + + outerLoopRepeat.addBrick(innerLoopRepeat); + FormulaElement outerLoopFormula = new FormulaElement(FormulaElement.ElementType.OPERATOR, + Operators.DIVIDE.name(), null, + new FormulaElement(FormulaElement.ElementType.NUMBER, String.valueOf(360), null), + new FormulaElement(FormulaElement.ElementType.USER_VARIABLE, variableOuterLoop.getName(), + null)); + outerLoopRepeat.addBrick(new TurnRightBrick(new Formula(outerLoopFormula))); + + script.addBrick(outerLoopRepeat); + needle.addScript(script); + + XstreamSerializer.getInstance().saveProject(project); + return project; + } +} diff --git a/catroid/src/embroideryDesigner/res/drawable-hdpi/ic_launcher.png b/catroid/src/embroideryDesigner/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 00000000000..fdcc527d44e Binary files /dev/null and b/catroid/src/embroideryDesigner/res/drawable-hdpi/ic_launcher.png differ diff --git a/catroid/src/embroideryDesigner/res/drawable-hdpi/pc_toolbar_icon.png b/catroid/src/embroideryDesigner/res/drawable-hdpi/pc_toolbar_icon.png new file mode 100644 index 00000000000..84f6dedc3e0 Binary files /dev/null and b/catroid/src/embroideryDesigner/res/drawable-hdpi/pc_toolbar_icon.png differ diff --git a/catroid/src/embroideryDesigner/res/drawable-ldpi/ic_launcher.png b/catroid/src/embroideryDesigner/res/drawable-ldpi/ic_launcher.png new file mode 100644 index 00000000000..4e9dcf3ecc0 Binary files /dev/null and b/catroid/src/embroideryDesigner/res/drawable-ldpi/ic_launcher.png differ diff --git a/catroid/src/embroideryDesigner/res/drawable-ldpi/pc_toolbar_icon.png b/catroid/src/embroideryDesigner/res/drawable-ldpi/pc_toolbar_icon.png new file mode 100644 index 00000000000..8c8dfd0eb51 Binary files /dev/null and b/catroid/src/embroideryDesigner/res/drawable-ldpi/pc_toolbar_icon.png differ diff --git a/catroid/src/embroideryDesigner/res/drawable-mdpi/ic_launcher.png b/catroid/src/embroideryDesigner/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 00000000000..95e4837e998 Binary files /dev/null and b/catroid/src/embroideryDesigner/res/drawable-mdpi/ic_launcher.png differ diff --git a/catroid/src/embroideryDesigner/res/drawable-mdpi/pc_toolbar_icon.png b/catroid/src/embroideryDesigner/res/drawable-mdpi/pc_toolbar_icon.png new file mode 100644 index 00000000000..428380a57ec Binary files /dev/null and b/catroid/src/embroideryDesigner/res/drawable-mdpi/pc_toolbar_icon.png differ diff --git a/catroid/src/embroideryDesigner/res/drawable-nodpi/default_project_needle.png b/catroid/src/embroideryDesigner/res/drawable-nodpi/default_project_needle.png new file mode 100644 index 00000000000..16224d14d6c Binary files /dev/null and b/catroid/src/embroideryDesigner/res/drawable-nodpi/default_project_needle.png differ diff --git a/catroid/src/embroideryDesigner/res/drawable-nodpi/default_project_screenshot.png b/catroid/src/embroideryDesigner/res/drawable-nodpi/default_project_screenshot.png new file mode 100644 index 00000000000..51a8dcc8d99 Binary files /dev/null and b/catroid/src/embroideryDesigner/res/drawable-nodpi/default_project_screenshot.png differ diff --git a/catroid/src/embroideryDesigner/res/drawable-nodpi/default_project_screenshot_landscape.png b/catroid/src/embroideryDesigner/res/drawable-nodpi/default_project_screenshot_landscape.png new file mode 100644 index 00000000000..2a75860d4fc Binary files /dev/null and b/catroid/src/embroideryDesigner/res/drawable-nodpi/default_project_screenshot_landscape.png differ diff --git a/catroid/src/embroideryDesigner/res/drawable-xhdpi/ic_launcher.png b/catroid/src/embroideryDesigner/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 00000000000..a5d56e6028d Binary files /dev/null and b/catroid/src/embroideryDesigner/res/drawable-xhdpi/ic_launcher.png differ diff --git a/catroid/src/embroideryDesigner/res/drawable-xhdpi/pc_toolbar_icon.png b/catroid/src/embroideryDesigner/res/drawable-xhdpi/pc_toolbar_icon.png new file mode 100644 index 00000000000..0ef2beb661b Binary files /dev/null and b/catroid/src/embroideryDesigner/res/drawable-xhdpi/pc_toolbar_icon.png differ diff --git a/catroid/src/embroideryDesigner/res/drawable-xxhdpi/ic_launcher.png b/catroid/src/embroideryDesigner/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 00000000000..4d7f923a31c Binary files /dev/null and b/catroid/src/embroideryDesigner/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/catroid/src/embroideryDesigner/res/drawable-xxhdpi/pc_toolbar_icon.png b/catroid/src/embroideryDesigner/res/drawable-xxhdpi/pc_toolbar_icon.png new file mode 100644 index 00000000000..7085fc4d6ae Binary files /dev/null and b/catroid/src/embroideryDesigner/res/drawable-xxhdpi/pc_toolbar_icon.png differ diff --git a/catroid/src/embroideryDesigner/res/values/colors.xml b/catroid/src/embroideryDesigner/res/values/colors.xml new file mode 100644 index 00000000000..8f525b1265f --- /dev/null +++ b/catroid/src/embroideryDesigner/res/values/colors.xml @@ -0,0 +1,53 @@ + + + + + + #20103B + @color/solid_white + + + @color/solid_white + @color/solid_white + @color/accent + @color/accent + + + #380F50 + #392757 + #392757 + @color/accent + + + @color/solid_white + @color/solid_white + #33B5E5 + + + #390B52 + #390B52 + #380F50 + #F9F9F9 + + diff --git a/catroid/src/arduino/res/values/strings.xml b/catroid/src/embroideryDesigner/res/values/strings.xml similarity index 93% rename from catroid/src/arduino/res/values/strings.xml rename to catroid/src/embroideryDesigner/res/values/strings.xml index c0e692580c9..9b3849a0d87 100644 --- a/catroid/src/arduino/res/values/strings.xml +++ b/catroid/src/embroideryDesigner/res/values/strings.xml @@ -23,5 +23,5 @@ --> - Pocket Code for Arduino - \ No newline at end of file + Embroidery Designer + diff --git a/catroid/src/lunaAndCat/java/org/catrobat/catroid/common/defaultprojectcreators/DefaultExampleProject.java b/catroid/src/lunaAndCat/java/org/catrobat/catroid/common/defaultprojectcreators/DefaultExampleProject.java new file mode 100644 index 00000000000..b73c6cb4ceb --- /dev/null +++ b/catroid/src/lunaAndCat/java/org/catrobat/catroid/common/defaultprojectcreators/DefaultExampleProject.java @@ -0,0 +1,27 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2018 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.common.defaultprojectcreators; + +public class DefaultExampleProject extends DefaultProjectCreator { +} diff --git a/catroid/src/lunaAndCat/res/values/colors.xml b/catroid/src/lunaAndCat/res/values/colors.xml index 882c0a960a6..b51fd284b06 100644 --- a/catroid/src/lunaAndCat/res/values/colors.xml +++ b/catroid/src/lunaAndCat/res/values/colors.xml @@ -36,6 +36,7 @@ #2A1848 #392757 + #392757 @color/accent diff --git a/catroid/src/main/AndroidManifest.xml b/catroid/src/main/AndroidManifest.xml index 93652e9b761..7ebd6b9b56a 100644 --- a/catroid/src/main/AndroidManifest.xml +++ b/catroid/src/main/AndroidManifest.xml @@ -44,7 +44,7 @@ diff --git a/catroid/src/main/java/org/catrobat/catroid/camera/CameraManager.java b/catroid/src/main/java/org/catrobat/catroid/camera/CameraManager.java deleted file mode 100644 index 93d524823e4..00000000000 --- a/catroid/src/main/java/org/catrobat/catroid/camera/CameraManager.java +++ /dev/null @@ -1,570 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2018 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.camera; - -import android.graphics.Rect; -import android.graphics.SurfaceTexture; -import android.graphics.YuvImage; -import android.hardware.Camera; -import android.hardware.Camera.Parameters; -import android.util.Log; -import android.view.ViewGroup; -import android.view.ViewParent; - -import org.catrobat.catroid.ProjectManager; -import org.catrobat.catroid.common.ScreenValues; -import org.catrobat.catroid.facedetection.FaceDetectionHandler; -import org.catrobat.catroid.stage.CameraSurface; -import org.catrobat.catroid.stage.DeviceCameraControl; -import org.catrobat.catroid.stage.StageActivity; -import org.catrobat.catroid.utils.FlashUtil; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import androidx.annotation.VisibleForTesting; - -public final class CameraManager implements DeviceCameraControl, Camera.PreviewCallback { - - public class CameraInformation { - - protected int cameraId; - protected boolean flashAvailable = false; - - protected CameraInformation(int cameraId, boolean flashAvailable) { - this.cameraId = cameraId; - this.flashAvailable = flashAvailable; - } - } - - public static final int TEXTURE_NAME = 1; - private static final String TAG = CameraManager.class.getSimpleName(); - private static CameraManager instance; - private Camera currentCamera; - private SurfaceTexture texture; - private List callbacks = new ArrayList<>(); - private int previewFormat; - private int previewWidth; - private int previewHeight; - - private CameraInformation defaultCameraInformation = null; - private CameraInformation currentCameraInformation = null; - private CameraInformation frontCameraInformation = null; - private CameraInformation backCameraInformation = null; - - private int cameraCount = 0; - - StageActivity stageActivity = null; - CameraSurface cameraSurface = null; - - public final Object cameraChangeLock = new Object(); - private final Object cameraBaseLock = new Object(); - private boolean wasRunning = false; - - //Mode used for CameraPreview - public enum CameraState { - notUsed, - prepare, - previewRunning, - previewPaused, - stopped - } - - private CameraState state = CameraState.notUsed; - - public static CameraManager getInstance() { - return instance; - } - - @VisibleForTesting(otherwise = VisibleForTesting.NONE) - public static void setInstance(CameraManager cameraManager) { - instance = cameraManager; - } - - public static void makeInstance() { - instance = new CameraManager(); - } - - private CameraManager() { - cameraCount = Camera.getNumberOfCameras(); - for (int id = 0; id < cameraCount; id++) { - Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); - Camera.getCameraInfo(id, cameraInfo); - - if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) { - backCameraInformation = new CameraInformation(id, hasCameraFlash(id)); - currentCameraInformation = backCameraInformation; - } - if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { - frontCameraInformation = new CameraInformation(id, hasCameraFlash(id)); - currentCameraInformation = frontCameraInformation; - } - } - defaultCameraInformation = currentCameraInformation; - createTexture(); - } - - public boolean hasBackCamera() { - return backCameraInformation != null; - } - - public boolean hasFrontCamera() { - return frontCameraInformation != null; - } - - private boolean hasCameraFlash(int cameraId) { - try { - Camera camera; - camera = Camera.open(cameraId); - - if (camera == null) { - return false; - } - - Camera.Parameters parameters = camera.getParameters(); - - if (parameters.getFlashMode() == null) { - camera.release(); - return false; - } - - List supportedFlashModes = parameters.getSupportedFlashModes(); - if (supportedFlashModes == null || supportedFlashModes.isEmpty() - || (supportedFlashModes.size() == 1 && supportedFlashModes.get(0).equals(Camera.Parameters.FLASH_MODE_OFF))) { - camera.release(); - return false; - } - - camera.release(); - return true; - } catch (Exception exception) { - Log.e(TAG, "failed checking for flash", exception); - return false; - } - } - - public boolean isCurrentCameraFacingBack() { - return currentCameraInformation == backCameraInformation; - } - - public boolean isCurrentCameraFacingFront() { - return currentCameraInformation == frontCameraInformation; - } - - public boolean isCameraActive() { - return state == CameraState.previewRunning; - } - - public void setToDefaultCamera() { - updateCamera(defaultCameraInformation); - } - - public boolean setToBackCamera() { - if (!hasBackCamera()) { - return false; - } - updateCamera(backCameraInformation); - return true; - } - - public boolean setToFrontCamera() { - if (!hasFrontCamera()) { - return false; - } - updateCamera(frontCameraInformation); - return true; - } - - private void updateCamera(CameraInformation cameraInformation) { - - synchronized (cameraChangeLock) { - CameraState currentState = state; - - if (cameraInformation == currentCameraInformation) { - return; - } - - currentCameraInformation = cameraInformation; - - if (FlashUtil.isOn() && !currentCameraInformation.flashAvailable) { - Log.w(TAG, "destroy Stage because flash isOn while changing camera"); - CameraManager.getInstance().destroyStage(); - return; - } - - FlashUtil.pauseFlash(); - FaceDetectionHandler.pauseFaceDetection(); - releaseCamera(); - - startCamera(); - FaceDetectionHandler.resumeFaceDetection(); - FlashUtil.resumeFlash(); - - if (currentState == CameraState.prepare - || currentState == CameraState.previewRunning) { - changeCameraAsync(); - } - } - } - - public boolean startCamera() { - synchronized (cameraBaseLock) { - if (currentCamera == null) { - boolean success = createCamera(); - if (!success) { - return false; - } - } - Camera.Parameters cameraParameters = currentCamera.getParameters(); - previewFormat = cameraParameters.getPreviewFormat(); - previewWidth = cameraParameters.getPreviewSize().width; - previewHeight = cameraParameters.getPreviewSize().height; - try { - currentCamera.startPreview(); - } catch (Exception e) { - Log.e(TAG, e.getMessage()); - return false; - } - return true; - } - } - - public void releaseCamera() { - synchronized (cameraBaseLock) { - if (currentCamera == null) { - return; - } - currentCamera.setPreviewCallback(null); - currentCamera.stopPreview(); - currentCamera.release(); - currentCamera = null; - } - } - - private boolean createCamera() { - if (currentCamera != null) { - return false; - } - - try { - currentCamera = Camera.open(currentCameraInformation.cameraId); - if (ProjectManager.getInstance().isCurrentProjectLandscapeMode()) { - currentCamera.setDisplayOrientation(0); - } else { - currentCamera.setDisplayOrientation(90); - } - - Camera.Parameters cameraParameters = currentCamera.getParameters(); - List previewSizes = cameraParameters.getSupportedPreviewSizes(); - int previewHeight = 0; - int previewWidth = 0; - for (int i = 0; i < previewSizes.size() - && previewSizes.get(i).height <= ScreenValues.SCREEN_HEIGHT; i++) { - if (previewSizes.get(i).height > previewHeight) { - previewHeight = previewSizes.get(i).height; - previewWidth = previewSizes.get(i).width; - } - } - - cameraParameters.setPreviewSize(previewWidth, previewHeight); - currentCamera.setParameters(cameraParameters); - } catch (RuntimeException runtimeException) { - Log.e(TAG, "Creating camera caused an exception", runtimeException); - return false; - } - - currentCamera.setPreviewCallbackWithBuffer(this); - if (texture != null) { - try { - setTexture(); - } catch (IOException ioException) { - Log.e(TAG, "Setting preview texture failed!", ioException); - return false; - } - } - return true; - } - - public boolean hasCurrentCameraFlash() { - return (currentCameraInformation != null && currentCameraInformation.flashAvailable); - } - - public boolean hasBackCameraFlash() { - return (hasBackCamera() && backCameraInformation.flashAvailable); - } - - public boolean hasFrontCameraFlash() { - return (hasFrontCamera() && frontCameraInformation.flashAvailable); - } - - public CameraState getState() { - return state; - } - - public Camera getCurrentCamera() { - return currentCamera; - } - - public void addOnJpgPreviewFrameCallback(JpgPreviewCallback callback) { - if (callbacks.contains(callback)) { - return; - } - callbacks.add(callback); - } - - public void removeOnJpgPreviewFrameCallback(JpgPreviewCallback callback) { - callbacks.remove(callback); - } - - @Override - public void onPreviewFrame(byte[] data, Camera camera) { - if (callbacks.size() == 0) { - return; - } - byte[] jpgData = getDecodeableBytesFromCameraFrame(data); - for (JpgPreviewCallback callback : callbacks) { - callback.onFrame(jpgData); - } - } - - private byte[] getDecodeableBytesFromCameraFrame(byte[] cameraData) { - byte[] decodableBytes; - YuvImage image = new YuvImage(cameraData, previewFormat, previewWidth, previewHeight, null); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - image.compressToJpeg(new Rect(0, 0, previewWidth, previewHeight), 50, out); - decodableBytes = out.toByteArray(); - return decodableBytes; - } - - private void createTexture() { - texture = new SurfaceTexture(TEXTURE_NAME); - } - - private void setTexture() throws IOException { - currentCamera.setPreviewTexture(texture); - } - - public void setFlashParams(Parameters flash) { - Log.d(TAG, flash.toString()); - if (currentCamera != null && flash != null) { - Parameters current = currentCamera.getParameters(); - current.setFlashMode(flash.getFlashMode()); - currentCamera.setParameters(current); - } - } - - @Override - public void prepareCamera() { - state = CameraState.previewRunning; - - if (cameraSurface == null) { - cameraSurface = new CameraSurface(stageActivity); - } - - ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup - .LayoutParams.WRAP_CONTENT); - - //java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first. - ViewGroup parent = (ViewGroup) cameraSurface.getParent(); - if (parent != null) { - parent.removeView(cameraSurface); - } - stageActivity.addContentView(cameraSurface, params); - startCamera(); - } - - @Override - public void stopPreview() { - state = CameraState.notUsed; - if (cameraSurface != null) { - ViewParent parentView = cameraSurface.getParent(); - if (parentView instanceof ViewGroup) { - ViewGroup viewGroup = (ViewGroup) parentView; - viewGroup.removeView(cameraSurface); - } - - cameraSurface = null; - try { - if (currentCamera != null) { - currentCamera.stopPreview(); - setTexture(); - } - if (FaceDetectionHandler.isFaceDetectionRunning() || FlashUtil.isAvailable()) { - currentCamera.startPreview(); - } - } catch (IOException e) { - Log.e(TAG, "reset Texture failed at stopPreview"); - Log.e(TAG, e.getMessage()); - } - } - } - - @Override - public void pausePreview() { - if (state == CameraState.previewRunning) { - wasRunning = true; - } - - stopPreview(); - state = CameraState.previewPaused; - } - - @Override - public void resumePreview() { - prepareCamera(); - wasRunning = false; - } - - public void pauseForScene() { - Runnable r = new Runnable() { - public void run() { - pausePreview(); - } - }; - stageActivity.post(r); - wasRunning = false; - state = CameraState.notUsed; - } - - public void resumeForScene() { - Runnable r = new Runnable() { - public void run() { - resumePreview(); - } - }; - stageActivity.post(r); - } - - @Override - public void prepareCameraAsync() { - Runnable r = new Runnable() { - public void run() { - prepareCamera(); - } - }; - stageActivity.post(r); - } - - @Override - public void stopPreviewAsync() { - Runnable r = new Runnable() { - public void run() { - stopPreview(); - } - }; - stageActivity.post(r); - } - - @Override - public void pausePreviewAsync() { - if (state == CameraState.previewPaused - || state == CameraState.stopped - || state == CameraState.notUsed) { - return; - } - - Runnable r = new Runnable() { - public void run() { - pausePreview(); - } - }; - stageActivity.post(r); - } - - @Override - public void resumePreviewAsync() { - if (state != CameraState.previewPaused || !wasRunning) { - return; - } - - Runnable r = new Runnable() { - public void run() { - resumePreview(); - } - }; - stageActivity.post(r); - } - - @Override - public boolean isReady() { - if (currentCamera != null) { - return true; - } - return false; - } - - public void updatePreview(CameraState newState) { - - synchronized (cameraChangeLock) { - if (state == CameraState.previewRunning - && newState != CameraState.prepare) { - stopPreviewAsync(); - } else if (state == CameraState.notUsed - && newState != CameraState.stopped) { - prepareCameraAsync(); - } - } - } - - public void changeCameraAsync() { - Runnable r = new Runnable() { - public void run() { - changeCamera(); - } - }; - stageActivity.post(r); - } - - public void changeCamera() { - stopPreview(); - prepareCamera(); - } - - public void setStageActivity(StageActivity stageActivity) { - this.stageActivity = stageActivity; - } - - public void destroyStage() { - if (this.stageActivity != null) { - stageActivity.finish(); - } - } - - public boolean isCameraFlashAvailable() { - return (hasBackCameraFlash() || hasFrontCameraFlash()); - } - - public void switchToCameraWithFlash() { - if (hasCurrentCameraFlash()) { - return; - } - - if (hasFrontCameraFlash()) { - updateCamera(frontCameraInformation); - } else if (hasBackCameraFlash()) { - updateCamera(backCameraInformation); - } - } -} diff --git a/catroid/src/main/java/org/catrobat/catroid/camera/CameraManager.kt b/catroid/src/main/java/org/catrobat/catroid/camera/CameraManager.kt new file mode 100644 index 00000000000..7068e3bf565 --- /dev/null +++ b/catroid/src/main/java/org/catrobat/catroid/camera/CameraManager.kt @@ -0,0 +1,279 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2018 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.camera + +import android.content.pm.PackageManager +import android.util.Log +import android.view.View +import android.view.ViewGroup +import androidx.camera.core.Camera +import androidx.camera.core.CameraSelector +import androidx.camera.core.ImageAnalysis +import androidx.camera.core.Preview +import androidx.camera.core.UseCase +import androidx.camera.lifecycle.ProcessCameraProvider +import androidx.camera.view.PreviewView +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.LifecycleRegistry +import org.catrobat.catroid.R +import org.catrobat.catroid.stage.StageActivity +import org.catrobat.catroid.utils.ToastUtil +import java.util.concurrent.CountDownLatch +import java.util.concurrent.Executors + +class CameraManager(private val stageActivity: StageActivity) : LifecycleOwner { + private val cameraProvider = ProcessCameraProvider.getInstance(stageActivity).get() + private val lifecycle = LifecycleRegistry(this) + private val previewView = PreviewView(stageActivity).also { + it.scaleType = PreviewView.ScaleType.FILL_CENTER + it.visibility = View.INVISIBLE + } + + private val previewUseCase = Preview.Builder().build() + private val analysisUseCase = ImageAnalysis.Builder().build() + + private var currentCamera: Camera? = null + private val defaultCameraSelector: CameraSelector + private var currentCameraSelector: CameraSelector + + val hasFrontCamera = cameraProvider.hasCamera(CameraSelector.DEFAULT_FRONT_CAMERA) + val hasBackCamera = cameraProvider.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA) + val hasFlash = stageActivity.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH) + + var previewVisible = false + private set + + var faceDetectionOn = false + private set + + var flashOn = false + private set + + companion object { + private val TAG = CameraManager::class.java.simpleName + } + + init { + if (hasFrontCamera || hasBackCamera) { + stageActivity.addContentView(previewView, ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + )) + } + + defaultCameraSelector = if (hasFrontCamera) { + CameraSelector.DEFAULT_FRONT_CAMERA + } else { + CameraSelector.DEFAULT_BACK_CAMERA + } + currentCameraSelector = defaultCameraSelector + lifecycle.currentState = Lifecycle.State.CREATED + } + + val isCameraFacingFront: Boolean + get() = currentCameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA + + val isCameraActive: Boolean + get() = lifecycle.currentState in listOf(Lifecycle.State.STARTED, Lifecycle.State.RESUMED) && + (cameraProvider.isBound(previewUseCase) || cameraProvider.isBound(analysisUseCase)) + + @Synchronized + fun reset() { + flashOn = false + previewVisible = false + faceDetectionOn = false + unbindPreview() + switchToDefaultCamera() + } + + @Synchronized + fun destroy() { + lifecycle.currentState = Lifecycle.State.DESTROYED + } + + @Synchronized + fun pause() { + lifecycle.currentState = Lifecycle.State.CREATED + } + + @Synchronized + fun resume() { + lifecycle.currentState = Lifecycle.State.RESUMED + currentCamera?.cameraControl?.enableTorch(flashOn) + } + + @Synchronized + fun switchToFrontCamera() { + if (hasFrontCamera) { + runInMainThreadAndWait(Runnable { switchCamera(CameraSelector.DEFAULT_FRONT_CAMERA) }) + } + } + + @Synchronized + fun switchToBackCamera() { + if (hasBackCamera) { + runInMainThreadAndWait(Runnable { switchCamera(CameraSelector.DEFAULT_BACK_CAMERA) }) + } + } + + private fun switchToDefaultCamera() = switchCamera(defaultCameraSelector) + + private fun switchCamera(cameraSelector: CameraSelector): Boolean { + if (currentCameraSelector != cameraSelector) { + currentCameraSelector = cameraSelector + currentCamera = null + + if (cameraProvider.isBound(previewUseCase)) { + bindPreview() + } + if (cameraProvider.isBound(analysisUseCase)) { + bindFaceDetector() + } + return true + } + return false + } + + @Synchronized + fun startPreview() { + if (previewVisible.not()) { + previewVisible = true + runInMainThreadAndWait(Runnable { + previewView.visibility = View.VISIBLE + if (cameraProvider.isBound(previewUseCase).not()) { + bindPreview() + } + }) + } + } + + @Synchronized + fun stopPreview() { + if (previewVisible) { + previewVisible = false + runInMainThreadAndWait(Runnable { + if (flashOn.not()) { + unbindPreview() + } + previewView.visibility = View.INVISIBLE + }) + } + } + + @Synchronized + fun enableFlash() { + if (flashOn.not()) { + flashOn = true + if (currentCamera?.cameraInfo?.hasFlashUnit()?.not() != false && isCameraFacingFront) { + switchToBackCamera() + } + if (cameraProvider.isBound(previewUseCase).not()) { + runInMainThreadAndWait(Runnable { bindPreview() }) + } else { + currentCamera?.cameraControl?.enableTorch(true) + } + } + } + + @Synchronized + fun disableFlash() { + if (flashOn) { + flashOn = false + currentCamera?.cameraControl?.enableTorch(false) + if (previewVisible.not()) { + runInMainThreadAndWait(Runnable { unbindPreview() }) + } + } + } + + @Synchronized + fun startFaceDetection(): Boolean { + if (faceDetectionOn.not()) { + faceDetectionOn = true + bindFaceDetector() + } + return true + } + + private fun bindPreview(): Boolean { + previewView.visibility = View.VISIBLE + return bindUseCase(previewUseCase).also { + previewUseCase.setSurfaceProvider(previewView.createSurfaceProvider()) + if (previewVisible.not()) { + previewView.visibility = View.INVISIBLE + } + } + } + + private fun unbindPreview() { + cameraProvider.unbind(previewUseCase) + if (cameraProvider.isBound(analysisUseCase).not()) { + currentCamera = null + } + } + + private fun bindFaceDetector() = bindUseCase(analysisUseCase).also { + analysisUseCase.setAnalyzer(Executors.newSingleThreadExecutor(), FaceDetector) + } + + private fun bindUseCase(useCase: UseCase): Boolean { + if (cameraProvider.isBound(useCase)) { + cameraProvider.unbind(useCase) + } + return try { + currentCamera = cameraProvider.bindToLifecycle( + this, + currentCameraSelector, + useCase + ) + currentCamera?.cameraControl?.enableTorch(flashOn) + lifecycle.currentState = Lifecycle.State.STARTED + true + } catch (exception: IllegalStateException) { + Log.e(TAG, "Could not bind use case.", exception) + handleError() + false + } catch (exception: IllegalArgumentException) { + Log.e(TAG, "No suitable camera found.", exception) + handleError() + false + } + } + + private fun runInMainThreadAndWait(runnable: Runnable) { + val executionLatch = CountDownLatch(1) + stageActivity.runOnUiThread { + runnable.run() + executionLatch.countDown() + } + executionLatch.await() + } + + private fun handleError() { + ToastUtil.showError(stageActivity, stageActivity.getString(R.string.camera_error_generic)) + destroy() + } + + override fun getLifecycle() = lifecycle +} diff --git a/catroid/src/main/java/org/catrobat/catroid/camera/FaceDetector.kt b/catroid/src/main/java/org/catrobat/catroid/camera/FaceDetector.kt new file mode 100644 index 00000000000..fe2e7e1a429 --- /dev/null +++ b/catroid/src/main/java/org/catrobat/catroid/camera/FaceDetector.kt @@ -0,0 +1,164 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2018 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.camera + +import android.graphics.Point +import android.os.Build +import android.util.Log +import androidx.annotation.VisibleForTesting +import androidx.camera.core.ExperimentalGetImage +import androidx.camera.core.ImageAnalysis +import androidx.camera.core.ImageProxy +import com.google.mlkit.vision.common.InputImage +import com.google.mlkit.vision.face.Face +import com.google.mlkit.vision.face.FaceDetection +import com.google.mlkit.vision.face.FaceDetectorOptions +import org.catrobat.catroid.CatroidApplication +import org.catrobat.catroid.ProjectManager +import org.catrobat.catroid.R +import org.catrobat.catroid.common.Constants.COORDINATE_TRANSFORMATION_OFFSET +import org.catrobat.catroid.common.ScreenValues.SCREEN_HEIGHT +import org.catrobat.catroid.common.ScreenValues.SCREEN_WIDTH +import org.catrobat.catroid.formulaeditor.SensorCustomEvent +import org.catrobat.catroid.formulaeditor.SensorCustomEventListener +import org.catrobat.catroid.formulaeditor.Sensors +import org.catrobat.catroid.stage.StageActivity +import kotlin.math.roundToInt + +object FaceDetector : ImageAnalysis.Analyzer { + private const val MAX_FACE_SIZE = 100 + private val sensorListeners = mutableSetOf() + private val detectionClient by lazy { + FaceDetection.getClient( + FaceDetectorOptions.Builder() + .setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL) + .enableTracking() + .build() + ) + } + + @JvmStatic + fun addListener(listener: SensorCustomEventListener) { + sensorListeners.add(listener) + } + + @JvmStatic + fun removeListener(listener: SensorCustomEventListener) { + sensorListeners.remove(listener) + } + + @ExperimentalGetImage + override fun analyze(imageProxy: ImageProxy) { + imageProxy.image?.let { mediaImage -> + val image = InputImage.fromMediaImage( + mediaImage, + imageProxy.imageInfo.rotationDegrees + ) + + detectionClient.process(image) + .addOnSuccessListener { faces -> + updateSensorValues(faces, mediaImage.width, mediaImage.height) + imageProxy.close() + } + .addOnFailureListener { e -> + updateDetectionStatus(false) + val context = CatroidApplication.getAppContext() + StageActivity.messageHandler.obtainMessage( + StageActivity.SHOW_TOAST, + arrayListOf(context.getString(R.string.camera_error_face_detection)) + ).sendToTarget() + Log.e(javaClass.simpleName, "Could not analyze image.", e) + } + } + } + + private fun updateSensorValues(faces: List, imageWidth: Int, imageHeight: Int) { + val detected = faces.isNotEmpty() + + updateDetectionStatus(detected) + if (detected) { + translateFaceToSensorValues(faces[0], imageWidth, imageHeight) + } + } + + private fun translateFaceToSensorValues(face: Face, imageWidth: Int, imageHeight: Int) { + val frontCamera = StageActivity.getActiveCameraManager().isCameraFacingFront + val oldAPI = Build.VERSION.SDK_INT < Build.VERSION_CODES.M + val aspectRatio = imageWidth.toFloat() / imageHeight + val faceBounds = face.boundingBox + + val facePosition = if (ProjectManager.getInstance().isCurrentProjectLandscapeMode) { + val relativeX = faceBounds.exactCenterY() / imageHeight + val relativeY = faceBounds.exactCenterX() / imageWidth + coordinatesFromRelativePosition( + if (oldAPI) relativeX else 1 - relativeX, + SCREEN_WIDTH / aspectRatio, + if (frontCamera) relativeY else 1 - relativeY, + SCREEN_WIDTH.toFloat() + ) + } else { + val relativeX = faceBounds.exactCenterX() / imageHeight + coordinatesFromRelativePosition( + if (frontCamera.xor(oldAPI)) 1 - relativeX else relativeX, + SCREEN_HEIGHT / aspectRatio, + 1 - faceBounds.exactCenterY() / imageWidth, + SCREEN_HEIGHT.toFloat() + ) + } + + val relativeFaceSize = (faceBounds.height().toFloat() / imageHeight).coerceAtMost(1f) + val faceSize = (MAX_FACE_SIZE * relativeFaceSize).roundToInt() + onFaceDetected(facePosition, faceSize) + } + + private fun coordinatesFromRelativePosition( + relativeX: Float, + width: Float, + relativeY: Float, + height: Float + ) = Point( + (width * (relativeX - COORDINATE_TRANSFORMATION_OFFSET)).roundToInt(), + (height * (relativeY - COORDINATE_TRANSFORMATION_OFFSET)).roundToInt() + ) + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + fun updateDetectionStatus(faceDetected: Boolean) { + val sensorValue = if (faceDetected) 1f else 0f + sensorListeners.forEach { + it.onCustomSensorChanged( + SensorCustomEvent(Sensors.FACE_DETECTED, floatArrayOf(sensorValue))) + } + } + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + fun onFaceDetected(position: Point, size: Int) { + sensorListeners.forEach { + it.onCustomSensorChanged( + SensorCustomEvent(Sensors.FACE_X_POSITION, floatArrayOf(position.x.toFloat()))) + it.onCustomSensorChanged( + SensorCustomEvent(Sensors.FACE_Y_POSITION, floatArrayOf(position.y.toFloat()))) + it.onCustomSensorChanged( + SensorCustomEvent(Sensors.FACE_SIZE, floatArrayOf(size.toFloat()))) + } + } +} diff --git a/catroid/src/main/java/org/catrobat/catroid/common/Constants.java b/catroid/src/main/java/org/catrobat/catroid/common/Constants.java index 006b880129d..fc48ba77963 100644 --- a/catroid/src/main/java/org/catrobat/catroid/common/Constants.java +++ b/catroid/src/main/java/org/catrobat/catroid/common/Constants.java @@ -39,7 +39,7 @@ public final class Constants { - public static final float CURRENT_CATROBAT_LANGUAGE_VERSION = 0.999997f; + public static final float CURRENT_CATROBAT_LANGUAGE_VERSION = 0.999999f; public static final String REMOTE_DISPLAY_APP_ID = "CEBB9229"; public static final int CAST_CONNECTION_TIMEOUT = 5000; //in milliseconds @@ -75,6 +75,8 @@ public final class Constants { public static final String IMAGE_DIRECTORY_NAME = "images"; public static final String SOUND_DIRECTORY_NAME = "sounds"; + public static final String SCREENSHOT_AUTOMATIC_FILE_NAME = "automatic_screenshot" + DEFAULT_IMAGE_EXTENSION; + public static final String SCREENSHOT_MANUAL_FILE_NAME = "manual_screenshot" + DEFAULT_IMAGE_EXTENSION; // Backpack Directories public static final String BACKPACK_DIRECTORY_NAME = "backpack"; @@ -246,6 +248,7 @@ public final class Constants { public static final int BUFFER_8K = 8 * 1024; public static final String PREF_PROJECTNAME_KEY = "projectName"; public static final int MAX_FILE_NAME_LENGTH = 127; + public static final double COORDINATE_TRANSFORMATION_OFFSET = 0.5; public static final String COLLISION_PNG_META_TAG_KEY = "CollisionPolygonVertices"; public static final int COLLISION_VERTEX_LIMIT = 100; diff --git a/catroid/src/main/java/org/catrobat/catroid/common/DefaultProjectHandler.java b/catroid/src/main/java/org/catrobat/catroid/common/DefaultProjectHandler.java index 18ec1dcd5cf..df18d57cd5a 100644 --- a/catroid/src/main/java/org/catrobat/catroid/common/DefaultProjectHandler.java +++ b/catroid/src/main/java/org/catrobat/catroid/common/DefaultProjectHandler.java @@ -27,7 +27,7 @@ import org.catrobat.catroid.BuildConfig; import org.catrobat.catroid.common.defaultprojectcreators.ArDroneProjectCreator; import org.catrobat.catroid.common.defaultprojectcreators.ChromeCastProjectCreator; -import org.catrobat.catroid.common.defaultprojectcreators.DefaultProjectCreator; +import org.catrobat.catroid.common.defaultprojectcreators.DefaultExampleProject; import org.catrobat.catroid.common.defaultprojectcreators.JumpingSumoProjectCreator; import org.catrobat.catroid.common.defaultprojectcreators.ProjectCreator; import org.catrobat.catroid.content.Project; @@ -84,20 +84,20 @@ public static Project createAndSaveEmptyProject(String name, Context context, bo public void setDefaultProjectCreator(ProjectCreatorType type) { switch (type) { case PROJECT_CREATOR_DEFAULT: - defaultProjectCreator = new DefaultProjectCreator(); + defaultProjectCreator = new DefaultExampleProject(); break; case PROJECT_CREATOR_DRONE: if (BuildConfig.FEATURE_PARROT_AR_DRONE_ENABLED) { defaultProjectCreator = new ArDroneProjectCreator(); } else { - defaultProjectCreator = new DefaultProjectCreator(); + defaultProjectCreator = new DefaultExampleProject(); } break; case PROJECT_CREATOR_JUMPING_SUMO: if (BuildConfig.FEATURE_PARROT_JUMPING_SUMO_ENABLED) { defaultProjectCreator = new JumpingSumoProjectCreator(); } else { - defaultProjectCreator = new DefaultProjectCreator(); + defaultProjectCreator = new DefaultExampleProject(); } break; case PROJECT_CREATOR_CAST: diff --git a/catroid/src/main/java/org/catrobat/catroid/common/ParameterizedData.kt b/catroid/src/main/java/org/catrobat/catroid/common/ParameterizedData.kt new file mode 100644 index 00000000000..d4ab4ca2da4 --- /dev/null +++ b/catroid/src/main/java/org/catrobat/catroid/common/ParameterizedData.kt @@ -0,0 +1,40 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2020 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.common + +class ParameterizedData( + var currentPosition: Int = 0, + var listSize: Int = 1, + var failMessages: String = "", + var successMessages: String = "", + var currentParameters: String = "" +) { + fun reset() { + currentPosition = 0 + listSize = 1 + failMessages = "" + successMessages = "" + currentParameters = "" + } +} diff --git a/catroid/src/main/java/org/catrobat/catroid/common/defaultprojectcreators/ChromeCastProjectCreator.java b/catroid/src/main/java/org/catrobat/catroid/common/defaultprojectcreators/ChromeCastProjectCreator.java index 6b3c87d7e2e..8625ba43ddb 100644 --- a/catroid/src/main/java/org/catrobat/catroid/common/defaultprojectcreators/ChromeCastProjectCreator.java +++ b/catroid/src/main/java/org/catrobat/catroid/common/defaultprojectcreators/ChromeCastProjectCreator.java @@ -69,7 +69,7 @@ import static org.catrobat.catroid.formulaeditor.FormulaElement.ElementType.OPERATOR; import static org.catrobat.catroid.formulaeditor.FormulaElement.ElementType.SENSOR; import static org.catrobat.catroid.formulaeditor.FormulaElement.ElementType.USER_VARIABLE; -import static org.catrobat.catroid.stage.StageListener.SCREENSHOT_AUTOMATIC_FILE_NAME; +import static org.catrobat.catroid.common.Constants.SCREENSHOT_AUTOMATIC_FILE_NAME; public class ChromeCastProjectCreator extends ProjectCreator { diff --git a/catroid/src/main/java/org/catrobat/catroid/common/defaultprojectcreators/DefaultProjectCreator.java b/catroid/src/main/java/org/catrobat/catroid/common/defaultprojectcreators/DefaultProjectCreator.java index 04e477d8109..a685ef8e38b 100644 --- a/catroid/src/main/java/org/catrobat/catroid/common/defaultprojectcreators/DefaultProjectCreator.java +++ b/catroid/src/main/java/org/catrobat/catroid/common/defaultprojectcreators/DefaultProjectCreator.java @@ -62,7 +62,7 @@ import static org.catrobat.catroid.formulaeditor.FormulaElement.ElementType.FUNCTION; import static org.catrobat.catroid.formulaeditor.FormulaElement.ElementType.NUMBER; import static org.catrobat.catroid.formulaeditor.FormulaElement.ElementType.OPERATOR; -import static org.catrobat.catroid.stage.StageListener.SCREENSHOT_AUTOMATIC_FILE_NAME; +import static org.catrobat.catroid.common.Constants.SCREENSHOT_AUTOMATIC_FILE_NAME; public class DefaultProjectCreator extends ProjectCreator { diff --git a/catroid/src/main/java/org/catrobat/catroid/content/ActionFactory.java b/catroid/src/main/java/org/catrobat/catroid/content/ActionFactory.java index 00f5ede2b0a..eb3f9d916f4 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/ActionFactory.java +++ b/catroid/src/main/java/org/catrobat/catroid/content/ActionFactory.java @@ -28,9 +28,9 @@ import com.parrot.freeflight.drone.DroneProxy.ARDRONE_LED_ANIMATION; import org.catrobat.catroid.ProjectManager; -import org.catrobat.catroid.camera.CameraManager; import org.catrobat.catroid.common.BrickValues; import org.catrobat.catroid.common.LookData; +import org.catrobat.catroid.common.ParameterizedData; import org.catrobat.catroid.common.SoundInfo; import org.catrobat.catroid.content.actions.AddItemToUserListAction; import org.catrobat.catroid.content.actions.ArduinoSendDigitalValueAction; @@ -107,6 +107,7 @@ import org.catrobat.catroid.content.actions.MoveNStepsAction; import org.catrobat.catroid.content.actions.NextLookAction; import org.catrobat.catroid.content.actions.NotifyEventWaiterAction; +import org.catrobat.catroid.content.actions.ParameterizedAssertAction; import org.catrobat.catroid.content.actions.PenDownAction; import org.catrobat.catroid.content.actions.PenUpAction; import org.catrobat.catroid.content.actions.PhiroMotorMoveBackwardAction; @@ -126,6 +127,7 @@ import org.catrobat.catroid.content.actions.ReadVariableFromDeviceAction; import org.catrobat.catroid.content.actions.ReadVariableFromFileAction; import org.catrobat.catroid.content.actions.RepeatAction; +import org.catrobat.catroid.content.actions.RepeatParameterizedAction; import org.catrobat.catroid.content.actions.RepeatUntilAction; import org.catrobat.catroid.content.actions.ReplaceItemInUserListAction; import org.catrobat.catroid.content.actions.RunningStitchAction; @@ -201,6 +203,10 @@ import org.catrobat.catroid.formulaeditor.UserVariable; import org.catrobat.catroid.physics.PhysicsObject; +import java.util.List; + +import kotlin.Pair; + public class ActionFactory extends Actions { public EventAction createBroadcastAction(String broadcastMessage, @EventWrapper.WaitMode int waitMode) { @@ -1181,15 +1187,9 @@ public Action createHideVariableAction(Sprite sprite, UserVariable userVariable) return action; } - public Action createTurnFlashOnAction() { - FlashAction action = action(FlashAction.class); - action.turnFlashOn(); - return action; - } - - public Action createTurnFlashOffAction() { + public Action createFlashAction(boolean flashOn) { FlashAction action = action(FlashAction.class); - action.turnFlashOff(); + action.setFlashOn(flashOn); return action; } @@ -1200,9 +1200,9 @@ public Action createVibrateAction(Sprite sprite, Formula duration) { return action; } - public Action createUpdateCameraPreviewAction(CameraManager.CameraState state) { + public Action createUpdateCameraPreviewAction(boolean turnOn) { CameraBrickAction action = action(CameraBrickAction.class); - action.setCameraAction(state); + action.setActive(turnOn); return action; } @@ -1297,8 +1297,8 @@ public Action createSetNfcTagAction(Sprite sprite, Formula nfcNdefMessage, int n public Action createAssertEqualsAction(Sprite sprite, Formula actual, Formula expected, String position) { AssertEqualsAction action = action(AssertEqualsAction.class); - action.setActual(actual); - action.setExpected(expected); + action.setActualFormula(actual); + action.setExpectedFormula(expected); action.setSprite(sprite); action.setPosition(position); @@ -1309,8 +1309,35 @@ public Action createAssertEqualsAction(Sprite sprite, Formula actual, Formula ex public Action createAssertUserListsAction(Sprite sprite, UserList actual, UserList expected, String position) { AssertUserListAction action = action(AssertUserListAction.class); - action.setActual(actual); - action.setExpected(expected); + action.setActualUserList(actual); + action.setExpectedUserList(expected); + + action.setSprite(sprite); + action.setPosition(position); + + return action; + } + + public Action createRepeatParameterizedAction(Sprite sprite, ParameterizedData data, + List> parameters, + String position, Action repeatedAction) { + RepeatParameterizedAction action = action(RepeatParameterizedAction.class); + action.setParameterizedData(data); + action.setParameters(parameters); + action.setAction(repeatedAction); + + action.setSprite(sprite); + action.setPosition(position); + + return action; + } + + public Action createParameterizedAssertAction(Sprite sprite, Formula actual, UserList expected, + ParameterizedData data, String position) { + ParameterizedAssertAction action = action(ParameterizedAssertAction.class); + action.setActualFormula(actual); + action.setExpectedList(expected); + action.setParameterizedData(data); action.setSprite(sprite); action.setPosition(position); @@ -1318,8 +1345,9 @@ public Action createAssertUserListsAction(Sprite sprite, UserList actual, UserLi return action; } - public Action createFinishStageAction() { + public Action createFinishStageAction(boolean silent) { FinishStageAction action = action(FinishStageAction.class); + action.setSilent(silent); return action; } diff --git a/catroid/src/main/java/org/catrobat/catroid/content/Project.java b/catroid/src/main/java/org/catrobat/catroid/content/Project.java index 4d3a9ef3918..192f6b70e18 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/Project.java +++ b/catroid/src/main/java/org/catrobat/catroid/content/Project.java @@ -34,6 +34,7 @@ import org.catrobat.catroid.common.ScreenModes; import org.catrobat.catroid.common.ScreenValues; import org.catrobat.catroid.content.bricks.Brick; +import org.catrobat.catroid.formulaeditor.UserData; import org.catrobat.catroid.formulaeditor.UserList; import org.catrobat.catroid.formulaeditor.UserVariable; import org.catrobat.catroid.io.XStreamFieldKeyOrder; @@ -382,4 +383,16 @@ public BroadcastMessageContainer getBroadcastMessageContainer() { public void setXmlHeader(XmlHeader xmlHeader) { this.xmlHeader = xmlHeader; } + + public void updateUserDataReferences(String oldName, String newName, UserData item) { + for (Scene scene : sceneList) { + scene.updateUserDataReferences(oldName, newName, item); + } + } + + public void deselectElements(List> elements) { + for (Scene scene : sceneList) { + scene.deselectElements(elements); + } + } } diff --git a/catroid/src/main/java/org/catrobat/catroid/content/Scene.java b/catroid/src/main/java/org/catrobat/catroid/content/Scene.java index cf27e547528..2c1ee7b6f68 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/Scene.java +++ b/catroid/src/main/java/org/catrobat/catroid/content/Scene.java @@ -27,6 +27,7 @@ import org.catrobat.catroid.common.Nameable; import org.catrobat.catroid.content.bricks.Brick; import org.catrobat.catroid.content.bricks.BroadcastMessageBrick; +import org.catrobat.catroid.formulaeditor.UserData; import org.catrobat.catroid.io.XStreamFieldKeyOrder; import org.catrobat.catroid.physics.PhysicsWorld; import org.catrobat.catroid.ui.controller.BackpackListManager; @@ -169,4 +170,16 @@ public Set getBroadcastMessagesInUse() { } return messagesInUse; } + + public void updateUserDataReferences(String oldName, String newName, UserData item) { + for (Sprite sprite : spriteList) { + sprite.updateUserDataReferences(oldName, newName, item); + } + } + + public void deselectElements(List> elements) { + for (Sprite sprite : spriteList) { + sprite.deselectElements(elements); + } + } } diff --git a/catroid/src/main/java/org/catrobat/catroid/content/Script.java b/catroid/src/main/java/org/catrobat/catroid/content/Script.java index 38889b4ca97..dfe27631a08 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/Script.java +++ b/catroid/src/main/java/org/catrobat/catroid/content/Script.java @@ -24,8 +24,13 @@ import org.catrobat.catroid.content.actions.ScriptSequenceAction; import org.catrobat.catroid.content.bricks.Brick; +import org.catrobat.catroid.content.bricks.CompositeBrick; +import org.catrobat.catroid.content.bricks.FormulaBrick; +import org.catrobat.catroid.content.bricks.ListSelectorBrick; import org.catrobat.catroid.content.bricks.ScriptBrick; +import org.catrobat.catroid.content.bricks.UserDefinedBrick; import org.catrobat.catroid.content.eventids.EventId; +import org.catrobat.catroid.formulaeditor.UserData; import java.io.Serializable; import java.util.ArrayList; @@ -128,6 +133,22 @@ public boolean removeBrick(Brick brick) { return false; } + public void removeAllOccurrencesOfUserDefinedBrick(List brickList, UserDefinedBrick userDefinedBrick) { + for (int brickIndex = 0; brickIndex < brickList.size(); brickIndex++) { + Brick currentBrick = brickList.get(brickIndex); + if (currentBrick instanceof CompositeBrick) { + CompositeBrick currentCompositeBrick = (CompositeBrick) currentBrick; + removeAllOccurrencesOfUserDefinedBrick(currentCompositeBrick.getNestedBricks(), userDefinedBrick); + if (currentCompositeBrick.hasSecondaryList()) { + removeAllOccurrencesOfUserDefinedBrick(currentCompositeBrick.getSecondaryNestedBricks(), userDefinedBrick); + } + } + if (currentBrick instanceof UserDefinedBrick && userDefinedBrick.isUserDefinedBrickDataEqual(currentBrick)) { + brickList.remove(brickIndex--); + } + } + } + public void addRequiredResources(final Brick.ResourcesSet resourcesSet) { for (Brick brick : brickList) { if (!brick.isCommentedOut()) { @@ -135,4 +156,34 @@ public void addRequiredResources(final Brick.ResourcesSet resourcesSet) { } } } + + public void updateUserDataReferences(String oldName, String newName, UserData item) { + List flatList = new ArrayList<>(); + addToFlatList(flatList); + boolean containedInListSelector = false; + + for (Brick brick : flatList) { + if (brick instanceof ListSelectorBrick) { + containedInListSelector = true; + break; + } + } + + for (Brick brick : flatList) { + if (brick instanceof FormulaBrick) { + ((FormulaBrick) brick).updateUserDataReference(oldName, newName, item, + containedInListSelector); + } + } + } + + public void deselectElements(List> elements) { + List flatList = new ArrayList<>(); + addToFlatList(flatList); + for (Brick brick : flatList) { + if (brick instanceof ListSelectorBrick) { + ((ListSelectorBrick) brick).deselectElements(elements); + } + } + } } diff --git a/catroid/src/main/java/org/catrobat/catroid/content/Sprite.java b/catroid/src/main/java/org/catrobat/catroid/content/Sprite.java index 7c97d9b6e23..a1c5f487267 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/Sprite.java +++ b/catroid/src/main/java/org/catrobat/catroid/content/Sprite.java @@ -47,6 +47,7 @@ import org.catrobat.catroid.content.eventids.EventId; import org.catrobat.catroid.embroidery.RunningStitch; import org.catrobat.catroid.formulaeditor.Formula; +import org.catrobat.catroid.formulaeditor.UserData; import org.catrobat.catroid.formulaeditor.UserList; import org.catrobat.catroid.formulaeditor.UserVariable; import org.catrobat.catroid.io.XStreamFieldKeyOrder; @@ -162,7 +163,7 @@ public UserDefinedBrick getUserDefinedBrickByID(UUID userDefinedBrickID) { return null; } - private boolean containsUserDefinedBrickWithSameUserData(UserDefinedBrick userDefinedBrick) { + public boolean containsUserDefinedBrickWithSameUserData(UserDefinedBrick userDefinedBrick) { return getUserDefinedBrickWithSameUserData(userDefinedBrick) != null; } @@ -170,6 +171,13 @@ public void addUserDefinedBrick(UserDefinedBrick userDefinedBrick) { userDefinedBrickList.add(userDefinedBrick); } + public void removeUserDefinedBrick(UserDefinedBrick userDefinedBrick) { + for (Script script : scriptList) { + script.removeAllOccurrencesOfUserDefinedBrick(script.brickList, userDefinedBrick); + } + userDefinedBrickList.remove(userDefinedBrick); + } + public void addClonesOfUserDefinedBrickList(List userDefinedBricks) { for (UserDefinedBrick userDefinedBrick : userDefinedBricks) { if (!containsUserDefinedBrickWithSameUserData(userDefinedBrick)) { @@ -487,8 +495,8 @@ public void createCollisionPolygons() { } } - public static boolean doesUserBrickAlreadyExist(UserDefinedBrick userDefinedBrick, Sprite sprite) { - for (Brick alreadyDefinedBrick : sprite.getUserDefinedBrickList()) { + public boolean doesUserBrickAlreadyExist(UserDefinedBrick userDefinedBrick) { + for (Brick alreadyDefinedBrick : getUserDefinedBrickList()) { if (((UserDefinedBrick) alreadyDefinedBrick).isUserDefinedBrickDataEqual(userDefinedBrick)) { return true; } @@ -496,6 +504,18 @@ public static boolean doesUserBrickAlreadyExist(UserDefinedBrick userDefinedBric return false; } + public void updateUserDataReferences(String oldName, String newName, UserData item) { + for (Script script : scriptList) { + script.updateUserDataReferences(oldName, newName, item); + } + } + + public void deselectElements(List> elements) { + for (Script script : scriptList) { + script.deselectElements(elements); + } + } + public class PenConfiguration { public boolean penDown = false; public double penSize = BrickValues.PEN_SIZE; diff --git a/catroid/src/main/java/org/catrobat/catroid/content/actions/AssertAction.java b/catroid/src/main/java/org/catrobat/catroid/content/actions/AssertAction.java deleted file mode 100644 index 476d39fafcf..00000000000 --- a/catroid/src/main/java/org/catrobat/catroid/content/actions/AssertAction.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2018 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.content.actions; - -import com.badlogic.gdx.scenes.scene2d.Action; - -import org.catrobat.catroid.content.Sprite; -import org.catrobat.catroid.stage.StageActivity; -import org.catrobat.catroid.stage.TestResult; - -import static org.catrobat.catroid.stage.TestResult.STAGE_ACTIVITY_TEST_FAIL; - -public abstract class AssertAction extends Action { - - protected String position; - protected Sprite sprite; - protected String assertTitle = "\nAssertEqualsError\n"; - - protected void failWith(String message) { - StageActivity.finishTestWithResult( - new TestResult(formattedPosition() - + assertTitle + message, STAGE_ACTIVITY_TEST_FAIL)); - } - - protected boolean equalValues(String actual, String expected) { - try { - return actual.equals(expected) || Double.parseDouble(actual) == Double.parseDouble(expected); - } catch (NumberFormatException numberFormatException) { - return false; - } - } - - protected int indexOfDifference(CharSequence actual, CharSequence expected) { - if (actual == null || expected == null) { - return 0; - } - int position; - for (position = 0; position < actual.length() && position < expected.length(); ++position) { - if (actual.charAt(position) != expected.charAt(position)) { - break; - } - } - if (position < expected.length() || position < actual.length()) { - return position; - } - return 0; - } - - protected String generateIndicator(Object actual, Object expected) { - int errorPosition = indexOfDifference(actual.toString(), expected.toString()); - - return String.valueOf(new char[errorPosition]).replace('\0', '-') - + "^"; - } - - private String formattedPosition() { - return "on sprite \"" + sprite.getName() + "\"\n" + position; - } - - public void setSprite(Sprite sprite) { - this.sprite = sprite; - } - - public void setPosition(String position) { - this.position = position; - } -} diff --git a/catroid/src/main/java/org/catrobat/catroid/content/actions/AssertAction.kt b/catroid/src/main/java/org/catrobat/catroid/content/actions/AssertAction.kt new file mode 100644 index 00000000000..7373f496a50 --- /dev/null +++ b/catroid/src/main/java/org/catrobat/catroid/content/actions/AssertAction.kt @@ -0,0 +1,79 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2018 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.content.actions + +import com.badlogic.gdx.scenes.scene2d.Action +import org.catrobat.catroid.content.Sprite +import org.catrobat.catroid.stage.StageActivity +import org.catrobat.catroid.stage.TestResult + +abstract class AssertAction : Action() { + var position: String? = null + var sprite: Sprite? = null + var assertTitle = "\nAssertError\n" + + protected fun failWith(message: String) { + StageActivity.finishTestWithResult( + TestResult( + formattedPosition() + + assertTitle + message, + TestResult.STAGE_ACTIVITY_TEST_FAIL + ) + ) + } + + protected fun equalValues(actual: String, expected: String): Boolean { + return try { + actual == expected || actual.toDouble() == expected.toDouble() + } catch (numberFormatException: NumberFormatException) { + false + } + } + + private fun indexOfDifference( + actual: CharSequence?, + expected: CharSequence? + ): Int { + if (actual == null || expected == null) { + return 0 + } + var position = 0 + while (position < actual.length && position < expected.length) { + if (actual[position] != expected[position]) { + break + } + ++position + } + return position + } + + protected fun generateIndicator(actual: Any, expected: Any): String { + val errorPosition = indexOfDifference(actual.toString(), expected.toString()) + return String(CharArray(errorPosition)).replace('\u0000', '-') + "^" + } + + private fun formattedPosition(): String { + return "on sprite \"${sprite?.name}\"\n" + + "$position\n" + } +} diff --git a/catroid/src/main/java/org/catrobat/catroid/content/actions/AssertEqualsAction.java b/catroid/src/main/java/org/catrobat/catroid/content/actions/AssertEqualsAction.java deleted file mode 100644 index f7c2f590f80..00000000000 --- a/catroid/src/main/java/org/catrobat/catroid/content/actions/AssertEqualsAction.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2018 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.content.actions; - -import org.catrobat.catroid.formulaeditor.Formula; - -public class AssertEqualsAction extends AssertAction { - - private Formula actualFormula = null; - private Formula expectedFormula = null; - - @Override - public boolean act(float delta) { - assertTitle = "\nAssertEqualsError\n"; - - if (actualFormula == null) { - failWith("Actual is null"); - return false; - } - if (expectedFormula == null) { - failWith("Expected is null"); - return false; - } - - String actualValue = actualFormula.interpretObject(sprite).toString(); - String expectedValue = expectedFormula.interpretObject(sprite).toString(); - - if (!equalValues(actualValue, expectedValue)) { - failWith(formattedAssertEqualsError(actualValue, expectedValue)); - return false; - } - return true; - } - - private String formattedAssertEqualsError(Object actual, Object expected) { - String indicator = generateIndicator(actual, expected); - return "expected: <" + expected + ">\nactual: <" + actual + ">\ndeviation: " + indicator; - } - - public void setActual(Formula actual) { - this.actualFormula = actual; - } - - public void setExpected(Formula expected) { - this.expectedFormula = expected; - } - - public void setPosition(String position) { - this.position = position; - } -} diff --git a/catroid/src/main/java/org/catrobat/catroid/content/actions/AssertEqualsAction.kt b/catroid/src/main/java/org/catrobat/catroid/content/actions/AssertEqualsAction.kt new file mode 100644 index 00000000000..960b18fff5e --- /dev/null +++ b/catroid/src/main/java/org/catrobat/catroid/content/actions/AssertEqualsAction.kt @@ -0,0 +1,57 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2018 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.content.actions + +import org.catrobat.catroid.formulaeditor.Formula + +class AssertEqualsAction : AssertAction() { + var actualFormula: Formula? = null + var expectedFormula: Formula? = null + + override fun act(delta: Float): Boolean { + assertTitle = "\nAssertEqualsError\n" + if (actualFormula == null) { + failWith("Actual is null") + return false + } + if (expectedFormula == null) { + failWith("Expected is null") + return false + } + val actualValue = actualFormula!!.interpretObject(sprite).toString() + val expectedValue = expectedFormula!!.interpretObject(sprite).toString() + if (!equalValues(actualValue, expectedValue)) { + failWith(formattedAssertEqualsError(actualValue, expectedValue)) + return false + } + return true + } + + private fun formattedAssertEqualsError( + actual: Any, + expected: Any + ): String { + val indicator = generateIndicator(actual, expected) + return "expected: <$expected>\nactual: <$actual>\ndeviation: $indicator" + } +} diff --git a/catroid/src/main/java/org/catrobat/catroid/content/actions/AssertUserListAction.java b/catroid/src/main/java/org/catrobat/catroid/content/actions/AssertUserListAction.java deleted file mode 100644 index 31e1ce2d192..00000000000 --- a/catroid/src/main/java/org/catrobat/catroid/content/actions/AssertUserListAction.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2018 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.content.actions; - -import org.catrobat.catroid.formulaeditor.UserList; - -import java.util.List; - -public class AssertUserListAction extends AssertAction { - - private UserList actualUserList = null; - private UserList expectedUserList = null; - - @Override - public boolean act(float delta) { - assertTitle = "\nAssertUserListError\n"; - String message = ""; - - if (actualUserList == null) { - failWith("Actual list is null"); - return false; - } - if (expectedUserList == null) { - failWith("Expected list is null"); - return false; - } - - List actualList = actualUserList.getValue(); - List expectedList = expectedUserList.getValue(); - if (actualList.size() != expectedList.size()) { - message = "The number of list elements are not equal\nexpected: " + expectedList.size() - + " element/s\nactual: " + actualList.size() + " element/s\n\n"; - } - - for (int listPosition = 0; listPosition < expectedList.size(); listPosition++) { - try { - if (!equalValues(actualList.get(listPosition).toString(), - expectedList.get(listPosition).toString())) { - message = message.concat(formattedAssertError(listPosition, - actualList.get(listPosition), - expectedList.get(listPosition))); - } - } catch (IndexOutOfBoundsException e) { - break; - } - } - - if (message.isEmpty()) { - return true; - } else { - failWith(message); - return false; - } - } - - private String formattedAssertError(int listPosition, Object actual, Object expected) { - String indicator = generateIndicator(actual, expected); - return "position: " + listPosition + "\nexpected: <" + expected + ">\nactual: <" - + actual + ">\ndeviation: " + indicator + "\n\n"; - } - - public void setActual(UserList actual) { - this.actualUserList = actual; - } - - public void setExpected(UserList expected) { - this.expectedUserList = expected; - } -} diff --git a/catroid/src/main/java/org/catrobat/catroid/content/actions/AssertUserListAction.kt b/catroid/src/main/java/org/catrobat/catroid/content/actions/AssertUserListAction.kt new file mode 100644 index 00000000000..15c130a1b9d --- /dev/null +++ b/catroid/src/main/java/org/catrobat/catroid/content/actions/AssertUserListAction.kt @@ -0,0 +1,89 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2018 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.content.actions + +import org.catrobat.catroid.formulaeditor.UserList + +class AssertUserListAction : AssertAction() { + var actualUserList: UserList? = null + var expectedUserList: UserList? = null + var message: String = "" + + override fun act(delta: Float): Boolean { + assertTitle = "\nAssertUserListError\n" + message = "" + + if (actualUserList == null) { + failWith("Actual list is null") + return false + } + if (expectedUserList == null) { + failWith("Expected list is null") + return false + } + + validateLists() + + return if (message.isEmpty()) { + true + } else { + failWith(message) + false + } + } + + private fun validateLists() { + val actualList = actualUserList!!.value + val expectedList = expectedUserList!!.value + if (actualList.size != expectedList.size) { + message = "The number of list elements are not equal\n" + + "expected: ${expectedList.size} element/s\n" + + "actual: ${actualList.size} element/s\n" + } + for (listPosition in 0..minOf(expectedList.size - 1, actualList.size - 1)) { + if (!equalValues( + actualList[listPosition].toString(), + expectedList[listPosition].toString() + ) + ) { + message += formattedAssertError( + listPosition, + actualList[listPosition], + expectedList[listPosition] + ) + } + } + } + + private fun formattedAssertError( + listPosition: Int, + actual: Any, + expected: Any + ): String { + val indicator = generateIndicator(actual, expected) + return "position: $listPosition\n" + + "expected: <$expected>\n" + + "actual: <$actual>\n" + + "deviation: $indicator\n\n" + } +} diff --git a/catroid/src/main/java/org/catrobat/catroid/stage/DeviceCameraControl.java b/catroid/src/main/java/org/catrobat/catroid/content/actions/CameraBrickAction.kt similarity index 68% rename from catroid/src/main/java/org/catrobat/catroid/stage/DeviceCameraControl.java rename to catroid/src/main/java/org/catrobat/catroid/content/actions/CameraBrickAction.kt index ab2b10e500f..5f1adc9ec37 100644 --- a/catroid/src/main/java/org/catrobat/catroid/stage/DeviceCameraControl.java +++ b/catroid/src/main/java/org/catrobat/catroid/content/actions/CameraBrickAction.kt @@ -20,32 +20,19 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ +package org.catrobat.catroid.content.actions -package org.catrobat.catroid.stage; +import com.badlogic.gdx.scenes.scene2d.actions.TemporalAction +import org.catrobat.catroid.stage.StageActivity -public interface DeviceCameraControl { +class CameraBrickAction : TemporalAction() { + var active = false - // Synchronous interface - void prepareCamera(); - - void stopPreview(); - - void pausePreview(); - - void resumePreview(); - - void changeCamera(); - - // Asynchronous interface - need when called from a non platform thread (GDX OpenGl thread) - void prepareCameraAsync(); - - void stopPreviewAsync(); - - void pausePreviewAsync(); - - void resumePreviewAsync(); - - void changeCameraAsync(); - - boolean isReady(); + override fun update(percent: Float) { + if (active) { + StageActivity.getActiveCameraManager()?.startPreview() + } else { + StageActivity.getActiveCameraManager()?.stopPreview() + } + } } diff --git a/catroid/src/main/java/org/catrobat/catroid/content/actions/ChooseCameraAction.java b/catroid/src/main/java/org/catrobat/catroid/content/actions/ChooseCameraAction.kt similarity index 58% rename from catroid/src/main/java/org/catrobat/catroid/content/actions/ChooseCameraAction.java rename to catroid/src/main/java/org/catrobat/catroid/content/actions/ChooseCameraAction.kt index cdd50134c0b..5ff61d179e3 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/actions/ChooseCameraAction.java +++ b/catroid/src/main/java/org/catrobat/catroid/content/actions/ChooseCameraAction.kt @@ -20,33 +20,33 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - -package org.catrobat.catroid.content.actions; - -import com.badlogic.gdx.scenes.scene2d.actions.TemporalAction; - -import org.catrobat.catroid.camera.CameraManager; - -public class ChooseCameraAction extends TemporalAction { - - private static final int BACK = 0; - private static final int FRONT = 1; - private int cameraFacing = FRONT; - - @Override - protected void update(float percent) { - if (cameraFacing == FRONT) { - CameraManager.getInstance().setToFrontCamera(); - } else { - CameraManager.getInstance().setToBackCamera(); - } - } - - public void setFrontCamera() { - cameraFacing = FRONT; - } - - public void setBackCamera() { - cameraFacing = BACK; - } +package org.catrobat.catroid.content.actions + +import com.badlogic.gdx.scenes.scene2d.actions.TemporalAction +import org.catrobat.catroid.stage.StageActivity + +class ChooseCameraAction : TemporalAction() { + private var cameraFacing = FRONT + + override fun update(percent: Float) { + val cameraManager = StageActivity.getActiveCameraManager() + if (cameraFacing == FRONT) { + cameraManager?.switchToFrontCamera() + } else { + cameraManager?.switchToBackCamera() + } + } + + fun setFrontCamera() { + cameraFacing = FRONT + } + + fun setBackCamera() { + cameraFacing = BACK + } + + companion object { + private const val BACK = 0 + private const val FRONT = 1 + } } diff --git a/catroid/src/main/java/org/catrobat/catroid/content/actions/FinishStageAction.java b/catroid/src/main/java/org/catrobat/catroid/content/actions/FinishStageAction.kt similarity index 61% rename from catroid/src/main/java/org/catrobat/catroid/content/actions/FinishStageAction.java rename to catroid/src/main/java/org/catrobat/catroid/content/actions/FinishStageAction.kt index 93a7fccc46b..304f6e8fcf4 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/actions/FinishStageAction.java +++ b/catroid/src/main/java/org/catrobat/catroid/content/actions/FinishStageAction.kt @@ -20,22 +20,26 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ +package org.catrobat.catroid.content.actions -package org.catrobat.catroid.content.actions; +import com.badlogic.gdx.scenes.scene2d.actions.TemporalAction +import org.catrobat.catroid.stage.StageActivity +import org.catrobat.catroid.stage.TestResult -import com.badlogic.gdx.scenes.scene2d.actions.TemporalAction; +class FinishStageAction : TemporalAction() { + var silent: Boolean = false -import org.catrobat.catroid.stage.StageActivity; -import org.catrobat.catroid.stage.TestResult; + override fun update(percent: Float) { + if (silent) { + StageActivity.finishStage() + } else { + StageActivity.finishTestWithResult( + TestResult(SUCCESS_MESSAGE, TestResult.STAGE_ACTIVITY_TEST_SUCCESS) + ) + } + } -import static org.catrobat.catroid.stage.TestResult.STAGE_ACTIVITY_TEST_SUCCESS; - -public class FinishStageAction extends TemporalAction { - - private static final String SUCCESS_MESSAGE = "Success"; - - @Override - protected void update(float percent) { - StageActivity.finishTestWithResult(new TestResult(SUCCESS_MESSAGE, STAGE_ACTIVITY_TEST_SUCCESS)); - } + companion object { + private const val SUCCESS_MESSAGE = "Success" + } } diff --git a/catroid/src/main/java/org/catrobat/catroid/content/actions/FlashAction.kt b/catroid/src/main/java/org/catrobat/catroid/content/actions/FlashAction.kt new file mode 100644 index 00000000000..9eb13b09239 --- /dev/null +++ b/catroid/src/main/java/org/catrobat/catroid/content/actions/FlashAction.kt @@ -0,0 +1,38 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2018 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.content.actions + +import com.badlogic.gdx.scenes.scene2d.actions.TemporalAction +import org.catrobat.catroid.stage.StageActivity + +class FlashAction : TemporalAction() { + var flashOn = false + + override fun update(percent: Float) { + if (flashOn) { + StageActivity.getActiveCameraManager()?.enableFlash() + } else { + StageActivity.getActiveCameraManager()?.disableFlash() + } + } +} diff --git a/catroid/src/main/java/org/catrobat/catroid/content/actions/ParameterizedAssertAction.kt b/catroid/src/main/java/org/catrobat/catroid/content/actions/ParameterizedAssertAction.kt new file mode 100644 index 00000000000..367e22518b8 --- /dev/null +++ b/catroid/src/main/java/org/catrobat/catroid/content/actions/ParameterizedAssertAction.kt @@ -0,0 +1,86 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2018 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.content.actions + +import org.catrobat.catroid.common.ParameterizedData +import org.catrobat.catroid.formulaeditor.Formula +import org.catrobat.catroid.formulaeditor.UserList + +class ParameterizedAssertAction : AssertAction() { + var actualFormula: Formula? = null + var expectedList: UserList? = null + var parameterizedData: ParameterizedData? = null + + init { + assertTitle = "\nParameterizedAssertError\n" + } + + override fun act(delta: Float): Boolean { + parameterizedData?.listSize = expectedList?.value?.size ?: 0 + + if (actualFormula == null) { + failWith("Actual is null") + return false + } + + if (expectedList == null) { + failWith("Expected is null") + return false + } + + val actualValue = actualFormula?.interpretObject(sprite).toString() + val expectedValue = + expectedList?.value?.get(parameterizedData?.currentPosition ?: 0) ?: "null" + + parameterizedData?.let { data -> + if (!equalValues(actualValue, expectedValue.toString())) { + data.failMessages = data.failMessages.plus( + "\n${data.currentParameters}\n" + + formattedAssertEqualsError(actualValue, expectedValue) + ) + } else { + data.successMessages = data.successMessages.plus( + "\n${data.currentParameters}\n" + + formattedSuccessMessage(actualValue, expectedValue) + ) + } + + data.currentPosition++ + data.currentParameters = "" + + if (data.failMessages.isNotEmpty() && + data.currentPosition >= expectedList?.value?.size ?: 0) { + failWith("Failed Tests:\n${data.failMessages}\n\nSucceeded Tests:\n${data.successMessages}") + } + } + + return true + } + + private fun formattedAssertEqualsError(actual: Any, expected: Any): String { + val indicator = generateIndicator(actual, expected) + return "expected: <$expected>\nactual: <$actual>\ndeviation: $indicator\n" + } + + private fun formattedSuccessMessage(actual: Any?, expected: Any?): String = "$actual == $expected\n" +} diff --git a/catroid/src/main/java/org/catrobat/catroid/content/actions/ReadVariableFromFileAction.kt b/catroid/src/main/java/org/catrobat/catroid/content/actions/ReadVariableFromFileAction.kt index a0506201382..66b3c869cbe 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/actions/ReadVariableFromFileAction.kt +++ b/catroid/src/main/java/org/catrobat/catroid/content/actions/ReadVariableFromFileAction.kt @@ -44,7 +44,7 @@ class ReadVariableFromFileAction : EventAction() { } var fileName = Utils.sanitizeFileName(formula!!.interpretString(sprite)) - if (!fileName.endsWith(".txt")) { + if (!fileName.contains(Regex("\\.\\w+$"))) { fileName += ".txt" } diff --git a/catroid/src/main/java/org/catrobat/catroid/content/actions/RepeatParameterizedAction.kt b/catroid/src/main/java/org/catrobat/catroid/content/actions/RepeatParameterizedAction.kt new file mode 100644 index 00000000000..61403153d69 --- /dev/null +++ b/catroid/src/main/java/org/catrobat/catroid/content/actions/RepeatParameterizedAction.kt @@ -0,0 +1,115 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2018 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.content.actions + +import com.badlogic.gdx.scenes.scene2d.actions.RepeatAction +import org.catrobat.catroid.common.ParameterizedData +import org.catrobat.catroid.content.Sprite +import org.catrobat.catroid.formulaeditor.UserList +import org.catrobat.catroid.formulaeditor.UserVariable +import org.catrobat.catroid.stage.StageActivity +import org.catrobat.catroid.stage.TestResult + +class RepeatParameterizedAction : RepeatAction() { + private var assertTitle = "ParameterizedInitialisationError" + private var currentTime = 0f + private var isCurrentLoopInitialized = false + var position = "" + var sprite: Sprite? = null + var parameterizedData: ParameterizedData? = null + var parameters: List> = emptyList() + + override fun delegate(delta: Float): Boolean { + if (parameters.isNullOrEmpty()) { + fail("Input was not selected") + return true + } + + if (!isCurrentLoopInitialized) { + if (parameterizedData?.currentPosition ?: 1 >= parameterizedData?.listSize ?: 0) { + parameterizedData?.reset() + return true + } + if (initParameter()) { + return true + } + + currentTime = 0f + isCurrentLoopInitialized = true + } + + currentTime += delta + if (action.act(delta) && currentTime >= loopDelay) { + if (parameterizedData?.currentPosition ?: 1 >= parameterizedData?.listSize ?: 0) { + parameterizedData?.reset() + return true + } + + isCurrentLoopInitialized = false + + action?.restart() + } + return false + } + + override fun restart() { + isCurrentLoopInitialized = false + super.restart() + } + + private fun fail(error: String) { + StageActivity.finishTestWithResult( + TestResult( + "${formattedPosition()}\n\n$assertTitle\n$error", + TestResult.STAGE_ACTIVITY_TEST_FAIL + ) + ) + } + + private fun initParameter(): Boolean = parameterizedData?.let { + it.currentParameters = "[${it.currentPosition}] " + + for ((userList, userVariable) in parameters) { + val data = userList.value + if (data.size <= it.currentPosition) { + fail( + "Input list is missing elements\n" + + "Failed Tests:\n${it.failMessages}\n\n" + + "Succeeded Tests:\n${it.successMessages}" + ) + return@let true + } else { + userVariable.value = data[it.currentPosition] + it.currentParameters += "${userVariable.name} = ${userVariable.value} | " + } + } + it.currentParameters = it.currentParameters.removeSuffix(" | ") + false + } ?: true + + private fun formattedPosition(): String = "on sprite ${sprite?.name}\n$position" + + companion object { + private const val loopDelay = 0.02f + } +} diff --git a/catroid/src/main/java/org/catrobat/catroid/content/backwardcompatibility/LegacyProjectWithoutScenes.java b/catroid/src/main/java/org/catrobat/catroid/content/backwardcompatibility/LegacyProjectWithoutScenes.java index 6b784f42f48..8d2d36621fd 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/backwardcompatibility/LegacyProjectWithoutScenes.java +++ b/catroid/src/main/java/org/catrobat/catroid/content/backwardcompatibility/LegacyProjectWithoutScenes.java @@ -35,7 +35,6 @@ import org.catrobat.catroid.formulaeditor.UserList; import org.catrobat.catroid.formulaeditor.UserVariable; import org.catrobat.catroid.io.StorageOperations; -import org.catrobat.catroid.stage.StageListener; import org.catrobat.catroid.utils.FileMetaDataExtractor; import java.io.File; @@ -48,6 +47,8 @@ import static org.catrobat.catroid.common.Constants.IMAGE_DIRECTORY_NAME; import static org.catrobat.catroid.common.Constants.SOUND_DIRECTORY_NAME; import static org.catrobat.catroid.common.FlavoredConstants.DEFAULT_ROOT_DIRECTORY; +import static org.catrobat.catroid.common.Constants.SCREENSHOT_MANUAL_FILE_NAME; +import static org.catrobat.catroid.common.Constants.SCREENSHOT_AUTOMATIC_FILE_NAME; @XStreamAlias("program") public class LegacyProjectWithoutScenes implements Serializable { @@ -80,8 +81,8 @@ public Project toProject(Context context) throws IOException { StorageOperations.createSceneDirectory(scene.getDirectory()); - File automaticScreenshot = new File(projectDir, StageListener.SCREENSHOT_AUTOMATIC_FILE_NAME); - File manualScreenshot = new File(projectDir, StageListener.SCREENSHOT_MANUAL_FILE_NAME); + File automaticScreenshot = new File(projectDir, SCREENSHOT_AUTOMATIC_FILE_NAME); + File manualScreenshot = new File(projectDir, SCREENSHOT_MANUAL_FILE_NAME); StorageOperations.copyDir(new File(projectDir, IMAGE_DIRECTORY_NAME), new File(scene.getDirectory(), IMAGE_DIRECTORY_NAME)); diff --git a/catroid/src/main/java/org/catrobat/catroid/content/bricks/AssertEqualsBrick.java b/catroid/src/main/java/org/catrobat/catroid/content/bricks/AssertEqualsBrick.java index 996f58d430c..ef7794d8054 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/bricks/AssertEqualsBrick.java +++ b/catroid/src/main/java/org/catrobat/catroid/content/bricks/AssertEqualsBrick.java @@ -52,14 +52,4 @@ public void addActionToSequence(Sprite sprite, ScriptSequenceAction sequence) { getFormulaWithBrickField(BrickField.ASSERT_EQUALS_EXPECTED), getPositionInformation())); } - - private String getPositionInformation() { - int position = 999; - String scriptName = "unknown"; - if (getParent() != null) { - position = getPositionInScript(); - scriptName = getScript().getClass().getSimpleName(); - } - return "Brick at position " + position + "\nin \"" + scriptName + "\""; - } } diff --git a/catroid/src/main/java/org/catrobat/catroid/content/bricks/AssertUserListsBrick.java b/catroid/src/main/java/org/catrobat/catroid/content/bricks/AssertUserListsBrick.java index 4f1007ed082..4c27befff14 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/bricks/AssertUserListsBrick.java +++ b/catroid/src/main/java/org/catrobat/catroid/content/bricks/AssertUserListsBrick.java @@ -47,14 +47,4 @@ public void addActionToSequence(Sprite sprite, ScriptSequenceAction sequence) { getUserListWithBrickData(BrickData.ASSERT_LISTS_EXPECTED), getPositionInformation())); } - - private String getPositionInformation() { - int position = 999; - String scriptName = "unknown"; - if (getParent() != null) { - position = getPositionInScript(); - scriptName = getScript().getClass().getSimpleName(); - } - return "Brick at position " + position + "\nin \"" + scriptName + "\""; - } } diff --git a/catroid/src/main/java/org/catrobat/catroid/content/bricks/Brick.java b/catroid/src/main/java/org/catrobat/catroid/content/bricks/Brick.java index 42c70ef196c..8e964a5db85 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/bricks/Brick.java +++ b/catroid/src/main/java/org/catrobat/catroid/content/bricks/Brick.java @@ -77,7 +77,9 @@ enum BrickField { STORE_CSV_INTO_USERLIST_COLUMN, STORE_CSV_INTO_USERLIST_CSV, - ASSERT_EQUALS_EXPECTED, ASSERT_EQUALS_ACTUAL; + ASSERT_EQUALS_EXPECTED, ASSERT_EQUALS_ACTUAL, + + ASSERT_LOOP_ACTUAL; public static final BrickField[] EXPECTS_STRING_VALUE = {VARIABLE, NOTE, SPEAK, STRING, ASK_QUESTION, NFC_NDEF_MESSAGE, ASK_SPEECH_QUESTION, LIST_ADD_ITEM, INSERT_ITEM_INTO_USERLIST_VALUE, diff --git a/catroid/src/main/java/org/catrobat/catroid/content/bricks/BrickBaseType.java b/catroid/src/main/java/org/catrobat/catroid/content/bricks/BrickBaseType.java index 24d3eec5493..62b2796f6b1 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/bricks/BrickBaseType.java +++ b/catroid/src/main/java/org/catrobat/catroid/content/bricks/BrickBaseType.java @@ -186,4 +186,14 @@ void notifyDataSetChanged(AppCompatActivity activity) { public String getHelpUrl(String category) { return "https://wiki.catrobat.org/bin/view/Documentation/Brick%20Documentation/" + category + "%20Bricks/#" + this.getClass().getSimpleName(); } + + protected String getPositionInformation() { + int position = -1; + String scriptName = "unknown"; + if (getParent() != null) { + position = getPositionInScript(); + scriptName = getScript().getClass().getSimpleName(); + } + return "Brick at position " + position + "\nin \"" + scriptName + "\""; + } } diff --git a/catroid/src/main/java/org/catrobat/catroid/content/bricks/CameraBrick.java b/catroid/src/main/java/org/catrobat/catroid/content/bricks/CameraBrick.java index 6f904b1dacb..0d958fa1af2 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/bricks/CameraBrick.java +++ b/catroid/src/main/java/org/catrobat/catroid/content/bricks/CameraBrick.java @@ -30,7 +30,6 @@ import android.widget.Spinner; import org.catrobat.catroid.R; -import org.catrobat.catroid.camera.CameraManager; import org.catrobat.catroid.content.Sprite; import org.catrobat.catroid.content.actions.ScriptSequenceAction; @@ -97,14 +96,6 @@ public void addRequiredResources(final ResourcesSet requiredResourcesSet) { @Override public void addActionToSequence(Sprite sprite, ScriptSequenceAction sequence) { - sequence.addAction(sprite.getActionFactory().createUpdateCameraPreviewAction(getCameraStateFromSpinner())); - } - - private CameraManager.CameraState getCameraStateFromSpinner() { - if (spinnerSelectionID == OFF) { - return CameraManager.CameraState.stopped; - } - - return CameraManager.CameraState.prepare; + sequence.addAction(sprite.getActionFactory().createUpdateCameraPreviewAction(spinnerSelectionID == ON)); } } diff --git a/catroid/src/main/java/org/catrobat/catroid/content/actions/FlashAction.java b/catroid/src/main/java/org/catrobat/catroid/content/bricks/ExitStageBrick.java similarity index 65% rename from catroid/src/main/java/org/catrobat/catroid/content/actions/FlashAction.java rename to catroid/src/main/java/org/catrobat/catroid/content/bricks/ExitStageBrick.java index f4ab13c0c8d..8105c203aa6 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/actions/FlashAction.java +++ b/catroid/src/main/java/org/catrobat/catroid/content/bricks/ExitStageBrick.java @@ -20,32 +20,26 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package org.catrobat.catroid.content.actions; +package org.catrobat.catroid.content.bricks; -import com.badlogic.gdx.scenes.scene2d.actions.TemporalAction; +import org.catrobat.catroid.R; +import org.catrobat.catroid.content.Sprite; +import org.catrobat.catroid.content.actions.ScriptSequenceAction; -import org.catrobat.catroid.utils.FlashUtil; +public class ExitStageBrick extends BrickBaseType { -public class FlashAction extends TemporalAction { + private static final long serialVersionUID = 1L; - private static final int OFF = 0; - private static final int ON = 1; - private int turnFlash = OFF; - - @Override - protected void update(float percent) { - if (turnFlash == ON) { - FlashUtil.flashOn(); - } else { - FlashUtil.flashOff(); - } + public ExitStageBrick() { } - public void turnFlashOn() { - turnFlash = ON; + @Override + public int getViewResource() { + return R.layout.brick_exit_stage; } - public void turnFlashOff() { - turnFlash = OFF; + @Override + public void addActionToSequence(Sprite sprite, ScriptSequenceAction sequence) { + sequence.addAction(sprite.getActionFactory().createFinishStageAction(true)); } } diff --git a/catroid/src/main/java/org/catrobat/catroid/content/bricks/FinishStageBrick.java b/catroid/src/main/java/org/catrobat/catroid/content/bricks/FinishStageBrick.java index fd067e04ac6..18e2ce09883 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/bricks/FinishStageBrick.java +++ b/catroid/src/main/java/org/catrobat/catroid/content/bricks/FinishStageBrick.java @@ -40,6 +40,6 @@ public int getViewResource() { @Override public void addActionToSequence(Sprite sprite, ScriptSequenceAction sequence) { - sequence.addAction(sprite.getActionFactory().createFinishStageAction()); + sequence.addAction(sprite.getActionFactory().createFinishStageAction(false)); } } diff --git a/catroid/src/main/java/org/catrobat/catroid/content/bricks/FlashBrick.java b/catroid/src/main/java/org/catrobat/catroid/content/bricks/FlashBrick.java index 0cf0e200e64..7c3cf9aa2b9 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/bricks/FlashBrick.java +++ b/catroid/src/main/java/org/catrobat/catroid/content/bricks/FlashBrick.java @@ -89,10 +89,6 @@ public void addRequiredResources(final ResourcesSet requiredResourcesSet) { @Override public void addActionToSequence(Sprite sprite, ScriptSequenceAction sequence) { - if (spinnerSelectionID == FLASH_ON) { - sequence.addAction(sprite.getActionFactory().createTurnFlashOnAction()); - } else { - sequence.addAction(sprite.getActionFactory().createTurnFlashOffAction()); - } + sequence.addAction(sprite.getActionFactory().createFlashAction(spinnerSelectionID == FLASH_ON)); } } diff --git a/catroid/src/main/java/org/catrobat/catroid/content/bricks/FormulaBrick.java b/catroid/src/main/java/org/catrobat/catroid/content/bricks/FormulaBrick.java index ad6964d9c66..6d63029900c 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/bricks/FormulaBrick.java +++ b/catroid/src/main/java/org/catrobat/catroid/content/bricks/FormulaBrick.java @@ -37,6 +37,8 @@ import org.catrobat.catroid.content.Sprite; import org.catrobat.catroid.formulaeditor.Formula; import org.catrobat.catroid.formulaeditor.InterpretationException; +import org.catrobat.catroid.formulaeditor.UserData; +import org.catrobat.catroid.formulaeditor.UserVariable; import org.catrobat.catroid.ui.fragment.FormulaEditorFragment; import org.catrobat.catroid.utils.Utils; @@ -186,4 +188,18 @@ protected void setSecondsLabel(View view, BrickField brickField) { textView.setText(context.getResources() .getQuantityString(R.plurals.second_plural, Utils.TRANSLATION_PLURAL_OTHER_INTEGER)); } + + public void updateUserDataReference(String oldName, String newName, UserData item, + boolean renameAll) { + for (Formula formula : getFormulas()) { + if (renameAll) { + formula.updateVariableName(oldName, newName); + formula.updateUserlistName(oldName, newName); + } else if (item instanceof UserVariable) { + formula.updateVariableName(oldName, newName); + } else { + formula.updateUserlistName(oldName, newName); + } + } + } } diff --git a/catroid/src/main/java/org/catrobat/catroid/content/bricks/ListSelectorBrick.kt b/catroid/src/main/java/org/catrobat/catroid/content/bricks/ListSelectorBrick.kt new file mode 100644 index 00000000000..df3c1c7da12 --- /dev/null +++ b/catroid/src/main/java/org/catrobat/catroid/content/bricks/ListSelectorBrick.kt @@ -0,0 +1,81 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2018 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.content.bricks + +import android.content.Context +import android.view.View +import android.widget.TextView +import androidx.annotation.IdRes +import org.catrobat.catroid.R +import org.catrobat.catroid.formulaeditor.UserData +import org.catrobat.catroid.formulaeditor.UserList +import org.catrobat.catroid.ui.recyclerview.fragment.ListSelectorFragment.Companion.showFragment +import org.catrobat.catroid.ui.recyclerview.fragment.ListSelectorFragment.ListSelectorInterface + +abstract class ListSelectorBrick : BrickBaseType(), View.OnClickListener, + ListSelectorInterface { + var userLists = mutableListOf() + + @Throws(CloneNotSupportedException::class) + override fun clone(): Brick { + val clone = super.clone() as ListSelectorBrick + clone.userLists = userLists.toMutableList() + return clone + } + + override fun getView(context: Context): View { + super.getView(context) + updateSelectorText() + return view + } + + @get:IdRes + protected abstract val selectorId: Int + protected open fun updateSelectorText() { + val brickFieldView = view.findViewById(selectorId) + brickFieldView.text = view.resources.getQuantityString( + R.plurals.list_selection_plural, + userLists.size, userLists.size + ) + } + + fun setClickListeners() { + val brickFieldView = view.findViewById(selectorId) + brickFieldView.setOnClickListener(this) + } + + override fun onClick(view: View) { + showFragment(view.context, this) + } + + override fun onUserListSelected(userLists: List) { + this.userLists = userLists.toMutableList() + updateSelectorText() + } + + fun deselectElements(deletedElements: List<*>) { + deletedElements.filterIsInstance>().forEach { element -> + userLists.removeAll { list -> list.name == element.name } + } + } +} diff --git a/catroid/src/main/java/org/catrobat/catroid/content/bricks/ParameterizedBrick.kt b/catroid/src/main/java/org/catrobat/catroid/content/bricks/ParameterizedBrick.kt new file mode 100644 index 00000000000..e1af8395a60 --- /dev/null +++ b/catroid/src/main/java/org/catrobat/catroid/content/bricks/ParameterizedBrick.kt @@ -0,0 +1,197 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2018 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.content.bricks + +import android.widget.TextView +import com.thoughtworks.xstream.annotations.XStreamOmitField +import org.catrobat.catroid.ProjectManager +import org.catrobat.catroid.R +import org.catrobat.catroid.common.ParameterizedData +import org.catrobat.catroid.content.ActionFactory +import org.catrobat.catroid.content.Sprite +import org.catrobat.catroid.content.actions.ScriptSequenceAction +import org.catrobat.catroid.content.bricks.Brick.ResourcesSet +import org.catrobat.catroid.formulaeditor.UserList +import org.catrobat.catroid.formulaeditor.UserVariable + +class ParameterizedBrick : ListSelectorBrick(), CompositeBrick { + private var loopBricks = mutableListOf() + private var endBrick = ParameterizedEndBrick(this) + + @Transient + @XStreamOmitField + var parameterizedData: ParameterizedData = ParameterizedData() + + override val selectorId: Int + get() = R.id.brick_param_list_of_list_text + + override fun hasSecondaryList(): Boolean = false + + override fun getNestedBricks(): List = loopBricks + + override fun getSecondaryNestedBricks(): List? = null + + fun addBrick(brick: Brick): Boolean = loopBricks.add(brick) + + override fun updateSelectorText() { + super.updateSelectorText() + val listSize = userLists.size + val firstLabel = view.findViewById(R.id.brick_param_first_label) + firstLabel.text = view.resources.getQuantityString( + R.plurals.brick_parameterized_foreach_plural, + listSize, listSize + ) + val secondLabel = view.findViewById(R.id.brick_param_second_label) + secondLabel.text = view.resources.getQuantityString( + R.plurals.brick_parameterized_stored_plural, + listSize, listSize + ) + } + + override fun setCommentedOut(commentedOut: Boolean) { + super.setCommentedOut(commentedOut) + for (brick in loopBricks) { + brick.isCommentedOut = commentedOut + } + endBrick.isCommentedOut = commentedOut + } + + @Throws(CloneNotSupportedException::class) + override fun clone(): Brick { + val clone = super.clone() as ParameterizedBrick + clone.endBrick = ParameterizedEndBrick(clone) + clone.loopBricks = mutableListOf() + clone.parameterizedData = ParameterizedData() + for (brick in loopBricks) { + clone.addBrick(brick.clone()) + } + return clone + } + + override fun consistsOfMultipleParts(): Boolean = true + + override fun getAllParts(): List { + val bricks = mutableListOf() + bricks.add(this) + bricks.add(endBrick) + return bricks + } + + override fun addToFlatList(bricks: MutableList) { + super.addToFlatList(bricks) + for (brick in loopBricks) { + brick.addToFlatList(bricks) + } + bricks.add(endBrick) + } + + override fun setParent(parent: Brick) { + super.setParent(parent) + for (brick in loopBricks) { + brick.parent = this + } + endBrick.parent = this + } + + override fun getDragAndDropTargetList(): List = loopBricks + + override fun removeChild(brick: Brick): Boolean { + if (loopBricks.remove(brick)) { + return true + } + + for (childBrick in loopBricks) { + if (childBrick.removeChild(brick)) { + return true + } + } + return false + } + + override fun getViewResource(): Int = R.layout.brick_parameterized_input + + override fun addActionToSequence(sprite: Sprite, sequence: ScriptSequenceAction) { + val repeatSequence = + ActionFactory.eventSequence(sequence.script) as ScriptSequenceAction + loopBricks.filterNot { brick -> brick.isCommentedOut }.forEach { + it.addActionToSequence(sprite, repeatSequence) + } + endBrick.addActionToSequence(sprite, repeatSequence) + parameterizedData?.reset() + + sequence.addAction( + sprite.actionFactory.createRepeatParameterizedAction( + sprite, parameterizedData, + createLinkedPair(), positionInformation, repeatSequence + ) + ) + } + + override fun addRequiredResources(requiredResourcesSet: ResourcesSet) { + super.addRequiredResources(requiredResourcesSet) + for (brick in loopBricks) { + brick.addRequiredResources(requiredResourcesSet) + } + } + + override fun onUserListSelected(userLists: List) { + super.onUserListSelected(userLists) + createLinkedVariables() + } + + private fun createLinkedVariables() { + val projectManager = ProjectManager.getInstance() + val currentProject = projectManager.currentProject + val currentSprite = projectManager.currentSprite + val globalLists = currentProject.userLists ?: emptyList() + val globalVariables = currentProject.userVariables ?: emptyList() + val localVariables = currentSprite?.userVariables ?: emptyList() + + userLists.forEach { currentList -> + if (globalVariables.none { it.name == currentList.name } && + localVariables.none { it.name == currentList.name }) { + if (globalLists.contains(currentList)) { + currentProject.addUserVariable(UserVariable(currentList.name)) + } else { + currentSprite.addUserVariable(UserVariable(currentList.name)) + } + } + } + } + + private fun createLinkedPair(): List> { + val result = mutableListOf>() + val projectManager = ProjectManager.getInstance() + val currentProject = projectManager.currentProject + val currentSprite = projectManager.currentSprite + + userLists.forEach { currentList -> + val variable = currentProject?.getUserVariable(currentList.name) + ?: currentSprite?.getUserVariable(currentList.name) ?: return@forEach + + result.add(Pair(currentList, variable)) + } + + return result + } +} diff --git a/catroid/src/main/java/org/catrobat/catroid/content/bricks/ParameterizedEndBrick.kt b/catroid/src/main/java/org/catrobat/catroid/content/bricks/ParameterizedEndBrick.kt new file mode 100644 index 00000000000..bc02e4c87f7 --- /dev/null +++ b/catroid/src/main/java/org/catrobat/catroid/content/bricks/ParameterizedEndBrick.kt @@ -0,0 +1,59 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2020 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.content.bricks + +import org.catrobat.catroid.R +import org.catrobat.catroid.content.Sprite +import org.catrobat.catroid.content.actions.ScriptSequenceAction + +class ParameterizedEndBrick() : UserListBrick() { + init { + addAllowedBrickField(Brick.BrickField.ASSERT_LOOP_ACTUAL, R.id.brick_param_assert_text) + } + + constructor(parent: ParameterizedBrick) : this() { + this.parent = parent + } + + override fun consistsOfMultipleParts(): Boolean = true + + override fun getAllParts(): List = parent.allParts + + override fun getDragAndDropTargetList(): List = parent.parent.dragAndDropTargetList + + override fun getPositionInDragAndDropTargetList(): Int = parent.parent.dragAndDropTargetList.indexOf(parent) + + override fun getViewResource(): Int = R.layout.brick_parameterized_assert + + override fun addActionToSequence(sprite: Sprite, sequence: ScriptSequenceAction) { + sequence.addAction( + sprite.actionFactory.createParameterizedAssertAction( + sprite, getFormulaWithBrickField(Brick.BrickField.ASSERT_LOOP_ACTUAL), userList, + (parent as ParameterizedBrick).parameterizedData, positionInformation + ) + ) + } + + override fun getSpinnerId(): Int = R.id.brick_param_expected_list +} diff --git a/catroid/src/main/java/org/catrobat/catroid/content/bricks/StopSoundBrick.kt b/catroid/src/main/java/org/catrobat/catroid/content/bricks/StopSoundBrick.kt index f4ec64d2332..6b386dbc049 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/bricks/StopSoundBrick.kt +++ b/catroid/src/main/java/org/catrobat/catroid/content/bricks/StopSoundBrick.kt @@ -40,16 +40,19 @@ class StopSoundBrick : BrickBaseType(), BrickSpinner.OnItemSelectedListener, NewItemInterface { var sound: SoundInfo? = null - lateinit var spinner: BrickSpinner + + @Transient + private lateinit var spinner: BrickSpinner override fun getViewResource() = R.layout.brick_stop_sound override fun getView(context: Context): View { super.getView(context) + val items = mutableListOf(NewOption(context.getString(R.string.new_option))) items.addAll(ProjectManager.getInstance().currentSprite.soundList) - spinner = BrickSpinner(R.id.brick_stop_sound_spinner, view, items) - spinner.apply { + with(BrickSpinner(R.id.brick_stop_sound_spinner, view, items)) { + spinner = this setOnItemSelectedListener(this@StopSoundBrick) setSelection(sound) } @@ -63,8 +66,8 @@ class StopSoundBrick : BrickBaseType(), } } - override fun addItem(item: SoundInfo?) { - item?.let { spinner.add(it) } + override fun addItem(item: SoundInfo) { + spinner.add(item) spinner.setSelection(item) } diff --git a/catroid/src/main/java/org/catrobat/catroid/content/bricks/UserDefinedBrick.java b/catroid/src/main/java/org/catrobat/catroid/content/bricks/UserDefinedBrick.java index 7d6ede91a38..cae013d565d 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/bricks/UserDefinedBrick.java +++ b/catroid/src/main/java/org/catrobat/catroid/content/bricks/UserDefinedBrick.java @@ -142,7 +142,11 @@ private boolean lastContentIsLabel() { return userDefinedBrickDataList.get(userDefinedBrickDataList.size() - 1) instanceof UserDefinedBrickLabel; } - public boolean isUserDefinedBrickDataEqual(UserDefinedBrick other) { + public boolean isUserDefinedBrickDataEqual(Brick brick) { + if (!(brick instanceof UserDefinedBrick)) { + return false; + } + UserDefinedBrick other = (UserDefinedBrick) brick; if (userDefinedBrickDataList.size() != other.userDefinedBrickDataList.size()) { return false; } diff --git a/catroid/src/main/java/org/catrobat/catroid/facedetection/FaceDetectionHandler.java b/catroid/src/main/java/org/catrobat/catroid/facedetection/FaceDetectionHandler.java deleted file mode 100644 index 283b42253ec..00000000000 --- a/catroid/src/main/java/org/catrobat/catroid/facedetection/FaceDetectionHandler.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2018 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.facedetection; - -import android.hardware.Camera; -import android.util.Log; - -import org.catrobat.catroid.camera.CameraManager; -import org.catrobat.catroid.formulaeditor.SensorCustomEventListener; - -import androidx.annotation.VisibleForTesting; - -public final class FaceDetectionHandler { - - private static final String TAG = FaceDetectionHandler.class.getSimpleName(); - private static FaceDetector faceDetector; - private static boolean running = false; - private static boolean paused = false; - - // Suppress default constructor for noninstantiability - private FaceDetectionHandler() { - throw new AssertionError(); - } - - private static void createFaceDetector() { - if (isIcsFaceDetectionSupported()) { - faceDetector = new IcsFaceDetector(); - } else { - faceDetector = new SlowFaceDetector(); - } - } - - public static boolean isFaceDetectionRunning() { - return running; - } - - public static boolean startFaceDetection() { - if (running) { - return true; - } - if (!CameraManager.getInstance().hasBackCamera() && !CameraManager.getInstance().hasFrontCamera()) { - return false; - } - - if (faceDetector == null) { - createFaceDetector(); - if (faceDetector == null) { - return false; - } - } - running = faceDetector.startFaceDetection(); - return running; - } - - public static void resetFaceDedection() { - if (running) { - stopFaceDetection(); - } - paused = false; - } - - public static void stopFaceDetection() { - if (!running) { - return; - } - if (faceDetector == null) { - return; - } - - faceDetector.stopFaceDetection(); - running = false; - } - - public static void pauseFaceDetection() { - if (!running) { - return; - } - if (faceDetector == null) { - return; - } - paused = true; - stopFaceDetection(); - running = false; - } - - public static void resumeFaceDetection() { - if (!paused) { - return; - } - startFaceDetection(); - paused = false; - } - - public static void registerOnFaceDetectedListener(SensorCustomEventListener listener) { - if (faceDetector == null) { - createFaceDetector(); - } - faceDetector.addOnFaceDetectedListener(listener); - } - - public static void unregisterOnFaceDetectedListener(SensorCustomEventListener listener) { - if (faceDetector == null) { - return; - } - faceDetector.removeOnFaceDetectedListener(listener); - } - - public static void registerOnFaceDetectionStatusListener(SensorCustomEventListener listener) { - if (faceDetector == null) { - createFaceDetector(); - } - faceDetector.addOnFaceDetectionStatusListener(listener); - } - - public static void unregisterOnFaceDetectionStatusListener(SensorCustomEventListener listener) { - if (faceDetector == null) { - return; - } - faceDetector.removeOnFaceDetectionStatusListener(listener); - } - - public static boolean isIcsFaceDetectionSupported() { - int possibleFaces = 0; - try { - boolean cameraReady = CameraManager.getInstance().isReady(); - if (!cameraReady) { - CameraManager.getInstance().startCamera(); - } - - Camera camera = CameraManager.getInstance().getCurrentCamera(); - possibleFaces = getMaxNumberOfFaces(camera); - - if (!cameraReady) { - CameraManager.getInstance().releaseCamera(); - } - } catch (Exception exception) { - Log.e(TAG, "Camera unaccessable!", exception); - } - return possibleFaces > 0; - } - - private static int getMaxNumberOfFaces(Camera camera) { - if (camera != null && camera.getParameters() != null) { - return camera.getParameters().getMaxNumDetectedFaces(); - } - return 0; - } - - @VisibleForTesting - public static void setFaceDetector(FaceDetector detector) { - faceDetector = detector; - } - - @VisibleForTesting - public static FaceDetector getFaceDetector() { - return faceDetector; - } -} diff --git a/catroid/src/main/java/org/catrobat/catroid/facedetection/FaceDetector.java b/catroid/src/main/java/org/catrobat/catroid/facedetection/FaceDetector.java deleted file mode 100644 index e049d2fbd55..00000000000 --- a/catroid/src/main/java/org/catrobat/catroid/facedetection/FaceDetector.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2018 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.facedetection; - -import android.graphics.Point; - -import org.catrobat.catroid.camera.CameraManager; -import org.catrobat.catroid.common.ScreenValues; -import org.catrobat.catroid.formulaeditor.SensorCustomEvent; -import org.catrobat.catroid.formulaeditor.SensorCustomEventListener; -import org.catrobat.catroid.formulaeditor.Sensors; - -import java.util.LinkedList; -import java.util.List; - -import androidx.annotation.VisibleForTesting; - -public abstract class FaceDetector { - - private List faceDetectedListeners = new LinkedList(); - private List faceDetectionStatusListeners = new LinkedList(); - - private boolean faceDetected = false; - - public abstract boolean startFaceDetection(); - - public abstract void stopFaceDetection(); - - public void addOnFaceDetectedListener(SensorCustomEventListener listener) { - if (listener == null) { - return; - } - faceDetectedListeners.add(listener); - } - - public void removeOnFaceDetectedListener(SensorCustomEventListener listener) { - faceDetectedListeners.remove(listener); - } - - public void addOnFaceDetectionStatusListener(SensorCustomEventListener listener) { - if (listener == null) { - return; - } - faceDetectionStatusListeners.add(listener); - } - - public void removeOnFaceDetectionStatusListener(SensorCustomEventListener listener) { - faceDetectionStatusListeners.remove(listener); - } - - void onFaceDetected(Point position, int size) { - float[] positionXFloatValue = new float[] {position.x}; - boolean invertY = !CameraManager.getInstance().isCurrentCameraFacingBack(); - float[] positionYFloatValue = new float[] {invertY ? -position.y : position.y}; - float[] sizeFloatValue = new float[] {size}; - SensorCustomEvent xPositionEvent = new SensorCustomEvent(Sensors.FACE_X_POSITION, positionXFloatValue); - SensorCustomEvent yPositionEvent = new SensorCustomEvent(Sensors.FACE_Y_POSITION, positionYFloatValue); - SensorCustomEvent sizeEvent = new SensorCustomEvent(Sensors.FACE_SIZE, sizeFloatValue); - for (SensorCustomEventListener faceDetectedListener : faceDetectedListeners) { - faceDetectedListener.onCustomSensorChanged(xPositionEvent); - faceDetectedListener.onCustomSensorChanged(yPositionEvent); - faceDetectedListener.onCustomSensorChanged(sizeEvent); - } - } - - protected void onFaceDetected(boolean faceDetected) { - if (this.faceDetected != faceDetected) { - this.faceDetected = faceDetected; - float[] detectedFloatValue = new float[] {faceDetected ? 1 : 0}; - SensorCustomEvent event = new SensorCustomEvent(Sensors.FACE_DETECTED, detectedFloatValue); - for (SensorCustomEventListener listener : faceDetectionStatusListeners) { - listener.onCustomSensorChanged(event); - } - } - } - - Point getRelationForFacePosition() { - return new Point(-ScreenValues.SCREEN_WIDTH, -ScreenValues.SCREEN_HEIGHT); - } - - @VisibleForTesting - public void callOnFaceDetected(boolean faceDetected) { - onFaceDetected(faceDetected); - } - - @VisibleForTesting - public void callOnFaceDetected(Point position, int size) { - onFaceDetected(position, size); - } -} diff --git a/catroid/src/main/java/org/catrobat/catroid/facedetection/IcsFaceDetector.java b/catroid/src/main/java/org/catrobat/catroid/facedetection/IcsFaceDetector.java deleted file mode 100644 index b4090cf7b80..00000000000 --- a/catroid/src/main/java/org/catrobat/catroid/facedetection/IcsFaceDetector.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2018 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.facedetection; - -import android.graphics.Point; -import android.graphics.Rect; -import android.hardware.Camera; -import android.hardware.Camera.Face; -import android.hardware.Camera.FaceDetectionListener; - -import org.catrobat.catroid.camera.CameraManager; - -public class IcsFaceDetector extends FaceDetector implements FaceDetectionListener { - - private boolean running = false; - - public IcsFaceDetector() { - } - - @Override - public boolean startFaceDetection() { - if (running) { - return true; - } - - if (!CameraManager.getInstance().isReady()) { - CameraManager.getInstance().startCamera(); - } - - Camera camera = CameraManager.getInstance().getCurrentCamera(); - if (camera == null) { - return false; - } - camera.setFaceDetectionListener(this); - camera.startFaceDetection(); - - return running = true; - } - - @Override - public void stopFaceDetection() { - if (!running) { - return; - } - running = false; - CameraManager.getInstance().getCurrentCamera().stopFaceDetection(); - } - - @Override - public void onFaceDetection(Face[] faces, Camera camera) { - boolean detected = faces.length > 0; - onFaceDetected(detected); - if (detected) { - int maxConfidence = faces[0].score; - int bestFaceIndex = 0; - for (int i = 1; i < faces.length; i++) { - if (faces[i].score > maxConfidence) { - maxConfidence = faces[i].score; - bestFaceIndex = i; - } - } - Face bestFace = faces[bestFaceIndex]; - Rect faceBounds = bestFace.rect; - Point centerPoint = new Point(faceBounds.centerX(), faceBounds.centerY()); - Point portraitCenterPoint = new Point(centerPoint.y, centerPoint.x); - Point relationSize = getRelationForFacePosition(); - Point relativePoint = new Point(portraitCenterPoint.x * relationSize.x / 2000, portraitCenterPoint.y - * relationSize.y / 2000); - int faceSize = (faceBounds.right - faceBounds.left) / 10; - faceSize = Math.min(faceSize, 100); - onFaceDetected(relativePoint, faceSize); - } - } -} diff --git a/catroid/src/main/java/org/catrobat/catroid/facedetection/SlowFaceDetector.java b/catroid/src/main/java/org/catrobat/catroid/facedetection/SlowFaceDetector.java deleted file mode 100644 index 44c23b471fd..00000000000 --- a/catroid/src/main/java/org/catrobat/catroid/facedetection/SlowFaceDetector.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2018 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.facedetection; - -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.Point; -import android.graphics.PointF; -import android.media.FaceDetector; -import android.media.FaceDetector.Face; - -import org.catrobat.catroid.camera.CameraManager; -import org.catrobat.catroid.camera.JpgPreviewCallback; - -import androidx.annotation.VisibleForTesting; - -public class SlowFaceDetector extends org.catrobat.catroid.facedetection.FaceDetector implements JpgPreviewCallback { - - private static final int NUMBER_OF_FACES = 1; - - @Override - public boolean startFaceDetection() { - CameraManager.getInstance().addOnJpgPreviewFrameCallback(this); - return CameraManager.getInstance().startCamera(); - } - - @Override - public void stopFaceDetection() { - CameraManager.getInstance().removeOnJpgPreviewFrameCallback(this); - CameraManager.getInstance().releaseCamera(); - } - - @Override - public void onFrame(byte[] data) { - Bitmap preview = BitmapFactory.decodeByteArray(data, 0, data.length); - detectFaces(preview); - preview.recycle(); - } - - private void detectFaces(Bitmap bitmap) { - if (bitmap == null) { - return; - } - int height = bitmap.getWidth(); - int width = bitmap.getHeight(); - - Matrix rotateAndInvertX = new Matrix(); - int rotationAngle = 0; - boolean invertX = CameraManager.getInstance().isCurrentCameraFacingBack(); - rotateAndInvertX.postRotate(rotationAngle); - rotateAndInvertX.postScale(invertX ? -1 : 1, 1); - Bitmap portraitBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), - rotateAndInvertX, true); - Bitmap rgb565Bitmap = Bitmap.createBitmap(width, height, Config.RGB_565); - Paint paint = new Paint(); - paint.setDither(true); - - Canvas canvas = new Canvas(); - canvas.setBitmap(rgb565Bitmap); - canvas.drawBitmap(portraitBitmap, 0, 0, paint); - - FaceDetector detector = new FaceDetector(width, height, NUMBER_OF_FACES); - Face[] faces = new Face[NUMBER_OF_FACES]; - int numberOfFaces = detector.findFaces(rgb565Bitmap, faces); - - boolean detected = numberOfFaces > 0; - onFaceDetected(detected); - if (detected) { - PointF centerPoint = new PointF(); - faces[0].getMidPoint(centerPoint); - float eyeDistance = faces[0].eyesDistance(); - onFaceFound(centerPoint, eyeDistance, width, height); - } - } - - private void onFaceFound(PointF centerPoint, float eyeDistance, int detectionWidth, int detectionHeight) { - Point intPoint = new Point((int) centerPoint.x, (int) centerPoint.y); - Point relationSize = getRelationForFacePosition(); - Point relativePoint = new Point((intPoint.x - detectionWidth / 2) * relationSize.x / detectionWidth, - (intPoint.y - detectionHeight / 2) * relationSize.y / detectionHeight); - int estimatedFaceWidth = (int) (eyeDistance * 2); - int relativeFaceSize = 200 * estimatedFaceWidth / detectionWidth; - relativeFaceSize = Math.min(relativeFaceSize, 100); - onFaceDetected(relativePoint, relativeFaceSize); - } - - @VisibleForTesting - public void callOnFaceFound(PointF centerPoint, float eyeDistance, int detectionWidth, int detectionHeight) { - onFaceFound(centerPoint, eyeDistance, detectionWidth, detectionHeight); - } -} diff --git a/catroid/src/main/java/org/catrobat/catroid/formulaeditor/SensorHandler.java b/catroid/src/main/java/org/catrobat/catroid/formulaeditor/SensorHandler.java index c9efdbd50df..b452a7cdeb1 100644 --- a/catroid/src/main/java/org/catrobat/catroid/formulaeditor/SensorHandler.java +++ b/catroid/src/main/java/org/catrobat/catroid/formulaeditor/SensorHandler.java @@ -43,7 +43,7 @@ import org.catrobat.catroid.ProjectManager; import org.catrobat.catroid.bluetooth.base.BluetoothDevice; import org.catrobat.catroid.bluetooth.base.BluetoothDeviceService; -import org.catrobat.catroid.camera.CameraManager; +import org.catrobat.catroid.camera.FaceDetector; import org.catrobat.catroid.cast.CastManager; import org.catrobat.catroid.common.CatroidService; import org.catrobat.catroid.common.ServiceProvider; @@ -51,7 +51,6 @@ import org.catrobat.catroid.devices.mindstorms.ev3.LegoEV3; import org.catrobat.catroid.devices.mindstorms.nxt.LegoNXT; import org.catrobat.catroid.drone.ardrone.DroneServiceWrapper; -import org.catrobat.catroid.facedetection.FaceDetectionHandler; import org.catrobat.catroid.nfc.NfcHandler; import org.catrobat.catroid.utils.TouchUtil; @@ -194,10 +193,7 @@ public static void startSensorListener(Context context) { SensorHandler.registerListener(instance); - if (CameraManager.getInstance() != null) { - FaceDetectionHandler.registerOnFaceDetectedListener(instance); - FaceDetectionHandler.registerOnFaceDetectionStatusListener(instance); - } + FaceDetector.addListener(instance); if (instance.sensorLoudness != null) { instance.sensorLoudness.registerListener(instance); @@ -268,8 +264,7 @@ public static void stopSensorListeners() { instance.locationManager.removeGpsStatusListener(instance); } - FaceDetectionHandler.unregisterOnFaceDetectedListener(instance); - FaceDetectionHandler.unregisterOnFaceDetectionStatusListener(instance); + FaceDetector.removeListener(instance); } public static Object getSensorValue(Sensors sensor) { diff --git a/catroid/src/main/java/org/catrobat/catroid/io/ProjectAndSceneScreenshotLoader.java b/catroid/src/main/java/org/catrobat/catroid/io/ProjectAndSceneScreenshotLoader.java index a3e324d94b9..9ded2faf3fb 100644 --- a/catroid/src/main/java/org/catrobat/catroid/io/ProjectAndSceneScreenshotLoader.java +++ b/catroid/src/main/java/org/catrobat/catroid/io/ProjectAndSceneScreenshotLoader.java @@ -31,16 +31,22 @@ import org.catrobat.catroid.utils.ImageEditing; import java.io.File; +import java.io.FilenameFilter; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import static org.catrobat.catroid.common.FlavoredConstants.DEFAULT_ROOT_DIRECTORY; -import static org.catrobat.catroid.stage.StageListener.SCREENSHOT_AUTOMATIC_FILE_NAME; -import static org.catrobat.catroid.stage.StageListener.SCREENSHOT_MANUAL_FILE_NAME; +import static org.catrobat.catroid.common.Constants.SCREENSHOT_AUTOMATIC_FILE_NAME; +import static org.catrobat.catroid.common.Constants.SCREENSHOT_MANUAL_FILE_NAME; +import static org.catrobat.catroid.common.Constants.DEFAULT_IMAGE_EXTENSION; public class ProjectAndSceneScreenshotLoader { @@ -115,6 +121,41 @@ public File getScreenshotFile(String projectName, String sceneName, boolean isBa return screenshotLoader.getScreenshotFile(); } + public String getScreenshotSceneName(File projectDir) { + FilenameFilter filter = new FilenameFilter() { + + public boolean accept(File f, String name) { + return name.endsWith(DEFAULT_IMAGE_EXTENSION); + } + }; + File[] projectScreenshots = projectDir.listFiles(filter); + if (projectScreenshots == null || projectScreenshots.length != 0) { + return null; + } + List screenshots = new ArrayList<>(); + for (File scene : projectDir.listFiles()) { + File[] sceneScreenshots = scene.listFiles(filter); + if (sceneScreenshots != null && sceneScreenshots.length > 0) { + screenshots.addAll(Arrays.asList(sceneScreenshots)); + } + } + if (screenshots.isEmpty()) { + return null; + } + Collections.sort(screenshots, new Comparator() { + @Override + public int compare(File screenshot2, File screenshot1) { + return Long.compare(screenshot1.lastModified(), screenshot2.lastModified()); + } + }); + for (File screenshot : screenshots) { + if (screenshot.getName().equals(SCREENSHOT_MANUAL_FILE_NAME)) { + return screenshot.getParentFile().getName(); + } + } + return screenshots.get(0).getParentFile().getName(); + } + class ScreenshotLoader implements Runnable { ScreenshotData projectAndSceneScreenshotData; diff --git a/catroid/src/main/java/org/catrobat/catroid/io/XstreamSerializer.java b/catroid/src/main/java/org/catrobat/catroid/io/XstreamSerializer.java index 11fc11eb3ef..8ad381fc1c9 100644 --- a/catroid/src/main/java/org/catrobat/catroid/io/XstreamSerializer.java +++ b/catroid/src/main/java/org/catrobat/catroid/io/XstreamSerializer.java @@ -105,6 +105,7 @@ import org.catrobat.catroid.content.bricks.DroneTakeOffLandBrick; import org.catrobat.catroid.content.bricks.DroneTurnLeftBrick; import org.catrobat.catroid.content.bricks.DroneTurnRightBrick; +import org.catrobat.catroid.content.bricks.ExitStageBrick; import org.catrobat.catroid.content.bricks.FinishStageBrick; import org.catrobat.catroid.content.bricks.FlashBrick; import org.catrobat.catroid.content.bricks.ForVariableFromToBrick; @@ -147,6 +148,8 @@ import org.catrobat.catroid.content.bricks.MoveNStepsBrick; import org.catrobat.catroid.content.bricks.NextLookBrick; import org.catrobat.catroid.content.bricks.NoteBrick; +import org.catrobat.catroid.content.bricks.ParameterizedBrick; +import org.catrobat.catroid.content.bricks.ParameterizedEndBrick; import org.catrobat.catroid.content.bricks.PenDownBrick; import org.catrobat.catroid.content.bricks.PenUpBrick; import org.catrobat.catroid.content.bricks.PhiroIfLogicBeginBrick; @@ -529,6 +532,9 @@ private void prepareXstream(Class projectClass, Class sceneClass) { xstream.alias("brick", AssertEqualsBrick.class); xstream.alias("brick", FinishStageBrick.class); xstream.alias("brick", AssertUserListsBrick.class); + xstream.alias("brick", ExitStageBrick.class); + xstream.alias("brick", ParameterizedBrick.class); + xstream.alias("brick", ParameterizedEndBrick.class); xstream.alias("brick", TapAtBrick.class); xstream.alias("brick", DroneFlipBrick.class); @@ -628,7 +634,7 @@ public static boolean renameProject(File xmlFile, String dstName) throws IOExcep throw new FileNotFoundException(xmlFile + " does not exist."); } - String currentXml = Files.toString(xmlFile, Charsets.UTF_8); + String currentXml = Files.asCharSource(xmlFile, Charsets.UTF_8).read(); StringFinder stringFinder = new StringFinder(); if (!stringFinder.findBetween(currentXml, PROGRAM_NAME_START_TAG, PROGRAM_NAME_END_TAG)) { @@ -713,7 +719,7 @@ public boolean saveProject(Project project) { if (currentCodeFile.exists()) { try { - String previousXml = Files.toString(currentCodeFile, Charsets.UTF_8); + String previousXml = Files.asCharSource(currentCodeFile, Charsets.UTF_8).read(); if (previousXml.equals(currentXml)) { Log.d(TAG, "Project version is the same. Do not update " + currentCodeFile.getName()); @@ -791,7 +797,7 @@ public static String extractDefaultSceneNameFromXml(File projectDir) { StringFinder stringFinder = new StringFinder(); try { - String xml = Files.toString(xmlFile, Charsets.UTF_8); + String xml = Files.asCharSource(xmlFile, Charsets.UTF_8).read(); if (!stringFinder.findBetween(xml, "\\s*\\s*", "")) { return null; } else { diff --git a/catroid/src/main/java/org/catrobat/catroid/stage/CameraSurface.java b/catroid/src/main/java/org/catrobat/catroid/stage/CameraSurface.java deleted file mode 100644 index 7c65ca22571..00000000000 --- a/catroid/src/main/java/org/catrobat/catroid/stage/CameraSurface.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2018 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.stage; - -import android.content.Context; -import android.hardware.Camera; -import android.util.Log; -import android.view.SurfaceHolder; -import android.view.SurfaceView; - -import org.catrobat.catroid.camera.CameraManager; - -public class CameraSurface extends SurfaceView implements SurfaceHolder.Callback { - private static final String TAG = CameraSurface.class.getSimpleName(); - - private Camera camera = null; - - public CameraSurface(Context context) { - super(context); - - // We're implementing the Callback interface and want to get notified - // about certain surface events. - getHolder().addCallback(this); - // We're changing the surface to a PUSH surface, meaning we're receiving - // all buffer data from another component - the camera, in this case. - getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); - } - - @Override - public void surfaceCreated(SurfaceHolder surfaceHolder) { - camera = CameraManager.getInstance().getCurrentCamera(); - } - - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - // This method is called when the surface changes, e.g. when it's size is set. - // We use the opportunity to initialize the camera preview display dimensions - - // We also assign the preview display to this surface... - synchronized (CameraManager.getInstance().cameraChangeLock) { - try { - if (camera != null) { - camera.stopPreview(); - camera.setPreviewDisplay(holder); - camera.startPreview(); - } - } catch (Exception e) { - Log.e(TAG, "Error at surfaceChanged"); - Log.e(TAG, e.getMessage()); - } - } - } - - @Override - public void surfaceDestroyed(SurfaceHolder surfaceHolder) { - this.getHolder().removeCallback(this); - //camera.stopPreview(); - //camera.release(); - //camera = null; - } -} diff --git a/catroid/src/main/java/org/catrobat/catroid/stage/OrthoCamController.java b/catroid/src/main/java/org/catrobat/catroid/stage/OrthoCamController.java deleted file mode 100644 index 3ced979256d..00000000000 --- a/catroid/src/main/java/org/catrobat/catroid/stage/OrthoCamController.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2018 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.stage; - -import com.badlogic.gdx.InputAdapter; -import com.badlogic.gdx.graphics.OrthographicCamera; -import com.badlogic.gdx.math.Vector3; - -public class OrthoCamController extends InputAdapter { - final OrthographicCamera camera; - final Vector3 curr = new Vector3(); - final Vector3 last = new Vector3(-1, -1, -1); - final Vector3 delta = new Vector3(); - - public OrthoCamController(OrthographicCamera camera) { - this.camera = camera; - } - - @Override - public boolean touchDragged(int x, int y, int pointer) { - camera.unproject(curr.set(x, y, 0)); - if (!(last.x == -1 && last.y == -1 && last.z == -1)) { - camera.unproject(delta.set(last.x, last.y, 0)); - delta.sub(curr); - camera.position.add(delta.x, delta.y, 0); - } - last.set(x, y, 0); - return false; - } - - @Override - public boolean touchUp(int x, int y, int pointer, int button) { - last.set(-1, -1, -1); - return false; - } -} diff --git a/catroid/src/main/java/org/catrobat/catroid/stage/ScreenshotSaver.kt b/catroid/src/main/java/org/catrobat/catroid/stage/ScreenshotSaver.kt index b3b45ee9895..672a3269935 100644 --- a/catroid/src/main/java/org/catrobat/catroid/stage/ScreenshotSaver.kt +++ b/catroid/src/main/java/org/catrobat/catroid/stage/ScreenshotSaver.kt @@ -30,6 +30,7 @@ import com.badlogic.gdx.Files import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import org.catrobat.catroid.ProjectManager import org.catrobat.catroid.common.Constants import java.io.File import java.io.IOException @@ -105,6 +106,15 @@ class ScreenshotSaver( File(folder + Constants.NO_MEDIA_FILE).createNewFile() fullScreenBitmap.compress(Bitmap.CompressFormat.PNG, IMAGE_QUALITY, streamScene) streamScene.close() + + if (ProjectManager.getInstance().currentProject != null) { + val projectFolder = ProjectManager.getInstance().currentProject.directory.absolutePath + "/" + val imageProject = gdxFileHandler.absolute(projectFolder + fileName) + val streamProject = imageProject.write(false) + File(projectFolder + Constants.NO_MEDIA_FILE).createNewFile() + fullScreenBitmap.compress(Bitmap.CompressFormat.PNG, IMAGE_QUALITY, streamProject) + streamProject.close() + } } catch (e: IOException) { Log.w(TAG, "Could not save screenshot to file", e) return false diff --git a/catroid/src/main/java/org/catrobat/catroid/stage/StageActivity.java b/catroid/src/main/java/org/catrobat/catroid/stage/StageActivity.java index 3229eb80ea0..005a2c4094b 100644 --- a/catroid/src/main/java/org/catrobat/catroid/stage/StageActivity.java +++ b/catroid/src/main/java/org/catrobat/catroid/stage/StageActivity.java @@ -24,6 +24,8 @@ import android.app.Activity; import android.app.PendingIntent; +import android.content.ClipData; +import android.content.ClipboardManager; import android.content.Intent; import android.nfc.NdefMessage; import android.nfc.NfcAdapter; @@ -46,6 +48,7 @@ import org.catrobat.catroid.ProjectManager; import org.catrobat.catroid.R; import org.catrobat.catroid.bluetooth.base.BluetoothDeviceService; +import org.catrobat.catroid.camera.CameraManager; import org.catrobat.catroid.common.CatroidService; import org.catrobat.catroid.common.ScreenValues; import org.catrobat.catroid.common.ServiceProvider; @@ -56,7 +59,6 @@ import org.catrobat.catroid.devices.raspberrypi.RaspberryPiService; import org.catrobat.catroid.drone.jumpingsumo.JumpingSumoDeviceController; import org.catrobat.catroid.drone.jumpingsumo.JumpingSumoInitializer; -import org.catrobat.catroid.facedetection.FaceDetectionHandler; import org.catrobat.catroid.io.StageAudioFocus; import org.catrobat.catroid.nfc.NfcHandler; import org.catrobat.catroid.ui.MarketingActivity; @@ -67,7 +69,6 @@ import org.catrobat.catroid.ui.runtimepermissions.PermissionHandlingActivity; import org.catrobat.catroid.ui.runtimepermissions.PermissionRequestActivityExtension; import org.catrobat.catroid.ui.runtimepermissions.RequiresPermissionTask; -import org.catrobat.catroid.utils.FlashUtil; import org.catrobat.catroid.utils.ScreenValueHandler; import org.catrobat.catroid.utils.ToastUtil; import org.catrobat.catroid.utils.VibrationUtil; @@ -80,7 +81,7 @@ import androidx.annotation.NonNull; import androidx.test.espresso.idling.CountingIdlingResource; -import static org.catrobat.catroid.stage.StageListener.SCREENSHOT_AUTOMATIC_FILE_NAME; +import static org.catrobat.catroid.common.Constants.SCREENSHOT_AUTOMATIC_FILE_NAME; import static org.catrobat.catroid.stage.TestResult.TEST_RESULT_MESSAGE; public class StageActivity extends AndroidApplication implements PermissionHandlingActivity, PermissionAdaptingActivity { @@ -107,6 +108,7 @@ public class StageActivity extends AndroidApplication implements PermissionHandl public static Handler messageHandler; JumpingSumoDeviceController jumpingSumoDeviceController; + CameraManager cameraManager; public static SparseArray intentListeners = new SparseArray<>(); public static Random randomGenerator = new Random(); @@ -209,9 +211,6 @@ public void onBackPressed() { } TextToSpeechHolder.getInstance().deleteSpeechFiles(); - if (FlashUtil.isAvailable()) { - FlashUtil.destroy(); - } if (VibrationUtil.isActive()) { VibrationUtil.destroy(); } @@ -238,10 +237,6 @@ public void manageLoadAndFinish() { service.pause(); } - if (FaceDetectionHandler.isFaceDetectionRunning()) { - FaceDetectionHandler.stopFaceDetection(); - } - if (VibrationUtil.isActive()) { VibrationUtil.pauseVibration(); } @@ -258,6 +253,17 @@ public boolean jumpingSumoDisconnect() { return success; } + public static CameraManager getActiveCameraManager() { + if (activeStageActivity != null) { + return activeStageActivity.get().cameraManager; + } + return null; + } + + public CameraManager getCameraManager() { + return cameraManager; + } + public boolean getResizePossible() { return resizePossible; } @@ -388,6 +394,16 @@ private void startQueuedIntent(int intentKey) { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == TestResult.STAGE_ACTIVITY_TEST_SUCCESS + || resultCode == TestResult.STAGE_ACTIVITY_TEST_FAIL) { + String message = data.getStringExtra(TEST_RESULT_MESSAGE); + ToastUtil.showError(this, message); + ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); + ClipData testResult = ClipData.newPlainText("TestResult", + ProjectManager.getInstance().getCurrentProject().getName() + "\n" + message); + clipboard.setPrimaryClip(testResult); + } + //Register your intent with "queueIntent" if (intentListeners.indexOfKey(requestCode) >= 0) { IntentListener asker = intentListeners.get(requestCode); @@ -455,6 +471,13 @@ private static void startStageActivity(Activity activity) { activity.startActivityForResult(intent, StageActivity.REQUEST_START_STAGE); } + public static void finishStage() { + StageActivity stageActivity = StageActivity.activeStageActivity.get(); + if (stageActivity != null && !stageActivity.isFinishing()) { + stageActivity.finish(); + } + } + public static void finishTestWithResult(TestResult testResult) { StageActivity stageActivity = StageActivity.activeStageActivity.get(); if (stageActivity != null && !stageActivity.isFinishing()) { diff --git a/catroid/src/main/java/org/catrobat/catroid/stage/StageLifeCycleController.java b/catroid/src/main/java/org/catrobat/catroid/stage/StageLifeCycleController.java index dab217aeafd..e3455b342fd 100644 --- a/catroid/src/main/java/org/catrobat/catroid/stage/StageLifeCycleController.java +++ b/catroid/src/main/java/org/catrobat/catroid/stage/StageLifeCycleController.java @@ -35,7 +35,6 @@ import org.catrobat.catroid.ProjectManager; import org.catrobat.catroid.R; import org.catrobat.catroid.bluetooth.base.BluetoothDeviceService; -import org.catrobat.catroid.camera.CameraManager; import org.catrobat.catroid.cast.CastManager; import org.catrobat.catroid.common.CatroidService; import org.catrobat.catroid.common.ServiceProvider; @@ -43,14 +42,12 @@ import org.catrobat.catroid.content.Sprite; import org.catrobat.catroid.content.bricks.Brick; import org.catrobat.catroid.devices.mindstorms.MindstormsException; -import org.catrobat.catroid.facedetection.FaceDetectionHandler; import org.catrobat.catroid.formulaeditor.SensorHandler; import org.catrobat.catroid.formulaeditor.UserDataWrapper; import org.catrobat.catroid.io.SoundManager; import org.catrobat.catroid.io.StageAudioFocus; import org.catrobat.catroid.ui.dialogs.StageDialog; import org.catrobat.catroid.ui.runtimepermissions.RequiresPermissionTask; -import org.catrobat.catroid.utils.FlashUtil; import org.catrobat.catroid.utils.VibrationUtil; import java.util.List; @@ -111,6 +108,7 @@ static void stageCreate(final StageActivity stageActivity) { if (stageActivity.getGdxGraphics().getView() instanceof SurfaceView) { SurfaceView glView = (SurfaceView) stageActivity.getGdxGraphics().getView(); glView.getHolder().setFormat(PixelFormat.TRANSLUCENT); + glView.setZOrderOnTop(true); } stageActivity.stageAudioFocus = new StageAudioFocus(stageActivity); stageActivity.stageResourceHolder = new StageResourceHolder(stageActivity); @@ -140,12 +138,10 @@ static void stagePause(final StageActivity stageActivity) { SoundManager.getInstance().pause(); StageActivity.stageListener.menuPause(); stageActivity.stageAudioFocus.releaseAudioFocus(); - if (CameraManager.getInstance() != null) { - FlashUtil.pauseFlash(); - FaceDetectionHandler.pauseFaceDetection(); - CameraManager.getInstance().pausePreview(); - CameraManager.getInstance().releaseCamera(); + if (stageActivity.cameraManager != null) { + stageActivity.cameraManager.pause(); } + BluetoothDeviceService bluetoothDeviceService = ServiceProvider.getService(CatroidService.BLUETOOTH_DEVICE_SERVICE); if (bluetoothDeviceService != null) { @@ -182,18 +178,10 @@ public static void stageResume(final StageActivity stageActivity) { } } - if (resourcesSet.contains(Brick.CAMERA_FLASH)) { - FlashUtil.resumeFlash(); - } - if (resourcesSet.contains(Brick.VIBRATION)) { VibrationUtil.resumeVibration(); } - if (resourcesSet.contains(Brick.FACE_DETECTION)) { - FaceDetectionHandler.resumeFaceDetection(); - } - if (resourcesSet.contains(Brick.BLUETOOTH_LEGO_NXT) || resourcesSet.contains(Brick.BLUETOOTH_PHIRO) || resourcesSet.contains(Brick.BLUETOOTH_SENSORS_ARDUINO)) { @@ -204,10 +192,8 @@ public static void stageResume(final StageActivity stageActivity) { } } - if (resourcesSet.contains(Brick.CAMERA_BACK) - || resourcesSet.contains(Brick.CAMERA_FRONT) - || resourcesSet.contains(Brick.VIDEO)) { - CameraManager.getInstance().resumePreviewAsync(); + if (stageActivity.cameraManager != null) { + stageActivity.cameraManager.resume(); } if (resourcesSet.contains(Brick.TEXT_TO_SPEECH)) { @@ -223,10 +209,6 @@ public static void stageResume(final StageActivity stageActivity) { CastManager.getInstance().resumeRemoteLayoutFromPauseScreen(); } - if (CameraManager.getInstance() != null) { - FaceDetectionHandler.resumeFaceDetection(); - } - SoundManager.getInstance().resume(); if (stageActivity.stageResourceHolder.initFinished()) { StageActivity.stageListener.menuResume(); @@ -249,14 +231,11 @@ static void stageDestroy(StageActivity stageActivity) { service.destroy(); } VibrationUtil.destroy(); - SensorHandler.stopSensorListeners(); - if (CameraManager.getInstance() != null) { - FlashUtil.destroy(); - FaceDetectionHandler.stopFaceDetection(); - CameraManager.getInstance().stopPreviewAsync(); - CameraManager.getInstance().releaseCamera(); - CameraManager.getInstance().setToDefaultCamera(); + if (stageActivity.cameraManager != null) { + stageActivity.cameraManager.destroy(); + stageActivity.cameraManager = null; } + SensorHandler.stopSensorListeners(); if (ProjectManager.getInstance().getCurrentProject().isCastProject()) { CastManager.getInstance().onStageDestroyed(); } diff --git a/catroid/src/main/java/org/catrobat/catroid/stage/StageListener.java b/catroid/src/main/java/org/catrobat/catroid/stage/StageListener.java index 75389cf078e..79059541500 100644 --- a/catroid/src/main/java/org/catrobat/catroid/stage/StageListener.java +++ b/catroid/src/main/java/org/catrobat/catroid/stage/StageListener.java @@ -66,7 +66,6 @@ import org.catrobat.catroid.content.eventids.GamepadEventId; import org.catrobat.catroid.embroidery.DSTPatternManager; import org.catrobat.catroid.embroidery.EmbroideryPatternManager; -import org.catrobat.catroid.facedetection.FaceDetectionHandler; import org.catrobat.catroid.formulaeditor.UserDataWrapper; import org.catrobat.catroid.io.SoundManager; import org.catrobat.catroid.physics.PhysicsDebugSettings; @@ -76,7 +75,6 @@ import org.catrobat.catroid.physics.shapebuilder.PhysicsShapeBuilder; import org.catrobat.catroid.ui.dialogs.StageDialog; import org.catrobat.catroid.ui.recyclerview.controller.SpriteController; -import org.catrobat.catroid.utils.FlashUtil; import org.catrobat.catroid.utils.TouchUtil; import org.catrobat.catroid.utils.VibrationUtil; import org.catrobat.catroid.web.WebConnectionHolder; @@ -92,7 +90,6 @@ import androidx.annotation.VisibleForTesting; import kotlinx.coroutines.GlobalScope; -import static org.catrobat.catroid.common.Constants.DEFAULT_IMAGE_EXTENSION; import static org.catrobat.catroid.common.ScreenValues.SCREEN_HEIGHT; import static org.catrobat.catroid.common.ScreenValues.SCREEN_WIDTH; @@ -104,8 +101,6 @@ public class StageListener implements ApplicationListener { private static final float AXIS_FONT_SIZE_SCALE_FACTOR = 0.025f; private float deltaActionTimeDivisor = 10f; - public static final String SCREENSHOT_AUTOMATIC_FILE_NAME = "automatic_screenshot" + DEFAULT_IMAGE_EXTENSION; - public static final String SCREENSHOT_MANUAL_FILE_NAME = "manual_screenshot" + DEFAULT_IMAGE_EXTENSION; private Stage stage = null; private boolean paused = true; @@ -208,7 +203,6 @@ public void create() { stage.addActor(passepartout); axes = new Texture(Gdx.files.internal("stage/red_pixel.bmp")); - FaceDetectionHandler.resumeFaceDetection(); } public void setPaused(boolean paused) { @@ -442,7 +436,10 @@ public void reloadProject(StageDialog stageDialog) { stageBackupMap.clear(); embroideryPatternManager.clear(); - FlashUtil.reset(); + CameraManager cameraManager = StageActivity.getActiveCameraManager(); + if (cameraManager != null) { + cameraManager.reset(); + } VibrationUtil.reset(); TouchUtil.reset(); removeAllClonedSpritesFromStage(); @@ -459,7 +456,6 @@ public void reloadProject(StageDialog stageDialog) { public void resume() { if (!paused) { setSchedulerStateForAllLooks(ThreadScheduler.RUNNING); - FaceDetectionHandler.resumeFaceDetection(); SoundManager.getInstance().resume(); VibrationUtil.resumeVibration(); } @@ -476,7 +472,6 @@ public void pause() { } if (!paused) { setSchedulerStateForAllLooks(ThreadScheduler.SUSPENDED); - FaceDetectionHandler.pauseFaceDetection(); SoundManager.getInstance().pause(); VibrationUtil.pauseVibration(); } @@ -484,10 +479,15 @@ public void pause() { @Override public void render() { - if (CameraManager.getInstance() != null && CameraManager.getInstance().getState() == CameraManager.CameraState.previewRunning) { + CameraManager cameraManager = StageActivity.getActiveCameraManager(); + boolean cameraVisible = cameraManager != null && cameraManager.getPreviewVisible(); + boolean hasBackground = ProjectManager.getInstance().getCurrentlyPlayingScene() + .getBackgroundSprite().getLookList().size() > 0; + + if (hasBackground || cameraVisible) { Gdx.gl20.glClearColor(0f, 0f, 0f, 0f); } else { - Gdx.gl20.glClearColor(1f, 1f, 1f, 0f); + Gdx.gl20.glClearColor(1f, 1f, 1f, 1f); } Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT); @@ -662,10 +662,6 @@ public void dispose() { } public void finish() { - if (CameraManager.getInstance() != null) { - CameraManager.getInstance().setToDefaultCamera(); - } - finished = true; } @@ -838,6 +834,7 @@ private class StageBackup { private StageBackup saveToBackup() { StageBackup backup = new StageBackup(); + CameraManager cameraManager = StageActivity.getActiveCameraManager(); backup.sprites = new ArrayList<>(sprites); backup.actors = new Array<>(stage.getActors()); @@ -848,11 +845,9 @@ private StageBackup saveToBackup() { backup.paused = paused; backup.finished = finished; backup.reloadProject = reloadProject; - if (CameraManager.getInstance() != null) { - backup.flashState = FlashUtil.isOn(); - if (backup.flashState) { - FlashUtil.flashOff(); - } + backup.flashState = cameraManager != null && cameraManager.getFlashOn(); + if (backup.flashState) { + cameraManager.disableFlash(); } backup.timeToVibrate = VibrationUtil.getTimeToVibrate(); backup.physicsWorld = physicsWorld; @@ -864,11 +859,9 @@ private StageBackup saveToBackup() { backup.axesOn = axesOn; backup.deltaActionTimeDivisor = deltaActionTimeDivisor; - if (CameraManager.getInstance() != null) { - backup.cameraRunning = CameraManager.getInstance().isCameraActive(); - if (backup.cameraRunning) { - CameraManager.getInstance().pauseForScene(); - } + backup.cameraRunning = cameraManager != null && cameraManager.isCameraActive(); + if (backup.cameraRunning) { + cameraManager.pause(); } backup.soundBackupList = new ArrayList<>(); backup.soundBackupList.addAll(SoundManager.getInstance().getPlayingSoundBackups()); @@ -878,6 +871,7 @@ private StageBackup saveToBackup() { private void restoreFromBackup(StageBackup backup) { sprites.clear(); sprites.addAll(backup.sprites); + CameraManager cameraManager = StageActivity.getActiveCameraManager(); stage.clear(); for (Actor actor : backup.actors) { @@ -894,8 +888,8 @@ private void restoreFromBackup(StageBackup backup) { paused = backup.paused; finished = backup.finished; reloadProject = backup.reloadProject; - if (CameraManager.getInstance() != null && backup.flashState) { - FlashUtil.flashOn(); + if (backup.flashState && cameraManager != null) { + cameraManager.enableFlash(); } if (backup.timeToVibrate > 0) { VibrationUtil.resumeVibration(); @@ -911,8 +905,8 @@ private void restoreFromBackup(StageBackup backup) { viewPort = backup.viewPort; axesOn = backup.axesOn; deltaActionTimeDivisor = backup.deltaActionTimeDivisor; - if (CameraManager.getInstance() != null && backup.cameraRunning) { - CameraManager.getInstance().resumeForScene(); + if (backup.cameraRunning && cameraManager != null) { + cameraManager.resume(); } for (SoundBackup soundBackup : backup.soundBackupList) { SoundManager.getInstance().playSoundFileWithStartTime(soundBackup.getPathToSoundFile(), diff --git a/catroid/src/main/java/org/catrobat/catroid/stage/StageResourceHolder.java b/catroid/src/main/java/org/catrobat/catroid/stage/StageResourceHolder.java index ce3f02a31a8..147fe41c8c8 100644 --- a/catroid/src/main/java/org/catrobat/catroid/stage/StageResourceHolder.java +++ b/catroid/src/main/java/org/catrobat/catroid/stage/StageResourceHolder.java @@ -60,13 +60,11 @@ import org.catrobat.catroid.drone.jumpingsumo.JumpingSumoDeviceController; import org.catrobat.catroid.drone.jumpingsumo.JumpingSumoInitializer; import org.catrobat.catroid.drone.jumpingsumo.JumpingSumoServiceWrapper; -import org.catrobat.catroid.facedetection.FaceDetectionHandler; import org.catrobat.catroid.formulaeditor.SensorHandler; import org.catrobat.catroid.formulaeditor.SensorLoudness; import org.catrobat.catroid.sensing.GatherCollisionInformationTask; import org.catrobat.catroid.ui.runtimepermissions.BrickResourcesToRuntimePermissions; import org.catrobat.catroid.ui.settingsfragments.SettingsFragment; -import org.catrobat.catroid.utils.FlashUtil; import org.catrobat.catroid.utils.ToastUtil; import org.catrobat.catroid.utils.TouchUtil; import org.catrobat.catroid.utils.Utils; @@ -281,8 +279,7 @@ public void onShow(DialogInterface dialog) { } if (requiredResourcesSet.contains(Brick.CAMERA_BACK)) { - CameraManager.makeInstance(); - if (CameraManager.getInstance().hasBackCamera()) { + if (getCameraManager().getHasBackCamera()) { resourceInitialized(); } else { resourceFailed(Brick.CAMERA_BACK); @@ -290,8 +287,7 @@ public void onShow(DialogInterface dialog) { } if (requiredResourcesSet.contains(Brick.CAMERA_FRONT)) { - CameraManager.makeInstance(); - if (CameraManager.getInstance().hasFrontCamera()) { + if (getCameraManager().getHasFrontCamera()) { resourceInitialized(); } else { resourceFailed(Brick.CAMERA_FRONT); @@ -299,9 +295,7 @@ public void onShow(DialogInterface dialog) { } if (requiredResourcesSet.contains(Brick.VIDEO)) { - CameraManager.makeInstance(); - if (CameraManager.getInstance().hasFrontCamera() - || CameraManager.getInstance().hasBackCamera()) { + if (getCameraManager().getHasFrontCamera() || getCameraManager().getHasBackCamera()) { resourceInitialized(); } else { resourceFailed(Brick.VIDEO); @@ -309,12 +303,11 @@ public void onShow(DialogInterface dialog) { } if (requiredResourcesSet.contains(Brick.CAMERA_FLASH)) { - CameraManager.makeInstance(); - if (CameraManager.getInstance().isCameraFlashAvailable()) { - CameraManager.getInstance().switchToCameraWithFlash(); + if (getCameraManager().getHasFlash()) { + resourceInitialized(); + } else { + resourceFailed(Brick.CAMERA_FLASH); } - FlashUtil.initializeFlash(); - resourceInitialized(); } if (requiredResourcesSet.contains(Brick.VIBRATION)) { @@ -346,11 +339,7 @@ public void onClick(DialogInterface dialog, int id) { } if (requiredResourcesSet.contains(Brick.FACE_DETECTION)) { - CameraManager.makeInstance(); - FaceDetectionHandler.resetFaceDedection(); - - boolean success = FaceDetectionHandler.startFaceDetection(); - if (success) { + if (getCameraManager().startFaceDetection()) { resourceInitialized(); } else { resourceFailed(Brick.FACE_DETECTION); @@ -361,7 +350,6 @@ public void onClick(DialogInterface dialog, int id) { if (CastManager.getInstance().isConnected()) { resourceInitialized(); } else { - if (!SettingsFragment.isCastSharedPreferenceEnabled(stageActivity)) { ToastUtil.showError(stageActivity, stageActivity.getString(R.string.cast_enable_cast_feature)); } else if (ProjectManager.getInstance().getCurrentProject().isCastProject()) { @@ -443,9 +431,6 @@ public void initFinishedRunStage() { } stageActivity.nfcAdapter = NfcAdapter.getDefaultAdapter(stageActivity); - if (CameraManager.getInstance() != null) { - CameraManager.getInstance().setStageActivity(stageActivity); - } StageActivity.stageListener.setPaused(false); } @@ -458,7 +443,6 @@ public synchronized void resourceFailed(int failedResource) { public synchronized void resourceInitialized() { requiredResourceCounter--; if (requiredResourceCounter == 0) { - if (failedResources.isEmpty()) { initFinishedRunStage(); } else { @@ -467,6 +451,13 @@ public synchronized void resourceInitialized() { } } + private CameraManager getCameraManager() { + if (stageActivity.cameraManager == null) { + stageActivity.cameraManager = new CameraManager(stageActivity); + } + return stageActivity.cameraManager; + } + public void endStageActivity() { Intent returnToActivityIntent = new Intent(); stageActivity.setResult(RESULT_CANCELED, returnToActivityIntent); @@ -506,13 +497,21 @@ private void showResourceFailedErrorDialog() { failedResourcesMessage = failedResourcesMessage + stageActivity.getString(R.string .prestage_no_front_camera_available); break; + case Brick.VIDEO: + failedResourcesMessage = failedResourcesMessage + stageActivity.getString(R.string + .prestage_no_camera_available); + break; + case Brick.CAMERA_FLASH: + failedResourcesMessage = failedResourcesMessage + stageActivity.getString(R.string + .prestage_no_flash_available); + break; case Brick.VIBRATION: failedResourcesMessage = failedResourcesMessage + stageActivity.getString(R.string .prestage_no_vibration_available); break; case Brick.FACE_DETECTION: failedResourcesMessage = failedResourcesMessage + stageActivity.getString(R.string - .prestage_no_camera_available); + .prestage_no_face_detection_available); break; case Brick.JUMPING_SUMO: failedResourcesMessage = failedResourcesMessage + stageActivity.getString(R.string @@ -525,32 +524,22 @@ private void showResourceFailedErrorDialog() { } } - AlertDialog.Builder failedResourceAlertBuilder = new AlertDialog.Builder(stageActivity); - failedResourceAlertBuilder.setTitle(R.string.prestage_resource_not_available_title); - failedResourceAlertBuilder.setMessage(failedResourcesMessage).setCancelable(false) - .setPositiveButton(stageActivity.getString(R.string.ok), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - endStageActivity(); - } - }); - AlertDialog alert = failedResourceAlertBuilder.create(); - alert.show(); + new AlertDialog.Builder(new ContextThemeWrapper(stageActivity, R.style.Theme_AppCompat_Dialog)) + .setTitle(R.string.prestage_resource_not_available_title) + .setMessage(failedResourcesMessage).setCancelable(false) + .setPositiveButton(stageActivity.getString(R.string.ok), (dialog, id) -> endStageActivity()) + .create() + .show(); } public void showResourceInUseErrorDialog() { String failedResourcesMessage = stageActivity.getString(R.string.prestage_resource_in_use_text); - AlertDialog.Builder failedResourceAlertBuilder = new AlertDialog.Builder(stageActivity); - failedResourceAlertBuilder.setTitle(R.string.prestage_resource_not_available_title); - failedResourceAlertBuilder.setMessage(failedResourcesMessage).setCancelable(false) - .setPositiveButton(stageActivity.getString(R.string.ok), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - endStageActivity(); - } - }); - AlertDialog alert = failedResourceAlertBuilder.create(); - alert.show(); + new AlertDialog.Builder(new ContextThemeWrapper(stageActivity, R.style.Theme_AppCompat_Dialog)) + .setTitle(R.string.prestage_resource_not_available_title) + .setMessage(failedResourcesMessage).setCancelable(false) + .setPositiveButton(stageActivity.getString(R.string.ok), (dialog, id) -> endStageActivity()) + .create() + .show(); } public void onActivityResult(int requestCode, int resultCode, Intent data) { diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/ProjectUploadActivity.java b/catroid/src/main/java/org/catrobat/catroid/ui/ProjectUploadActivity.java index 764f928ddfa..04233202cc1 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/ProjectUploadActivity.java +++ b/catroid/src/main/java/org/catrobat/catroid/ui/ProjectUploadActivity.java @@ -145,11 +145,11 @@ public void loadProjectActivity() { } private void onCreateView() { - int thumbnailWidth = getResources().getDimensionPixelSize(R.dimen.project_thumbnail_width); - int thumbnailHeight = getResources().getDimensionPixelSize(R.dimen.project_thumbnail_height); - ProjectAndSceneScreenshotLoader screenshotLoader = new ProjectAndSceneScreenshotLoader(thumbnailWidth, thumbnailHeight); + int thumbnailSize = 100; + ProjectAndSceneScreenshotLoader screenshotLoader = new ProjectAndSceneScreenshotLoader(thumbnailSize, + thumbnailSize); screenshotLoader.loadAndShowScreenshot(project.getDirectory().getName(), - project.getDirectory().getName(), + screenshotLoader.getScreenshotSceneName(project.getDirectory()), false, findViewById(R.id.project_image_view)); diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/SpriteActivity.java b/catroid/src/main/java/org/catrobat/catroid/ui/SpriteActivity.java index 0d398f51da9..6f419db97b6 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/SpriteActivity.java +++ b/catroid/src/main/java/org/catrobat/catroid/ui/SpriteActivity.java @@ -22,6 +22,8 @@ */ package org.catrobat.catroid.ui; +import android.content.ClipData; +import android.content.ClipboardManager; import android.content.Intent; import android.net.Uri; import android.os.Bundle; @@ -51,17 +53,20 @@ import org.catrobat.catroid.pocketmusic.PocketMusicActivity; import org.catrobat.catroid.soundrecorder.SoundRecorderActivity; import org.catrobat.catroid.stage.StageActivity; +import org.catrobat.catroid.stage.TestResult; import org.catrobat.catroid.ui.fragment.FormulaEditorFragment; import org.catrobat.catroid.ui.recyclerview.dialog.TextInputDialog; import org.catrobat.catroid.ui.recyclerview.dialog.dialoginterface.NewItemInterface; import org.catrobat.catroid.ui.recyclerview.dialog.textwatcher.NewItemTextWatcher; import org.catrobat.catroid.ui.recyclerview.fragment.DataListFragment; +import org.catrobat.catroid.ui.recyclerview.fragment.ListSelectorFragment; import org.catrobat.catroid.ui.recyclerview.fragment.LookListFragment; import org.catrobat.catroid.ui.recyclerview.fragment.NfcTagListFragment; import org.catrobat.catroid.ui.recyclerview.fragment.ScriptFragment; import org.catrobat.catroid.ui.recyclerview.fragment.SoundListFragment; import org.catrobat.catroid.ui.recyclerview.util.UniqueNameProvider; import org.catrobat.catroid.ui.settingsfragments.SettingsFragment; +import org.catrobat.catroid.utils.ToastUtil; import java.io.File; import java.io.IOException; @@ -82,6 +87,7 @@ import static org.catrobat.catroid.common.FlavoredConstants.LIBRARY_BACKGROUNDS_URL_PORTRAIT; import static org.catrobat.catroid.common.FlavoredConstants.LIBRARY_LOOKS_URL; import static org.catrobat.catroid.common.FlavoredConstants.LIBRARY_SOUNDS_URL; +import static org.catrobat.catroid.stage.TestResult.TEST_RESULT_MESSAGE; import static org.catrobat.catroid.ui.WebViewActivity.MEDIA_FILE_PATH; import static org.catrobat.catroid.visualplacement.VisualPlacementActivity.X_COORDINATE_BUNDLE_ARGUMENT; import static org.catrobat.catroid.visualplacement.VisualPlacementActivity.Y_COORDINATE_BUNDLE_ARGUMENT; @@ -269,6 +275,16 @@ private void saveProject() { public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); + if (resultCode == TestResult.STAGE_ACTIVITY_TEST_SUCCESS + || resultCode == TestResult.STAGE_ACTIVITY_TEST_FAIL) { + String message = data.getStringExtra(TEST_RESULT_MESSAGE); + ToastUtil.showError(this, message); + ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); + ClipData testResult = ClipData.newPlainText("TestResult", + ProjectManager.getInstance().getCurrentProject().getName() + "\n" + message); + clipboard.setPrimaryClip(testResult); + } + if (resultCode != RESULT_OK) { if (SettingsFragment.isCastSharedPreferenceEnabled(this) && projectManager.getCurrentProject().isCastProject() @@ -550,6 +566,9 @@ public void handleAddButton(View view) { if (getCurrentFragment() instanceof SoundListFragment) { handleAddSoundButton(); } + if (getCurrentFragment() instanceof ListSelectorFragment) { + handleAddUserListButton(); + } } public void handleAddSpriteButton() { @@ -776,6 +795,40 @@ public void handleAddUserDataButton() { alertDialog.show(); } + public void handleAddUserListButton() { + View view = View.inflate(this, R.layout.dialog_new_user_data, null); + RadioButton addToProjectUserDataRadioButton = view.findViewById(R.id.global); + + List lists = new ArrayList<>(); + lists.addAll(currentProject.getUserLists()); + lists.addAll(currentSprite.getUserLists()); + + NewItemTextWatcher textWatcher = new NewItemTextWatcher<>(lists); + + TextInputDialog.Builder builder = new TextInputDialog.Builder(this) + .setTextWatcher(textWatcher) + .setPositiveButton(getString(R.string.ok), (TextInputDialog.OnClickListener) (dialog, textInput) -> { + boolean addToProjectUserData = addToProjectUserDataRadioButton.isChecked(); + + UserList userList = new UserList(textInput); + if (addToProjectUserData) { + currentProject.addUserList(userList); + } else { + currentSprite.addUserList(userList); + } + + if (getCurrentFragment() instanceof ListSelectorFragment) { + ((ListSelectorFragment) getCurrentFragment()).notifyDataSetChanged(); + } + }); + + final AlertDialog alertDialog = builder.setTitle(R.string.formula_editor_list_dialog_title) + .setView(view) + .create(); + + alertDialog.show(); + } + public void handlePlayButton(View view) { Fragment currentFragment = getCurrentFragment(); if (currentFragment instanceof ScriptFragment) { diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/SpriteAttributesActivity.java b/catroid/src/main/java/org/catrobat/catroid/ui/SpriteAttributesActivity.java index 56694750bea..02849dff2a9 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/SpriteAttributesActivity.java +++ b/catroid/src/main/java/org/catrobat/catroid/ui/SpriteAttributesActivity.java @@ -22,6 +22,8 @@ */ package org.catrobat.catroid.ui; +import android.content.ClipData; +import android.content.ClipboardManager; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; @@ -34,14 +36,17 @@ import org.catrobat.catroid.BuildConfig; import org.catrobat.catroid.ProjectManager; import org.catrobat.catroid.R; +import org.catrobat.catroid.cast.CastManager; import org.catrobat.catroid.content.Scene; import org.catrobat.catroid.content.Sprite; import org.catrobat.catroid.stage.StageActivity; +import org.catrobat.catroid.stage.TestResult; import org.catrobat.catroid.ui.recyclerview.RVButton; import org.catrobat.catroid.ui.recyclerview.adapter.ButtonAdapter; import org.catrobat.catroid.ui.recyclerview.dialog.TextInputDialog; import org.catrobat.catroid.ui.recyclerview.dialog.textwatcher.RenameItemTextWatcher; import org.catrobat.catroid.ui.settingsfragments.SettingsFragment; +import org.catrobat.catroid.utils.ToastUtil; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -54,6 +59,9 @@ import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.RecyclerView; +import static org.catrobat.catroid.stage.TestResult.TEST_RESULT_MESSAGE; +import static org.catrobat.catroid.ui.settingsfragments.SettingsFragment.isCastSharedPreferenceEnabled; + public class SpriteAttributesActivity extends BaseActivity implements ButtonAdapter.OnItemClickListener { @Retention(RetentionPolicy.SOURCE) @@ -90,6 +98,31 @@ public void onCreate(Bundle savedInstanceState) { updateActionBarTitle(); } + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (resultCode == TestResult.STAGE_ACTIVITY_TEST_SUCCESS + || resultCode == TestResult.STAGE_ACTIVITY_TEST_FAIL) { + String message = data.getStringExtra(TEST_RESULT_MESSAGE); + ToastUtil.showError(this, message); + ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); + ClipData testResult = ClipData.newPlainText("TestResult", + ProjectManager.getInstance().getCurrentProject().getName() + "\n" + message); + clipboard.setPrimaryClip(testResult); + } + + if (resultCode != RESULT_OK) { + if (isCastSharedPreferenceEnabled(this) + && ProjectManager.getInstance().getCurrentProject().isCastProject() + && !CastManager.getInstance().isConnected()) { + + CastManager.getInstance().openDeviceSelectorOrDisconnectDialog(this); + } + return; + } + } + private void updateActionBarTitle() { Scene currentScene = ProjectManager.getInstance().getCurrentlyEditedScene(); Sprite currentSprite = ProjectManager.getInstance().getCurrentSprite(); diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/dialogs/FormulaEditorComputeDialog.java b/catroid/src/main/java/org/catrobat/catroid/ui/dialogs/FormulaEditorComputeDialog.java index 239596b2a17..9f2389c78a3 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/dialogs/FormulaEditorComputeDialog.java +++ b/catroid/src/main/java/org/catrobat/catroid/ui/dialogs/FormulaEditorComputeDialog.java @@ -35,12 +35,10 @@ import org.catrobat.catroid.R; import org.catrobat.catroid.bluetooth.base.BluetoothDevice; import org.catrobat.catroid.bluetooth.base.BluetoothDeviceService; -import org.catrobat.catroid.camera.CameraManager; import org.catrobat.catroid.common.CatroidService; import org.catrobat.catroid.common.ServiceProvider; import org.catrobat.catroid.content.Sprite; import org.catrobat.catroid.content.bricks.Brick; -import org.catrobat.catroid.facedetection.FaceDetectionHandler; import org.catrobat.catroid.formulaeditor.Formula; import org.catrobat.catroid.formulaeditor.FormulaElement.ElementType; import org.catrobat.catroid.formulaeditor.SensorHandler; @@ -87,11 +85,6 @@ public void setFormula(Formula formula) { SensorHandler.getInstance(getContext()).setSensorLoudness(new SensorLoudness()); } - if (resourcesSet.contains(Brick.FACE_DETECTION)) { - CameraManager.makeInstance(); - FaceDetectionHandler.startFaceDetection(); - } - if (resourcesSet.contains(Brick.BLUETOOTH_LEGO_NXT)) { BluetoothDeviceService btService = ServiceProvider.getService(CatroidService.BLUETOOTH_DEVICE_SERVICE); btService.connectDevice(BluetoothDevice.LEGO_NXT, this.getContext()); @@ -121,10 +114,7 @@ public void setFormula(Formula formula) { @Override protected void onStop() { SensorHandler.unregisterListener(this); - ServiceProvider.getService(CatroidService.BLUETOOTH_DEVICE_SERVICE).pause(); - - FaceDetectionHandler.stopFaceDetection(); super.onStop(); } diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/dialogs/StageDialog.java b/catroid/src/main/java/org/catrobat/catroid/ui/dialogs/StageDialog.java index 9c5315fbf20..bbd6906193e 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/dialogs/StageDialog.java +++ b/catroid/src/main/java/org/catrobat/catroid/ui/dialogs/StageDialog.java @@ -48,7 +48,7 @@ import java.io.File; import java.io.IOException; -import static org.catrobat.catroid.stage.StageListener.SCREENSHOT_MANUAL_FILE_NAME; +import static org.catrobat.catroid.common.Constants.SCREENSHOT_MANUAL_FILE_NAME; public class StageDialog extends Dialog implements View.OnClickListener { private static final String TAG = StageDialog.class.getSimpleName(); diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/fragment/AddUserDefinedBrickFragment.java b/catroid/src/main/java/org/catrobat/catroid/ui/fragment/AddUserDefinedBrickFragment.java index fab0a1a5dcc..a5d10ecc89c 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/fragment/AddUserDefinedBrickFragment.java +++ b/catroid/src/main/java/org/catrobat/catroid/ui/fragment/AddUserDefinedBrickFragment.java @@ -148,7 +148,7 @@ public boolean onOptionsItemSelected(MenuItem item) { userDefinedBrick.addLabel(""); } - if (Sprite.doesUserBrickAlreadyExist(userDefinedBrick, currentSprite)) { + if (currentSprite.doesUserBrickAlreadyExist(userDefinedBrick)) { ToastUtil.showErrorWithColor(getContext(), R.string.brick_user_defined_already_exists, Color.RED); if (brickIsEmpty) { userDefinedBrick.removeLastLabel(); diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/fragment/BrickCategoryFragment.java b/catroid/src/main/java/org/catrobat/catroid/ui/fragment/BrickCategoryFragment.java index 62126ff20d8..deb94b00e76 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/fragment/BrickCategoryFragment.java +++ b/catroid/src/main/java/org/catrobat/catroid/ui/fragment/BrickCategoryFragment.java @@ -206,7 +206,7 @@ private void setupBrickCategories() { if (!onlyBeginnerBricks() && BuildConfig.FEATURE_USERBRICKS_ENABLED) { categories.add(inflater.inflate(R.layout.brick_category_userbrick, null)); } - if (BuildConfig.DEBUG) { + if (SettingsFragment.isTestSharedPreferenceEnabled(getActivity())) { categories.add(inflater.inflate(R.layout.brick_category_assert, null)); } diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/fragment/CategoryBricksFactory.java b/catroid/src/main/java/org/catrobat/catroid/ui/fragment/CategoryBricksFactory.java index 1d9f277109e..0636ffc62f0 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/fragment/CategoryBricksFactory.java +++ b/catroid/src/main/java/org/catrobat/catroid/ui/fragment/CategoryBricksFactory.java @@ -81,6 +81,7 @@ import org.catrobat.catroid.content.bricks.DroneTakeOffLandBrick; import org.catrobat.catroid.content.bricks.DroneTurnLeftBrick; import org.catrobat.catroid.content.bricks.DroneTurnRightBrick; +import org.catrobat.catroid.content.bricks.ExitStageBrick; import org.catrobat.catroid.content.bricks.FinishStageBrick; import org.catrobat.catroid.content.bricks.FlashBrick; import org.catrobat.catroid.content.bricks.ForVariableFromToBrick; @@ -118,6 +119,8 @@ import org.catrobat.catroid.content.bricks.MoveNStepsBrick; import org.catrobat.catroid.content.bricks.NextLookBrick; import org.catrobat.catroid.content.bricks.NoteBrick; +import org.catrobat.catroid.content.bricks.ParameterizedBrick; +import org.catrobat.catroid.content.bricks.ParameterizedEndBrick; import org.catrobat.catroid.content.bricks.PenDownBrick; import org.catrobat.catroid.content.bricks.PenUpBrick; import org.catrobat.catroid.content.bricks.PhiroIfLogicBeginBrick; @@ -344,6 +347,7 @@ protected List setupControlCategoryList(Context context) { controlBrickList.add(new PhiroIfLogicBeginBrick()); } + controlBrickList.add(new ExitStageBrick()); controlBrickList.add(new StopScriptBrick(BrickValues.STOP_THIS_SCRIPT)); controlBrickList.add(new CloneBrick()); @@ -700,10 +704,10 @@ private List setupAssertionsCategoryList(Context context) { assertionsBrickList.add(new AssertEqualsBrick()); assertionsBrickList.add(new AssertUserListsBrick()); + assertionsBrickList.add(new ParameterizedBrick()); assertionsBrickList.add(new WaitTillIdleBrick()); assertionsBrickList.add(new TapAtBrick()); assertionsBrickList.add(new FinishStageBrick()); - assertionsBrickList.add(new StoreCSVIntoUserListBrick(BrickValues.STORE_CSV_INTO_USERLIST_COLUMN, context.getString(R.string.brick_store_csv_into_userlist_data))); @@ -850,6 +854,8 @@ public String getBrickCategory(Brick brick, boolean isBackgroundSprite, Context category = res.getString(R.string.category_user_bricks); } else if (brick instanceof UserDefinedReceiverBrick) { category = res.getString(R.string.category_user_bricks); + } else if (brick instanceof ParameterizedEndBrick) { + category = res.getString(R.string.category_assertions); } config.locale = savedLocale; diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/fragment/ProjectDetailsFragment.java b/catroid/src/main/java/org/catrobat/catroid/ui/fragment/ProjectDetailsFragment.java index 1d17476cded..aada8ff826d 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/fragment/ProjectDetailsFragment.java +++ b/catroid/src/main/java/org/catrobat/catroid/ui/fragment/ProjectDetailsFragment.java @@ -77,15 +77,15 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa getActivity().onBackPressed(); } - String sceneName = XstreamSerializer.extractDefaultSceneNameFromXml(projectData.getDirectory()); - int thumbnailWidth = getActivity().getResources().getDimensionPixelSize(R.dimen.project_thumbnail_width); int thumbnailHeight = getActivity().getResources().getDimensionPixelSize(R.dimen.project_thumbnail_height); ProjectAndSceneScreenshotLoader screenshotLoader = new ProjectAndSceneScreenshotLoader(thumbnailWidth, thumbnailHeight); XmlHeader header = project.getXmlHeader(); ImageView image = view.findViewById(R.id.image); - screenshotLoader.loadAndShowScreenshot(projectData.getName(), sceneName, false, image); + screenshotLoader.loadAndShowScreenshot(projectData.getName(), + screenshotLoader.getScreenshotSceneName(project.getDirectory()), false, + image); String size = FileMetaDataExtractor .getSizeAsString(new File(DEFAULT_ROOT_DIRECTORY, projectData.getName()), getActivity()); diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/adapter/BrickAdapter.java b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/adapter/BrickAdapter.java index 589d14594a6..7fbe7ec3397 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/adapter/BrickAdapter.java +++ b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/adapter/BrickAdapter.java @@ -35,6 +35,7 @@ import org.catrobat.catroid.content.Sprite; import org.catrobat.catroid.content.bricks.Brick; import org.catrobat.catroid.content.bricks.FormulaBrick; +import org.catrobat.catroid.content.bricks.ListSelectorBrick; import org.catrobat.catroid.content.bricks.ScriptBrick; import org.catrobat.catroid.content.bricks.UserDefinedReceiverBrick; import org.catrobat.catroid.ui.dragndrop.BrickAdapterInterface; @@ -136,6 +137,8 @@ public View getView(final int position, View convertView, final ViewGroup parent item.getCheckBox().setVisibility(View.GONE); if (item instanceof FormulaBrick) { ((FormulaBrick) item).setClickListeners(); + } else if (item instanceof ListSelectorBrick) { + ((ListSelectorBrick) item).setClickListeners(); } break; case ALL: diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/adapter/DataListAdapter.java b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/adapter/DataListAdapter.java index ea3517773d6..f2556d8e5b2 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/adapter/DataListAdapter.java +++ b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/adapter/DataListAdapter.java @@ -52,7 +52,9 @@ public class DataListAdapter extends RecyclerView.Adapter implement @Retention(RetentionPolicy.SOURCE) @IntDef({VAR_MULTIPLAYER, VAR_GLOBAL, VAR_LOCAL, LIST_GLOBAL, LIST_LOCAL}) - @interface DataType {} + @interface DataType { + } + private static final int VAR_MULTIPLAYER = 0; private static final int VAR_GLOBAL = 1; private static final int VAR_LOCAL = 2; @@ -100,7 +102,7 @@ public void onBindViewHolder(CheckableVH holder, int position) { }; globalVarAdapter.setSelectionListener(this); - localVarAdapter = new VariableRVAdapter(localVars){ + localVarAdapter = new VariableRVAdapter(localVars) { @Override public void onBindViewHolder(CheckableVH holder, int position) { super.onBindViewHolder(holder, position); @@ -108,6 +110,7 @@ public void onBindViewHolder(CheckableVH holder, int position) { ((TextView) holder.itemView.findViewById(R.id.headline)).setText(R.string.local_vars_headline); } } + @Override protected void onCheckBoxClick(int position) { super.onCheckBoxClick(getRelativeItemPosition(position, VAR_LOCAL)); @@ -123,6 +126,7 @@ public void onBindViewHolder(CheckableVH holder, int position) { ((TextView) holder.itemView.findViewById(R.id.headline)).setText(R.string.global_lists_headline); } } + @Override protected void onCheckBoxClick(int position) { super.onCheckBoxClick(getRelativeItemPosition(position, LIST_GLOBAL)); @@ -138,6 +142,7 @@ public void onBindViewHolder(CheckableVH holder, int position) { ((TextView) holder.itemView.findViewById(R.id.headline)).setText(R.string.local_lists_headline); } } + @Override protected void onCheckBoxClick(int position) { super.onCheckBoxClick(getRelativeItemPosition(position, LIST_LOCAL)); @@ -236,7 +241,8 @@ public CheckableVH onCreateViewHolder(@NonNull ViewGroup parent, @LayoutRes int } @Override - public @LayoutRes int getItemViewType(int position) { + public @LayoutRes + int getItemViewType(int position) { @DataType int dataType = getDataType(position); position = getRelativeItemPosition(position, dataType); @@ -307,6 +313,15 @@ public void clearSelection() { notifyDataSetChanged(); } + public void selectAll() { + multiplayerVarAdapter.selectAll(); + globalVarAdapter.selectAll(); + localVarAdapter.selectAll(); + globalListAdapter.selectAll(); + localListAdapter.selectAll(); + notifyDataSetChanged(); + } + public void remove(UserData item) { if (item instanceof UserVariable) { if (!globalVarAdapter.remove((UserVariable) item) && !localVarAdapter.remove((UserVariable) item)) { @@ -359,6 +374,33 @@ public List getSelectedItems() { return selectedItems; } + public void setSelection(UserData item, boolean selection) { + if (item instanceof UserVariable) { + if (!globalVarAdapter.setSelection((UserVariable) item, selection) + && !localVarAdapter.setSelection((UserVariable) item, selection)) { + multiplayerVarAdapter.setSelection((UserVariable) item, selection); + } + } else { + if (!globalListAdapter.setSelection((UserList) item, selection)) { + localListAdapter.setSelection((UserList) item, selection); + } + } + } + + public void toggleSelection(UserData item) { + if (item instanceof UserVariable) { + if (!globalVarAdapter.toggleSelection((UserVariable) item) + && !localVarAdapter.toggleSelection((UserVariable) item)) { + multiplayerVarAdapter.toggleSelection((UserVariable) item); + } + } else { + if (!globalListAdapter.toggleSelection((UserList) item)) { + localListAdapter.toggleSelection((UserList) item); + } + } + notifyDataSetChanged(); + } + @Override public int getItemCount() { return multiplayerVarAdapter.getItemCount() @@ -367,4 +409,12 @@ public int getItemCount() { + globalListAdapter.getItemCount() + localListAdapter.getItemCount(); } + + public int getSelectedItemCount() { + return multiplayerVarAdapter.getSelectedItemCount() + + globalVarAdapter.getSelectedItemCount() + + localVarAdapter.getSelectedItemCount() + + globalListAdapter.getSelectedItemCount() + + localListAdapter.getSelectedItemCount(); + } } diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/adapter/ProjectAdapter.java b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/adapter/ProjectAdapter.java index 3a59705d797..47aae8854e6 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/adapter/ProjectAdapter.java +++ b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/adapter/ProjectAdapter.java @@ -30,7 +30,6 @@ import org.catrobat.catroid.R; import org.catrobat.catroid.common.ProjectData; import org.catrobat.catroid.io.ProjectAndSceneScreenshotLoader; -import org.catrobat.catroid.io.XstreamSerializer; import org.catrobat.catroid.ui.recyclerview.viewholder.ExtendedVH; import org.catrobat.catroid.utils.FileMetaDataExtractor; @@ -52,10 +51,12 @@ public void onBindViewHolder(ExtendedVH holder, int position) { int thumbnailHeight = context.getResources().getDimensionPixelSize(R.dimen.project_thumbnail_height); ProjectAndSceneScreenshotLoader loader = new ProjectAndSceneScreenshotLoader(thumbnailWidth, thumbnailHeight); ProjectData item = items.get(position); - String sceneName = XstreamSerializer.extractDefaultSceneNameFromXml(item.getDirectory()); holder.title.setText(item.getName()); - loader.loadAndShowScreenshot(item.getDirectory().getName(), sceneName, false, holder.image); + loader.loadAndShowScreenshot(item.getDirectory().getName(), + loader.getScreenshotSceneName(item.getDirectory()), + false, + holder.image); if (showDetails) { Date lastModified = new Date(item.getLastUsed()); diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/adapter/RVAdapter.java b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/adapter/RVAdapter.java index b0c8ed7b04d..cf3c6df404e 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/adapter/RVAdapter.java +++ b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/adapter/RVAdapter.java @@ -46,7 +46,9 @@ public abstract class RVAdapter extends RecyclerView.Adapter imp @Retention(RetentionPolicy.SOURCE) @IntDef({SINGLE, PAIRS, MULTIPLE}) - @interface SelectionType {} + @interface SelectionType { + } + public static final int SINGLE = 0; public static final int PAIRS = 1; public static final int MULTIPLE = 2; @@ -175,6 +177,14 @@ public boolean setSelection(T item, boolean selection) { return true; } + public boolean toggleSelection(T item) { + if (items.indexOf(item) == -1) { + return false; + } + selectionManager.toggleSelection(items.indexOf(item)); + return true; + } + public void toggleSelection() { if (selectionManager.getSelectedPositions().size() == getSelectableItemCount()) { clearSelection(); @@ -205,6 +215,10 @@ public int getSelectableItemCount() { return getItemCount(); } + public int getSelectedItemCount() { + return selectionManager.getSelectedPositions().size(); + } + public interface SelectionListener { void onSelectionChanged(int selectedItemCnt); diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/controller/BrickController.java b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/controller/BrickController.java index 81815d2ff41..20528543960 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/controller/BrickController.java +++ b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/controller/BrickController.java @@ -29,6 +29,8 @@ import org.catrobat.catroid.content.Sprite; import org.catrobat.catroid.content.bricks.Brick; import org.catrobat.catroid.content.bricks.ScriptBrick; +import org.catrobat.catroid.content.bricks.UserDefinedBrick; +import org.catrobat.catroid.content.bricks.UserDefinedReceiverBrick; import java.util.List; @@ -63,6 +65,10 @@ public void copy(@NonNull List bricksToCopy, Sprite parent) { public void delete(@NonNull List bricksToDelete, Sprite parent) { for (Brick brick : bricksToDelete) { Script script = brick.getScript(); + if (brick instanceof UserDefinedReceiverBrick) { + UserDefinedBrick userDefinedBrick = ((UserDefinedReceiverBrick) brick).getUserDefinedBrick(); + parent.removeUserDefinedBrick(userDefinedBrick); + } if (brick instanceof ScriptBrick) { parent.removeScript(script); diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/DataListFragment.java b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/DataListFragment.java deleted file mode 100644 index 98f0ea2842e..00000000000 --- a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/DataListFragment.java +++ /dev/null @@ -1,361 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2018 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.ui.recyclerview.fragment; - -import android.os.Bundle; -import android.view.ActionMode; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; - -import org.catrobat.catroid.ProjectManager; -import org.catrobat.catroid.R; -import org.catrobat.catroid.content.Project; -import org.catrobat.catroid.content.Scene; -import org.catrobat.catroid.content.Script; -import org.catrobat.catroid.content.Sprite; -import org.catrobat.catroid.content.bricks.Brick; -import org.catrobat.catroid.content.bricks.FormulaBrick; -import org.catrobat.catroid.formulaeditor.Formula; -import org.catrobat.catroid.formulaeditor.UserData; -import org.catrobat.catroid.formulaeditor.UserList; -import org.catrobat.catroid.formulaeditor.UserVariable; -import org.catrobat.catroid.ui.BottomBar; -import org.catrobat.catroid.ui.recyclerview.adapter.DataListAdapter; -import org.catrobat.catroid.ui.recyclerview.adapter.RVAdapter; -import org.catrobat.catroid.ui.recyclerview.dialog.TextInputDialog; -import org.catrobat.catroid.ui.recyclerview.dialog.textwatcher.RenameItemTextWatcher; -import org.catrobat.catroid.ui.recyclerview.viewholder.CheckableVH; -import org.catrobat.catroid.utils.ToastUtil; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import androidx.annotation.IntDef; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.Fragment; -import androidx.recyclerview.widget.RecyclerView; - -public class DataListFragment extends Fragment implements - ActionMode.Callback, - RVAdapter.SelectionListener, - RVAdapter.OnItemClickListener { - - public static final String TAG = DataListFragment.class.getSimpleName(); - - @Retention(RetentionPolicy.SOURCE) - @IntDef({NONE, DELETE}) - @interface ActionModeType { - } - - private static final int NONE = 0; - private static final int DELETE = 1; - - private RecyclerView recyclerView; - private DataListAdapter adapter; - private ActionMode actionMode; - - private FormulaEditorDataInterface formulaEditorDataInterface; - - @ActionModeType - protected int actionModeType = NONE; - - public void setFormulaEditorDataInterface(FormulaEditorDataInterface formulaEditorDataInterface) { - this.formulaEditorDataInterface = formulaEditorDataInterface; - } - - @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - switch (actionModeType) { - case DELETE: - mode.setTitle(getString(R.string.am_delete)); - break; - case NONE: - return false; - } - MenuInflater inflater = mode.getMenuInflater(); - inflater.inflate(R.menu.context_menu, menu); - - adapter.showCheckBoxes(true); - adapter.updateDataSet(); - return true; - } - - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - return false; - } - - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - switch (item.getItemId()) { - case R.id.confirm: - handleContextualAction(); - break; - default: - return false; - } - return true; - } - - @Override - public void onDestroyActionMode(ActionMode mode) { - resetActionModeParameters(); - adapter.clearSelection(); - } - - private void handleContextualAction() { - if (adapter.getSelectedItems().isEmpty()) { - actionMode.finish(); - return; - } - - switch (actionModeType) { - case DELETE: - showDeleteAlert(adapter.getSelectedItems()); - break; - case NONE: - throw new IllegalStateException("ActionModeType not set Correctly"); - } - } - - protected void resetActionModeParameters() { - actionModeType = NONE; - actionMode = null; - adapter.showCheckBoxes(false); - adapter.allowMultiSelection = true; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View parent = inflater.inflate(R.layout.fragment_list_view, container, false); - recyclerView = parent.findViewById(R.id.recycler_view); - setHasOptionsMenu(true); - return parent; - } - - @Override - public void onActivityCreated(Bundle savedInstance) { - super.onActivityCreated(savedInstance); - initializeAdapter(); - } - - @Override - public void onResume() { - super.onResume(); - ((AppCompatActivity) getActivity()).getSupportActionBar().setTitle(R.string.formula_editor_data); - BottomBar.showBottomBar(getActivity()); - BottomBar.hidePlayButton(getActivity()); - } - - @Override - public void onStop() { - super.onStop(); - finishActionMode(); - BottomBar.hideBottomBar(getActivity()); - } - - private void initializeAdapter() { - Project currentProject = ProjectManager.getInstance().getCurrentProject(); - Sprite currentSprite = ProjectManager.getInstance().getCurrentSprite(); - - List globalVars = currentProject.getUserVariables(); - List localVars = currentSprite.getUserVariables(); - List multiplayerVars = currentProject.getMultiplayerVariables(); - List globalLists = currentProject.getUserLists(); - List localLists = currentSprite.getUserLists(); - - adapter = new DataListAdapter(multiplayerVars, globalVars, localVars, globalLists, - localLists); - onAdapterReady(); - } - - private void onAdapterReady() { - recyclerView.setAdapter(adapter); - adapter.setSelectionListener(this); - adapter.setOnItemClickListener(this); - } - - public void notifyDataSetChanged() { - adapter.notifyDataSetChanged(); - } - - @Override - public void onPrepareOptionsMenu(Menu menu) { - super.onPrepareOptionsMenu(menu); - for (int index = 0; index < menu.size(); index++) { - menu.getItem(index).setVisible(false); - } - menu.findItem(R.id.delete).setVisible(true); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.delete: - startActionMode(DELETE); - break; - default: - return super.onOptionsItemSelected(item); - } - return true; - } - - private void startActionMode(@ActionModeType int type) { - if (adapter.getItems().isEmpty()) { - ToastUtil.showError(getActivity(), R.string.am_empty_list); - } else { - actionModeType = type; - actionMode = getActivity().startActionMode(this); - } - } - - protected void finishActionMode() { - adapter.clearSelection(); - if (actionModeType != NONE) { - actionMode.finish(); - } - } - - public void showDeleteAlert(final List selectedItems) { - new AlertDialog.Builder(getContext()) - .setTitle(R.string.deletion_alert_title) - .setMessage(R.string.deletion_alert_text) - .setPositiveButton(R.string.deletion_alert_yes, (dialog, id) -> deleteItems(selectedItems)) - .setNegativeButton(R.string.no, null) - .setCancelable(false) - .show(); - } - - private void deleteItems(List selectedItems) { - finishActionMode(); - for (UserData item : selectedItems) { - adapter.remove(item); - } - ToastUtil.showSuccess(getActivity(), getResources().getQuantityString(R.plurals.deleted_Items, - selectedItems.size(), - selectedItems.size())); - } - - private void showRenameDialog(List selectedItems) { - final UserData item = selectedItems.get(0); - - TextInputDialog.Builder builder = new TextInputDialog.Builder(getContext()); - - builder.setHint(getString(R.string.data_label)) - .setText(item.getName()) - .setTextWatcher(new RenameItemTextWatcher<>(item, adapter.getItems())) - .setPositiveButton(getString(R.string.ok), (TextInputDialog.OnClickListener) (dialog, textInput) -> renameItem(item, textInput)); - - builder.setTitle(R.string.rename_data_dialog) - .setNegativeButton(R.string.cancel, null) - .show(); - } - - public void renameItem(UserData item, String name) { - String previousName = item.getName(); - updateUserDataReferences(previousName, name, item); - item.setName(name); - adapter.updateDataSet(); - finishActionMode(); - - if (item instanceof UserVariable) { - formulaEditorDataInterface.onVariableRenamed(previousName, name); - } else { - formulaEditorDataInterface.onListRenamed(previousName, name); - } - } - - public static void updateUserDataReferences(String oldName, String newName, - UserData item) { - for (Scene scene : ProjectManager.getInstance().getCurrentProject().getSceneList()) { - for (Sprite sprite : scene.getSpriteList()) { - for (Script script : sprite.getScriptList()) { - List flatList = new ArrayList(); - script.addToFlatList(flatList); - for (Brick brick : flatList) { - if (brick instanceof FormulaBrick) { - FormulaBrick formulaBrick = (FormulaBrick) brick; - for (Formula formula : formulaBrick.getFormulas()) { - if (item instanceof UserVariable) { - formula.updateVariableName(oldName, newName); - } else { - formula.updateUserlistName(oldName, newName); - } - } - } - } - } - } - } - } - - @Override - public void onSelectionChanged(int selectedItemCnt) { - if (actionModeType == DELETE) { - actionMode.setTitle(getString(R.string.am_delete) + " " + selectedItemCnt); - } - } - - @Override - public void onItemClick(UserData item) { - if (actionModeType == NONE) { - formulaEditorDataInterface.onDataItemSelected(item); - getFragmentManager().popBackStack(); - } - } - - @Override - public void onItemLongClick(final UserData item, CheckableVH holder) { - CharSequence[] items = new CharSequence[] {getString(R.string.delete), getString(R.string.rename)}; - new AlertDialog.Builder(getActivity()) - .setItems(items, (dialog, which) -> { - switch (which) { - case 0: - showDeleteAlert(new ArrayList<>(Collections.singletonList(item))); - break; - case 1: - showRenameDialog(new ArrayList<>(Collections.singletonList(item))); - break; - default: - dialog.dismiss(); - } - }) - .show(); - } - - public interface FormulaEditorDataInterface { - - void onDataItemSelected(UserData item); - void onVariableRenamed(String previousName, String newName); - void onListRenamed(String previousName, String newName); - } -} diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/DataListFragment.kt b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/DataListFragment.kt new file mode 100644 index 00000000000..a3c67c9d350 --- /dev/null +++ b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/DataListFragment.kt @@ -0,0 +1,347 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2018 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.ui.recyclerview.fragment + +import android.content.DialogInterface +import android.os.Bundle +import android.view.ActionMode +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import androidx.annotation.IntDef +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.RecyclerView +import org.catrobat.catroid.ProjectManager +import org.catrobat.catroid.R +import org.catrobat.catroid.formulaeditor.UserData +import org.catrobat.catroid.formulaeditor.UserVariable +import org.catrobat.catroid.ui.BottomBar +import org.catrobat.catroid.ui.recyclerview.adapter.DataListAdapter +import org.catrobat.catroid.ui.recyclerview.adapter.RVAdapter +import org.catrobat.catroid.ui.recyclerview.dialog.TextInputDialog +import org.catrobat.catroid.ui.recyclerview.dialog.textwatcher.RenameItemTextWatcher +import org.catrobat.catroid.ui.recyclerview.viewholder.CheckableVH +import org.catrobat.catroid.utils.ToastUtil +import org.catrobat.catroid.utils.UserDataUtil.renameUserData +import java.util.ArrayList + +class DataListFragment : Fragment(), + ActionMode.Callback, RVAdapter.SelectionListener, + RVAdapter.OnItemClickListener> { + @kotlin.annotation.Retention(AnnotationRetention.SOURCE) + @IntDef(NONE, DELETE) + internal annotation class ActionModeType + + private var recyclerView: RecyclerView? = null + private var adapter: DataListAdapter? = null + private var actionMode: ActionMode? = null + private var formulaEditorDataInterface: FormulaEditorDataInterface? = null + + @ActionModeType + var actionModeType = NONE + fun setFormulaEditorDataInterface(formulaEditorDataInterface: FormulaEditorDataInterface?) { + this.formulaEditorDataInterface = formulaEditorDataInterface + } + + override fun onCreateActionMode( + mode: ActionMode, + menu: Menu + ): Boolean { + when (actionModeType) { + DELETE -> mode.title = getString(R.string.am_delete) + NONE -> return false + } + val inflater = mode.menuInflater + inflater.inflate(R.menu.context_menu, menu) + adapter?.showCheckBoxes(true) + adapter?.updateDataSet() + return true + } + + override fun onPrepareActionMode( + mode: ActionMode, + menu: Menu + ): Boolean = false + + override fun onActionItemClicked( + mode: ActionMode, + item: MenuItem + ): Boolean { + when (item.itemId) { + R.id.confirm -> handleContextualAction() + else -> return false + } + return true + } + + override fun onDestroyActionMode(mode: ActionMode) { + resetActionModeParameters() + adapter?.clearSelection() + } + + private fun handleContextualAction() { + if (adapter?.selectedItems?.isEmpty() != false) { + actionMode?.finish() + return + } + when (actionModeType) { + DELETE -> showDeleteAlert(adapter!!.selectedItems) + NONE -> throw IllegalStateException("ActionModeType not set Correctly") + } + } + + private fun resetActionModeParameters() { + actionModeType = NONE + actionMode = null + adapter?.showCheckBoxes(false) + adapter?.allowMultiSelection = true + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + val parent = + inflater.inflate(R.layout.fragment_list_view, container, false) + recyclerView = parent.findViewById(R.id.recycler_view) + setHasOptionsMenu(true) + return parent + } + + override fun onActivityCreated(savedInstance: Bundle?) { + super.onActivityCreated(savedInstance) + initializeAdapter() + } + + override fun onResume() { + super.onResume() + (activity as AppCompatActivity?)?.supportActionBar?.setTitle(R.string.formula_editor_data) + BottomBar.showBottomBar(activity) + BottomBar.hidePlayButton(activity) + } + + override fun onStop() { + super.onStop() + finishActionMode() + BottomBar.hideBottomBar(activity) + } + + private fun initializeAdapter() { + val currentProject = + ProjectManager.getInstance().currentProject + val currentSprite = + ProjectManager.getInstance().currentSprite + val globalVars = currentProject.userVariables + val localVars = currentSprite.userVariables + val multiplayerVars = + currentProject.multiplayerVariables + val globalLists = currentProject.userLists + val localLists = currentSprite.userLists + adapter = DataListAdapter( + multiplayerVars, globalVars, localVars, globalLists, + localLists + ) + onAdapterReady() + } + + private fun onAdapterReady() { + recyclerView?.adapter = adapter + adapter?.setSelectionListener(this) + adapter?.setOnItemClickListener(this) + } + + fun notifyDataSetChanged() { + adapter?.notifyDataSetChanged() + } + + override fun onPrepareOptionsMenu(menu: Menu) { + super.onPrepareOptionsMenu(menu) + for (index in 0 until menu.size()) { + menu.getItem(index).isVisible = false + } + menu.findItem(R.id.delete).isVisible = true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.delete -> startActionMode(DELETE) + else -> return super.onOptionsItemSelected(item) + } + return true + } + + private fun startActionMode(@ActionModeType type: Int) { + if (adapter?.items?.isEmpty() != false) { + ToastUtil.showError(activity, R.string.am_empty_list) + } else { + actionModeType = type + actionMode = activity!!.startActionMode(this) + } + } + + private fun finishActionMode() { + adapter?.clearSelection() + if (actionModeType != NONE) { + actionMode?.finish() + } + } + + private fun showDeleteAlert(selectedItems: List>) { + AlertDialog.Builder(context!!) + .setTitle(R.string.deletion_alert_title) + .setMessage(R.string.deletion_alert_text) + .setPositiveButton( + R.string.deletion_alert_yes + ) { _: DialogInterface?, _: Int -> + deleteItems(selectedItems) + } + .setNegativeButton(R.string.no, null) + .setCancelable(false) + .show() + } + + private fun deleteItems(selectedItems: List>) { + finishActionMode() + for (item in selectedItems) { + adapter?.remove(item) + } + ProjectManager.getInstance().currentProject.deselectElements(selectedItems) + ToastUtil.showSuccess( + activity, resources.getQuantityString( + R.plurals.deleted_Items, + selectedItems.size, + selectedItems.size + ) + ) + } + + private fun showRenameDialog(selectedItems: List>) { + val item = selectedItems[0] + val builder = TextInputDialog.Builder(context!!) + + builder.setHint(getString(R.string.data_label)) + .setText(item.name) + .setTextWatcher( + RenameItemTextWatcher( + item, + adapter!!.items + ) + ) + .setPositiveButton( + getString(R.string.ok), + TextInputDialog.OnClickListener { _: DialogInterface?, textInput: String? -> + renameItem( + item, + textInput + ) + } + ) + builder.setTitle(R.string.rename_data_dialog) + .setNegativeButton(R.string.cancel, null) + .show() + } + + private fun renameItem( + item: UserData<*>, + name: String? + ) { + val previousName = item.name + updateUserDataReferences(previousName, name, item) + renameUserData(item, name ?: "") + adapter?.updateDataSet() + finishActionMode() + if (item is UserVariable) { + formulaEditorDataInterface?.onVariableRenamed(previousName, name) + } else { + formulaEditorDataInterface?.onListRenamed(previousName, name) + } + } + + override fun onSelectionChanged(selectedItemCnt: Int) { + if (actionModeType == DELETE) { + actionMode?.title = getString(R.string.am_delete) + " " + selectedItemCnt + } + } + + override fun onItemClick(item: UserData<*>) { + if (actionModeType == NONE) { + formulaEditorDataInterface?.onDataItemSelected(item) + fragmentManager?.popBackStack() + } + } + + override fun onItemLongClick( + item: UserData<*>, + holder: CheckableVH + ) { + val items = + arrayOf(getString(R.string.delete), getString(R.string.rename)) + AlertDialog.Builder(activity!!) + .setItems( + items + ) { dialog: DialogInterface, which: Int -> + when (which) { + 0 -> showDeleteAlert( + ArrayList( + listOf(item) + ) + ) + 1 -> showRenameDialog( + ArrayList( + listOf(item) + ) + ) + else -> dialog.dismiss() + } + } + .show() + } + + interface FormulaEditorDataInterface { + fun onDataItemSelected(item: UserData<*>?) + fun onVariableRenamed( + previousName: String?, + newName: String? + ) + + fun onListRenamed(previousName: String?, newName: String?) + } + + companion object { + @JvmField + val TAG: String = DataListFragment::class.java.simpleName + private const val NONE = 0 + private const val DELETE = 1 + + @JvmStatic + fun updateUserDataReferences(oldName: String?, newName: String?, item: UserData<*>?) { + ProjectManager.getInstance().currentProject + .updateUserDataReferences(oldName, newName, item) + } + } +} diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/ListSelectorFragment.kt b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/ListSelectorFragment.kt new file mode 100644 index 00000000000..708851a4c27 --- /dev/null +++ b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/ListSelectorFragment.kt @@ -0,0 +1,284 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2018 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.ui.recyclerview.fragment + +import android.content.Context +import android.content.DialogInterface +import android.os.Bundle +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup + +import org.catrobat.catroid.ProjectManager +import org.catrobat.catroid.R +import org.catrobat.catroid.content.bricks.ListSelectorBrick +import org.catrobat.catroid.formulaeditor.UserData +import org.catrobat.catroid.formulaeditor.UserList +import org.catrobat.catroid.ui.BottomBar +import org.catrobat.catroid.ui.UiUtils +import org.catrobat.catroid.ui.recyclerview.adapter.DataListAdapter +import org.catrobat.catroid.ui.recyclerview.adapter.RVAdapter +import org.catrobat.catroid.ui.recyclerview.dialog.TextInputDialog +import org.catrobat.catroid.ui.recyclerview.dialog.textwatcher.RenameItemTextWatcher +import org.catrobat.catroid.ui.recyclerview.viewholder.CheckableVH +import org.catrobat.catroid.utils.ToastUtil + +import java.util.ArrayList + +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.RecyclerView +import org.catrobat.catroid.utils.UserDataUtil + +class ListSelectorFragment : Fragment(), RVAdapter.SelectionListener, + RVAdapter.OnItemClickListener> { + + private var recyclerView: RecyclerView? = null + private var adapter: DataListAdapter? = null + private var listSelectorInterface: ListSelectorInterface? = null + private var preSelection: List? = null + + private fun updateSelection(userLists: List) { + adapter?.clearSelection() + userLists.forEach { list -> + adapter?.setSelection(list, true) + } + + updateTitle() + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.context_menu, menu) + super.onCreateOptionsMenu(menu, inflater) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.toggle_selection -> { + if (adapter?.itemCount != adapter?.selectedItemCount) { + adapter?.selectAll() + } else { + adapter?.clearSelection() + } + updateTitle() + } + R.id.confirm -> handleContextualAction() + } + return super.onOptionsItemSelected(item) + } + + private fun handleContextualAction() { + val selectedItems = adapter?.selectedItems ?: return + listSelectorInterface?.onUserListSelected(selectedItems.filterIsInstance()) + fragmentManager?.popBackStack() + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + val parent = inflater.inflate(R.layout.fragment_list_view, container, false) + recyclerView = parent.findViewById(R.id.recycler_view) + setHasOptionsMenu(true) + + return parent + } + + override fun onActivityCreated(savedInstance: Bundle?) { + super.onActivityCreated(savedInstance) + initializeAdapter() + } + + override fun onResume() { + super.onResume() + BottomBar.showBottomBar(activity) + BottomBar.hidePlayButton(activity) + } + + override fun onStop() { + super.onStop() + BottomBar.hideBottomBar(activity) + } + + override fun onPrepareOptionsMenu(menu: Menu) { + for (index in 0 until menu.size()) { + menu.getItem(index).isVisible = false + } + + menu.findItem(R.id.toggle_selection).setEnabled(true).isVisible = true + menu.findItem(R.id.confirm).setEnabled(true).isVisible = true + + super.onPrepareOptionsMenu(menu) + } + + private fun initializeAdapter() { + val globalLists = ProjectManager.getInstance().currentProject.userLists + val localLists = ProjectManager.getInstance().currentSprite.userLists + + adapter = DataListAdapter(ArrayList(), ArrayList(), ArrayList(), globalLists, localLists) + adapter?.showCheckBoxes(true) + onAdapterReady() + } + + private fun onAdapterReady() { + recyclerView?.adapter = adapter + adapter?.setSelectionListener(this) + adapter?.setOnItemClickListener(this) + + updateSelection(preSelection ?: emptyList()) + } + + fun notifyDataSetChanged() { + adapter?.notifyDataSetChanged() + } + + private fun deleteItems(selectedItems: List>) { + for (item in selectedItems) { + adapter?.remove(item) + } + ProjectManager.getInstance().currentProject.deselectElements(selectedItems) + ToastUtil.showSuccess( + activity, resources.getQuantityString( + R.plurals.deleted_Items, + selectedItems.size, + selectedItems.size + ) + ) + } + + private fun showDeleteAlert(selectedItems: List>) { + AlertDialog.Builder(context!!) + .setTitle(R.string.deletion_alert_title) + .setMessage(R.string.deletion_alert_text) + .setPositiveButton(R.string.deletion_alert_yes) { _, _ -> + deleteItems( + selectedItems + ) + } + .setNegativeButton(R.string.no, null) + .setCancelable(false) + .show() + } + + private fun showRenameDialog(selectedItems: List>) { + val item = selectedItems[0] + val builder = TextInputDialog.Builder(context!!) + + builder.setHint(getString(R.string.data_label)) + .setText(item.name) + .setTextWatcher( + RenameItemTextWatcher( + item, + adapter!!.items + ) + ) + .setPositiveButton( + getString(R.string.ok), + TextInputDialog.OnClickListener { _: DialogInterface?, textInput: String? -> + renameItem( + item, + textInput + ) + } + ) + builder.setTitle(R.string.rename_data_dialog) + .setNegativeButton(R.string.cancel, null) + .show() + } + + private fun renameItem(item: UserData<*>, name: String?) { + val previousName = item.name + ProjectManager.getInstance().currentProject.updateUserDataReferences( + previousName, + name, + item + ) + UserDataUtil.renameUserData(item, name ?: "") + adapter?.updateDataSet() + } + + private fun updateTitle() { + adapter?.selectedItemCount?.let { + (activity as? AppCompatActivity)?.supportActionBar?.title = resources.getQuantityString( + R.plurals.list_selection_plural, it, it + ) + } + } + + override fun onSelectionChanged(selectedItemCnt: Int) { + updateTitle() + } + + override fun onItemClick(item: UserData<*>) { + adapter?.toggleSelection(item) + updateTitle() + } + + override fun onItemLongClick(item: UserData<*>, holder: CheckableVH) { + val items = arrayOf(getString(R.string.delete), getString(R.string.rename)) + AlertDialog.Builder(activity!!) + .setItems(items) { dialog, which -> + when (which) { + 0 -> showDeleteAlert(listOf(item)) + 1 -> showRenameDialog(listOf(item)) + else -> dialog.dismiss() + } + } + .show() + } + + interface ListSelectorInterface { + fun onUserListSelected(userLists: List) + } + + companion object { + private val TAG: String = ListSelectorFragment::class.java.simpleName + + @JvmStatic + fun showFragment(context: Context, selectorBrick: ListSelectorBrick) { + val activity = UiUtils.getActivityFromContextWrapper(context) ?: return + + var listSelectorFragment = activity.supportFragmentManager + .findFragmentByTag(TAG) as ListSelectorFragment? + + if (listSelectorFragment == null) { + listSelectorFragment = ListSelectorFragment() + + listSelectorFragment.listSelectorInterface = selectorBrick + + activity.supportFragmentManager.beginTransaction() + .replace(R.id.fragment_container, listSelectorFragment, TAG) + .addToBackStack(TAG) + .commit() + } + + listSelectorFragment.preSelection = selectorBrick.userLists + } + } +} diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/MainMenuFragment.java b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/MainMenuFragment.java index 9c8861571a1..6c547da34f0 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/MainMenuFragment.java +++ b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/MainMenuFragment.java @@ -37,7 +37,6 @@ import org.catrobat.catroid.common.ProjectData; import org.catrobat.catroid.content.backwardcompatibility.ProjectMetaDataParser; import org.catrobat.catroid.io.ProjectAndSceneScreenshotLoader; -import org.catrobat.catroid.io.XstreamSerializer; import org.catrobat.catroid.io.asynctask.ProjectLoadTask; import org.catrobat.catroid.ui.ProjectActivity; import org.catrobat.catroid.ui.ProjectListActivity; @@ -287,9 +286,7 @@ private void loadProjectImage() { ProjectAndSceneScreenshotLoader loader = new ProjectAndSceneScreenshotLoader(CURRENTTHUMBNAILSIZE, CURRENTTHUMBNAILSIZE); - String sceneName = - XstreamSerializer.extractDefaultSceneNameFromXml(projectDir); - loader.loadAndShowScreenshot(currentProject, sceneName, false, + loader.loadAndShowScreenshot(projectDir.getName(), loader.getScreenshotSceneName(projectDir), false, parent.findViewById(R.id.image_view)); } } diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/ProjectListFragment.java b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/ProjectListFragment.java index 762292e164d..3bd9e0ae8e9 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/ProjectListFragment.java +++ b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/ProjectListFragment.java @@ -407,6 +407,7 @@ public void onClick(DialogInterface dialog, int which) { .commit(); break; case 4: + ProjectLoadTask.task(item.getDirectory(), getContext()); startActivity(new Intent(getActivity(), ProjectUploadActivity.class) .putExtra(ProjectUploadActivity.PROJECT_DIR, item.getDirectory())); break; diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/ScriptFragment.java b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/ScriptFragment.java index fa74ff12110..134f7c70e47 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/ScriptFragment.java +++ b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/ScriptFragment.java @@ -45,6 +45,7 @@ import org.catrobat.catroid.content.bricks.Brick; import org.catrobat.catroid.content.bricks.FormulaBrick; import org.catrobat.catroid.content.bricks.ScriptBrick; +import org.catrobat.catroid.content.bricks.UserDefinedReceiverBrick; import org.catrobat.catroid.content.bricks.VisualPlacementBrick; import org.catrobat.catroid.io.asynctask.ProjectSaveTask; import org.catrobat.catroid.ui.BottomBar; @@ -174,7 +175,7 @@ private void handleContextualAction() { copy(adapter.getSelectedItems()); break; case DELETE: - showDeleteAlert(adapter.getSelectedItems()); + showDeleteAlert(adapter.getSelectedItems(), false); break; case COMMENT: toggleComments(adapter.getSelectedItems()); @@ -468,6 +469,12 @@ public void onItemClick(Brick brick, int position) { private List getContextMenuItems(Brick brick) { List items = new ArrayList<>(); + if (brick instanceof UserDefinedReceiverBrick) { + items.add(R.string.brick_context_dialog_delete_definition); + items.add(R.string.brick_context_dialog_move_definition); + return items; + } + if (brick instanceof ScriptBrick) { items.add(R.string.backpack_add); items.add(R.string.brick_context_dialog_copy_script); @@ -529,7 +536,10 @@ private void handleContextMenuItemClick(int itemId, Brick brick, int position) { break; case R.string.brick_context_dialog_delete_brick: case R.string.brick_context_dialog_delete_script: - showDeleteAlert(brick.getAllParts()); + showDeleteAlert(brick.getAllParts(), false); + break; + case R.string.brick_context_dialog_delete_definition: + showDeleteAlert(brick.getAllParts(), true); break; case R.string.brick_context_dialog_comment_in: case R.string.brick_context_dialog_comment_in_script: @@ -555,6 +565,7 @@ private void handleContextMenuItemClick(int itemId, Brick brick, int position) { break; case R.string.brick_context_dialog_move_brick: case R.string.brick_context_dialog_move_script: + case R.string.brick_context_dialog_move_definition: onItemLongClick(brick, position); break; case R.string.brick_context_dialog_help: @@ -645,11 +656,18 @@ private void copy(List selectedBricks) { finishActionMode(); } - private void showDeleteAlert(List selectedBricks) { - new AlertDialog.Builder(getContext()) - .setTitle(getResources().getQuantityString(R.plurals.delete_bricks, selectedBricks.size())) - .setMessage(R.string.dialog_confirm_delete) - .setPositiveButton(R.string.yes, (dialog, id) -> delete(selectedBricks)) + private void showDeleteAlert(List selectedBricks, boolean isUserDefinedReceiverBrick) { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + + if (isUserDefinedReceiverBrick) { + builder.setTitle(R.string.delete_definition) + .setMessage(R.string.dialog_confirm_delete_definition); + } else { + builder.setTitle(getResources().getQuantityString(R.plurals.delete_bricks, selectedBricks.size())) + .setMessage(R.string.dialog_confirm_delete); + } + + builder.setPositiveButton(R.string.yes, (dialog, id) -> delete(selectedBricks)) .setNegativeButton(R.string.no, null) .setCancelable(false) .show(); diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/settingsfragments/SettingsFragment.java b/catroid/src/main/java/org/catrobat/catroid/ui/settingsfragments/SettingsFragment.java index dbebe70dd56..32fb6e16949 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/settingsfragments/SettingsFragment.java +++ b/catroid/src/main/java/org/catrobat/catroid/ui/settingsfragments/SettingsFragment.java @@ -82,6 +82,7 @@ public class SettingsFragment extends PreferenceFragment { public static final String SETTINGS_MULTILINGUAL = "setting_multilingual"; public static final String SETTINGS_PARROT_JUMPING_SUMO_CATROBAT_TERMS_OF_SERVICE_ACCEPTED_PERMANENTLY = "setting_parrot_jumping_sumo_catrobat_terms_of_service_accepted_permanently"; + public static final String SETTINGS_TEST_BRICKS = "setting_test_bricks"; PreferenceScreen screen = null; public static final String ACCESSIBILITY_SCREEN_KEY = "setting_accessibility_screen"; @@ -178,6 +179,13 @@ public void onCreate(Bundle savedInstanceState) { multiplayerPreference.setEnabled(false); screen.removePreference(multiplayerPreference); } + + if (!BuildConfig.FEATURE_TESTBRICK_ENABLED) { + CheckBoxPreference testPreference = + (CheckBoxPreference) findPreference(SETTINGS_TEST_BRICKS); + testPreference.setEnabled(BuildConfig.DEBUG); + screen.removePreference(testPreference); + } } @Override @@ -303,6 +311,10 @@ public static boolean isNfcSharedPreferenceEnabled(Context context) { return getBooleanSharedPreference(false, SETTINGS_SHOW_NFC_BRICKS, context); } + public static boolean isTestSharedPreferenceEnabled(Context context) { + return getBooleanSharedPreference(BuildConfig.DEBUG, SETTINGS_TEST_BRICKS, context); + } + public static void setAutoCrashReportingEnabled(Context context, boolean isEnabled) { getSharedPreferences(context).edit() .putBoolean(SETTINGS_CRASH_REPORTS, isEnabled) diff --git a/catroid/src/main/java/org/catrobat/catroid/utils/FlashUtil.java b/catroid/src/main/java/org/catrobat/catroid/utils/FlashUtil.java deleted file mode 100644 index a83330372cb..00000000000 --- a/catroid/src/main/java/org/catrobat/catroid/utils/FlashUtil.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2018 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. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * http://developer.catrobat.org/license_additional_term - * - * 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.catroid.utils; - -import android.hardware.Camera; - -import org.catrobat.catroid.camera.CameraManager; - -public final class FlashUtil { - - private static Camera.Parameters paramsOn = null; - private static Camera.Parameters paramsOff = null; - - private static boolean paused = false; - private static boolean available = false; - - private static boolean currentFlashValue = false; - - private static boolean startAgain = false; - - private FlashUtil() { - } - - public static boolean isAvailable() { - return available; - } - - public static boolean isOn() { - return currentFlashValue; - } - - public static boolean isPaused() { - return paused; - } - - public static void pauseFlash() { - if (!paused && isAvailable()) { - paused = true; - if (isOn()) { - startAgain = true; - flashOff(); - } - } - } - - public static void resumeFlash() { - if (paused && isAvailable()) { - initializeFlash(); - if (startAgain) { - flashOn(); - startAgain = false; - } - } - paused = false; - } - - public static void destroy() { - currentFlashValue = false; - paused = false; - available = false; - startAgain = false; - - if (CameraManager.getInstance().isReady()) { - paramsOff = null; - paramsOn = null; - } - } - - public static void reset() { - currentFlashValue = false; - } - - public static void initializeFlash() { - available = CameraManager.getInstance().hasCurrentCameraFlash(); - - if (!available) { - destroy(); - return; - } - - if (!CameraManager.getInstance().isReady()) { - CameraManager.getInstance().startCamera(); - } - - paramsOn = CameraManager.getInstance().getCurrentCamera().getParameters(); - if (paramsOn != null) { - paramsOn.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); - } - paramsOff = CameraManager.getInstance().getCurrentCamera().getParameters(); - if (paramsOff != null) { - paramsOff.setFlashMode("off"); - } - } - - public static void flashOn() { - if (!available) { - destroy(); - return; - } - - CameraManager.getInstance().setFlashParams(paramsOn); - currentFlashValue = true; - } - - public static void flashOff() { - if (!isAvailable()) { - return; - } - - CameraManager.getInstance().setFlashParams(paramsOff); - currentFlashValue = false; - } -} diff --git a/catroid/src/main/java/org/catrobat/catroid/utils/UserDataUtil.kt b/catroid/src/main/java/org/catrobat/catroid/utils/UserDataUtil.kt new file mode 100644 index 00000000000..7d69e8425d6 --- /dev/null +++ b/catroid/src/main/java/org/catrobat/catroid/utils/UserDataUtil.kt @@ -0,0 +1,64 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2020 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. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * 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.catroid.utils + +import org.catrobat.catroid.ProjectManager +import org.catrobat.catroid.content.bricks.Brick +import org.catrobat.catroid.content.bricks.ListSelectorBrick +import org.catrobat.catroid.formulaeditor.UserData + +object UserDataUtil { + @JvmStatic + fun containedInListSelector(dateName: String): Boolean { + return ProjectManager.getInstance().currentProject.sceneList.any { scene -> + scene.spriteList.any { sprite -> + sprite.scriptList.any { script -> + val flatList = mutableListOf() + script.addToFlatList(flatList) + flatList.filterIsInstance().any { brick -> + brick.userLists.any { it.name == dateName } + } + } + } + } + } + + @JvmStatic + fun renameUserData(item: UserData<*>, name: String) { + if (containedInListSelector(item.name)) { + val previousName: String = item.name + val rename = { data: UserData<*> -> + if (data.name == previousName) { + data.name = name + } + } + ProjectManager.getInstance()?.currentProject?.userLists?.forEach(rename) + ProjectManager.getInstance()?.currentSprite?.userLists?.forEach(rename) + ProjectManager.getInstance()?.currentProject?.userVariables?.forEach(rename) + ProjectManager.getInstance()?.currentSprite?.userVariables?.forEach(rename) + } else { + item.name = name + } + } +} diff --git a/catroid/src/main/java/org/catrobat/catroid/visualplacement/VisualPlacementActivity.java b/catroid/src/main/java/org/catrobat/catroid/visualplacement/VisualPlacementActivity.java index 29b97907d18..5c4f6747944 100644 --- a/catroid/src/main/java/org/catrobat/catroid/visualplacement/VisualPlacementActivity.java +++ b/catroid/src/main/java/org/catrobat/catroid/visualplacement/VisualPlacementActivity.java @@ -70,8 +70,8 @@ import static org.catrobat.catroid.content.Look.ROTATION_STYLE_ALL_AROUND; import static org.catrobat.catroid.content.Look.ROTATION_STYLE_LEFT_RIGHT_ONLY; import static org.catrobat.catroid.content.Look.ROTATION_STYLE_NONE; -import static org.catrobat.catroid.stage.StageListener.SCREENSHOT_AUTOMATIC_FILE_NAME; -import static org.catrobat.catroid.stage.StageListener.SCREENSHOT_MANUAL_FILE_NAME; +import static org.catrobat.catroid.common.Constants.SCREENSHOT_AUTOMATIC_FILE_NAME; +import static org.catrobat.catroid.common.Constants.SCREENSHOT_MANUAL_FILE_NAME; import static org.catrobat.catroid.ui.SpriteActivity.EXTRA_BRICK_HASH; import static org.catrobat.catroid.ui.SpriteActivity.EXTRA_X_TRANSFORM; import static org.catrobat.catroid.ui.SpriteActivity.EXTRA_Y_TRANSFORM; diff --git a/catroid/src/main/res/drawable/bottom_bar_background.xml b/catroid/src/main/res/drawable/bottom_bar_background.xml index d73ca73868b..251d6f74de2 100644 --- a/catroid/src/main/res/drawable/bottom_bar_background.xml +++ b/catroid/src/main/res/drawable/bottom_bar_background.xml @@ -25,7 +25,7 @@ + android:color="@color/button_bottom_bar" /> diff --git a/catroid/src/main/res/layout/activity_upload.xml b/catroid/src/main/res/layout/activity_upload.xml index ea6570ce7b5..4e53f88fbc3 100644 --- a/catroid/src/main/res/layout/activity_upload.xml +++ b/catroid/src/main/res/layout/activity_upload.xml @@ -46,6 +46,9 @@ android:id="@+id/project_image_view" android:layout_width="100dp" android:layout_height="100dp" + android:layout_marginStart="@dimen/dialog_input_offset" + android:cropToPadding="true" + android:scaleType="centerCrop" android:layout_marginBottom="@dimen/details_spacing"/> + + + + + + + + + + + + + \ No newline at end of file diff --git a/catroid/src/main/res/layout/brick_finish_stage.xml b/catroid/src/main/res/layout/brick_finish_stage.xml index 2e840c1388e..6cf3d975ddd 100644 --- a/catroid/src/main/res/layout/brick_finish_stage.xml +++ b/catroid/src/main/res/layout/brick_finish_stage.xml @@ -43,7 +43,7 @@ + android:text="@string/brick_finish_tests" /> \ No newline at end of file diff --git a/catroid/src/main/res/layout/brick_parameterized_assert.xml b/catroid/src/main/res/layout/brick_parameterized_assert.xml new file mode 100644 index 00000000000..18354e0165b --- /dev/null +++ b/catroid/src/main/res/layout/brick_parameterized_assert.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/catroid/src/main/res/layout/brick_parameterized_input.xml b/catroid/src/main/res/layout/brick_parameterized_input.xml new file mode 100644 index 00000000000..7f37e343de6 --- /dev/null +++ b/catroid/src/main/res/layout/brick_parameterized_input.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/catroid/src/main/res/layout/privacy_policy_view.xml b/catroid/src/main/res/layout/privacy_policy_view.xml index 1d92d9912b4..4af020372b1 100644 --- a/catroid/src/main/res/layout/privacy_policy_view.xml +++ b/catroid/src/main/res/layout/privacy_policy_view.xml @@ -77,7 +77,7 @@ style="@style/BottomBarButton" android:id="@+id/decline" android:onClick="handleDeclinedPrivacyPolicyButton" - android:text="@string/disagree" /> + android:text="@string/disagree"/>