diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/formulaeditor/FormulaEditorUndoTest.java b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/formulaeditor/FormulaEditorUndoTest.java deleted file mode 100644 index f3262497385..00000000000 --- a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/formulaeditor/FormulaEditorUndoTest.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2025 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.formulaeditor; - -import org.catrobat.catroid.ProjectManager; -import org.catrobat.catroid.R; -import org.catrobat.catroid.content.Script; -import org.catrobat.catroid.content.bricks.PlaceAtBrick; -import org.catrobat.catroid.content.bricks.SetVariableBrick; -import org.catrobat.catroid.formulaeditor.Formula; -import org.catrobat.catroid.formulaeditor.UserVariable; -import org.catrobat.catroid.test.utils.TestUtils; -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.util.UiTestUtils; -import org.catrobat.catroid.uiespresso.util.rules.FragmentActivityTestRule; -import org.junit.After; -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 static org.catrobat.catroid.uiespresso.content.brick.utils.BrickDataInteractionWrapper.onBrickAtPosition; -import static org.catrobat.catroid.uiespresso.content.brick.utils.ColorPickerInteractionWrapper.onColorPickerPresetButton; -import static org.catrobat.catroid.uiespresso.formulaeditor.utils.FormulaEditorDataListWrapper.onDataList; -import static org.catrobat.catroid.uiespresso.formulaeditor.utils.FormulaEditorWrapper.onFormulaEditor; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import static androidx.test.espresso.Espresso.closeSoftKeyboard; -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.assertion.ViewAssertions.doesNotExist; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; - -@RunWith(AndroidJUnit4.class) -public class FormulaEditorUndoTest { - - private int brickPosition; - - private static final String VARIABLE_NAME = "TestVariable"; - private static final String NEW_VARIABLE_NAME = "NewVariable"; - private static final int VARIABLE_VALUE = 5; - private static final String NEW_VARIABLE_VALUE = "10"; - UserVariable userVariable; - - @Rule - public FragmentActivityTestRule baseActivityTestRule = new - FragmentActivityTestRule<>(SpriteActivity.class, SpriteActivity.EXTRA_FRAGMENT_POSITION, SpriteActivity.FRAGMENT_SCRIPTS); - - @After - public void tearDown() throws Exception { - TestUtils.deleteProjects(FormulaEditorUndoTest.class.getName()); - } - - @Before - public void setUp() throws Exception { - brickPosition = 1; - Script script = UiTestUtils.createProjectAndGetStartScript(FormulaEditorUndoTest.class.getName()); - script.addBrick(new PlaceAtBrick()); - userVariable = new UserVariable(VARIABLE_NAME, VARIABLE_VALUE); - ProjectManager.getInstance().getCurrentProject().addUserVariable(userVariable); - script.addBrick(new SetVariableBrick(new Formula(0), userVariable)); - baseActivityTestRule.launchActivity(); - } - - @Category({Cat.AppUi.class, Level.Smoke.class}) - @Test - public void testUndoFormulaChangesOnPressBack() { - onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at); - onView(withId(R.id.brick_place_at_edit_text_x)) - .perform(click()); - onView(withText(R.string.brick_context_dialog_formula_edit_brick)) - .perform(click()); - onFormulaEditor() - .performEnterFormula("1234"); - - onView(withId(R.id.brick_place_at_edit_text_y)) - .perform(click()); - onView(withText(R.string.brick_context_dialog_formula_edit_brick)) - .perform(click()); - onFormulaEditor() - .performEnterFormula("745"); - - pressBack(); - - onView(withId(R.id.menu_undo)) - .check(matches(isDisplayed())); - onBrickAtPosition(brickPosition) - .onFormulaTextField(R.id.brick_place_at_edit_text_x) - .checkShowsNumber(1234); - onBrickAtPosition(brickPosition) - .onFormulaTextField(R.id.brick_place_at_edit_text_y) - .checkShowsNumber(745); - - onView(withId(R.id.menu_undo)) - .perform(click()); - onView(withId(R.id.menu_undo)) - .check(doesNotExist()); - - onBrickAtPosition(brickPosition) - .onFormulaTextField(R.id.brick_place_at_edit_text_x) - .checkShowsNumber(0); - onBrickAtPosition(brickPosition) - .onFormulaTextField(R.id.brick_place_at_edit_text_y) - .checkShowsNumber(0); - } - - @Category({Cat.AppUi.class, Level.Smoke.class}) - @Test - public void testUndoFormulaWithNoChanges() { - onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at); - - onView(withId(R.id.brick_place_at_edit_text_x)) - .perform(click()); - onView(withText(R.string.brick_context_dialog_formula_edit_brick)) - .perform(click()); - - pressBack(); - - onView(withId(R.id.menu_undo)) - .check(doesNotExist()); - - onBrickAtPosition(brickPosition) - .onFormulaTextField(R.id.brick_place_at_edit_text_x) - .checkShowsNumber(0); - onBrickAtPosition(brickPosition) - .onFormulaTextField(R.id.brick_place_at_edit_text_y) - .checkShowsNumber(0); - } - - @Category({Cat.AppUi.class, Level.Smoke.class}) - @Test - public void testUndoFormulaWithRevertedChanges() { - onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at); - onView(withId(R.id.brick_place_at_edit_text_x)) - .perform(click()); - onView(withText(R.string.brick_context_dialog_formula_edit_brick)) - .perform(click()); - onFormulaEditor() - .performEnterFormula("1234"); - - onView(withId(R.id.brick_place_at_edit_text_y)) - .perform(click()); - onView(withText(R.string.brick_context_dialog_formula_edit_brick)) - .perform(click()); - - onView(withId(R.id.brick_place_at_edit_text_x)) - .perform(click()); - onView(withText(R.string.brick_context_dialog_formula_edit_brick)).perform(click()); - onFormulaEditor().performEnterFormula("0"); - - pressBack(); - - onView(withId(R.id.menu_undo)) - .check(doesNotExist()); - - onBrickAtPosition(brickPosition) - .onFormulaTextField(R.id.brick_place_at_edit_text_x) - .checkShowsNumber(0); - onBrickAtPosition(brickPosition) - .onFormulaTextField(R.id.brick_place_at_edit_text_y) - .checkShowsNumber(0); - } - - @Category({Cat.AppUi.class, Level.Smoke.class}) - @Test - public void testUndoFormulaAddVariable() { - onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at); - - onView(withId(R.id.brick_place_at_edit_text_x)) - .perform(click()); - onView(withText(R.string.brick_context_dialog_formula_edit_brick)) - .perform(click()); - - onFormulaEditor() - .performOpenDataFragment(); - - onDataList() - .performAdd(NEW_VARIABLE_NAME); - - onDataList() - .performClose(); - - pressBack(); - - onView(withId(R.id.menu_undo)) - .check(matches(isDisplayed())); - - onView(withId(R.id.menu_undo)) - .perform(click()); - onView(withId(R.id.menu_undo)) - .check(doesNotExist()); - - assertNull(ProjectManager.getInstance().getCurrentProject().getUserVariable(NEW_VARIABLE_NAME)); - - onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at); - - onView(withId(R.id.brick_place_at_edit_text_x)) - .perform(click()); - onView(withText(R.string.brick_context_dialog_formula_edit_brick)) - .perform(click()); - - onFormulaEditor() - .performOpenDataFragment(); - - onDataList() - .performAdd(NEW_VARIABLE_NAME); - - onDataList() - .performClose(); - - pressBack(); - - onView(withId(R.id.menu_undo)) - .check(matches(isDisplayed())); - - assertNotNull(ProjectManager.getInstance().getCurrentProject().getUserVariable(NEW_VARIABLE_NAME)); - } - - @Category({Cat.AppUi.class, Level.Smoke.class}) - @Test - public void testUndoFormulaDeleteVariable() { - onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at); - - onView(withId(R.id.brick_place_at_edit_text_x)) - .perform(click()); - onView(withText(R.string.brick_context_dialog_formula_edit_brick)) - .perform(click()); - - onFormulaEditor() - .performOpenDataFragment(); - - onDataList().onVariableAtPosition(0) - .performDelete(); - - onDataList() - .performClose(); - - pressBack(); - - onView(withId(R.id.menu_undo)) - .check(matches(isDisplayed())); - - onView(withId(R.id.menu_undo)) - .perform(click()); - onView(withId(R.id.menu_undo)) - .check(doesNotExist()); - - assertNotNull(ProjectManager.getInstance().getCurrentProject().getUserVariable(VARIABLE_NAME)); - - assertEquals(ProjectManager.getInstance().getCurrentProject().getUserVariable(VARIABLE_NAME), userVariable); - assertEquals(ProjectManager.getInstance().getCurrentProject().getUserVariable(VARIABLE_NAME).getValue(), VARIABLE_VALUE); - } - - @Category({Cat.AppUi.class, Level.Smoke.class}) - @Test - public void testUndoFormulaRenameVariable() { - onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at); - onView(withId(R.id.brick_place_at_edit_text_x)).perform(click()); - onView(withText(R.string.brick_context_dialog_formula_edit_brick)).perform(click()); - onFormulaEditor().performOpenDataFragment(); - onDataList().onVariableAtPosition(0).performRename(NEW_VARIABLE_NAME); - onDataList().performClose(); - pressBack(); - - onView(withId(R.id.menu_undo)).check(matches(isDisplayed())); - - assertNull(ProjectManager.getInstance().getCurrentProject().getUserVariable(VARIABLE_NAME)); - assertNotNull(ProjectManager.getInstance().getCurrentProject().getUserVariable(NEW_VARIABLE_NAME)); - assertEquals(ProjectManager.getInstance().getCurrentProject().getUserVariable(NEW_VARIABLE_NAME), userVariable); - assertEquals(ProjectManager.getInstance().getCurrentProject().getUserVariable(NEW_VARIABLE_NAME).getValue(), VARIABLE_VALUE); - - onView(withId(R.id.menu_undo)) - .perform(click()); - userVariable.setName(VARIABLE_NAME); - onView(withId(R.id.menu_undo)) - .check(doesNotExist()); - - assertNull(ProjectManager.getInstance().getCurrentProject().getUserVariable(NEW_VARIABLE_NAME)); - assertNotNull(ProjectManager.getInstance().getCurrentProject().getUserVariable(VARIABLE_NAME)); - assertEquals(ProjectManager.getInstance().getCurrentProject().getUserVariable(VARIABLE_NAME), userVariable); - assertEquals(ProjectManager.getInstance().getCurrentProject().getUserVariable(VARIABLE_NAME).getValue(), VARIABLE_VALUE); - } - - @Category({Cat.AppUi.class, Level.Smoke.class}) - @Test - public void testUndoFormulaEditVariable() { - onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at); - - onView(withId(R.id.brick_place_at_edit_text_x)) - .perform(click()); - onView(withText(R.string.brick_context_dialog_formula_edit_brick)) - .perform(click()); - - onFormulaEditor() - .performOpenDataFragment(); - - onDataList().onVariableAtPosition(0) - .performEdit(NEW_VARIABLE_VALUE); - - onDataList() - .performClose(); - - pressBack(); - - onView(withId(R.id.menu_undo)) - .check(matches(isDisplayed())); - - assertEquals(ProjectManager.getInstance().getCurrentProject().getUserVariable(VARIABLE_NAME), userVariable); - assertEquals(ProjectManager.getInstance().getCurrentProject().getUserVariable(VARIABLE_NAME).getValue(), NEW_VARIABLE_VALUE); - - onView(withId(R.id.menu_undo)) - .perform(click()); - onView(withId(R.id.menu_undo)) - .check(doesNotExist()); - - assertEquals(ProjectManager.getInstance().getCurrentProject().getUserVariable(VARIABLE_NAME), userVariable); - assertEquals(ProjectManager.getInstance().getCurrentProject().getUserVariable(VARIABLE_NAME).getValue(), VARIABLE_VALUE); - } - - @Category({Cat.AppUi.class, Level.Smoke.class}) - @Test - public void testUndoFormulaWithSpinnerVariable() { - onBrickAtPosition(2).checkShowsText(R.string.brick_set_variable); - onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at); - - onBrickAtPosition(2) - .onSpinner(R.id.set_variable_spinner) - .checkShowsText(VARIABLE_NAME); - - onView(withId(R.id.brick_place_at_edit_text_x)) - .perform(click()); - onView(withText(R.string.brick_context_dialog_formula_edit_brick)) - .perform(click()); - - onFormulaEditor() - .performOpenDataFragment(); - - onDataList().onVariableAtPosition(0) - .performDelete(); - - onDataList() - .performClose(); - - pressBack(); - - onBrickAtPosition(2) - .onSpinner(R.id.set_variable_spinner) - .checkShowsText(R.string.new_option); - - onView(withId(R.id.menu_undo)) - .check(matches(isDisplayed())); - - onView(withId(R.id.menu_undo)) - .perform(click()); - onView(withId(R.id.menu_undo)) - .check(doesNotExist()); - - onBrickAtPosition(2) - .onSpinner(R.id.set_variable_spinner) - .checkShowsText(VARIABLE_NAME); - } - - @Category({Cat.AppUi.class, Level.Smoke.class}) - @Test - public void testUndoFormulaWithColorPicker() { - onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at); - - onView(withId(R.id.brick_place_at_edit_text_x)) - .perform(click()); - onView(withText(R.string.brick_context_dialog_formula_edit_brick)) - .perform(click()); - - onView(withId(R.id.formula_editor_keyboard_color_picker)).perform(click()); - - onColorPickerPresetButton(0, 0) - .perform(click()); - - closeSoftKeyboard(); - - onView(withText(R.string.color_picker_apply)) - .perform(click()); - - pressBack(); - - onView(withId(R.id.brick_place_at_edit_text_x)) - .check(matches(withText("'#0074CD ' "))); - onBrickAtPosition(brickPosition) - .onFormulaTextField(R.id.brick_place_at_edit_text_y) - .checkShowsNumber(0); - - onView(withId(R.id.menu_undo)) - .check(matches(isDisplayed())); - - onView(withId(R.id.menu_undo)) - .perform(click()); - onView(withId(R.id.menu_undo)) - .check(doesNotExist()); - - onBrickAtPosition(brickPosition) - .onFormulaTextField(R.id.brick_place_at_edit_text_x) - .checkShowsNumber(0); - onBrickAtPosition(brickPosition) - .onFormulaTextField(R.id.brick_place_at_edit_text_y) - .checkShowsNumber(0); - } -} diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/formulaeditor/FormulaEditorUndoTest.kt b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/formulaeditor/FormulaEditorUndoTest.kt new file mode 100644 index 00000000000..7c9488ec5ff --- /dev/null +++ b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/formulaeditor/FormulaEditorUndoTest.kt @@ -0,0 +1,522 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2026 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.formulaeditor + +import androidx.test.espresso.Espresso.closeSoftKeyboard +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.Espresso.pressBack +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions.doesNotExist +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.catrobat.catroid.ProjectManager +import org.catrobat.catroid.R +import org.catrobat.catroid.WaitForConditionAction.Companion.waitFor +import org.catrobat.catroid.content.bricks.PlaceAtBrick +import org.catrobat.catroid.content.bricks.SetVariableBrick +import org.catrobat.catroid.formulaeditor.Formula +import org.catrobat.catroid.formulaeditor.UserVariable +import org.catrobat.catroid.test.utils.TestUtils +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.BrickDataInteractionWrapper.onBrickAtPosition +import org.catrobat.catroid.uiespresso.content.brick.utils.ColorPickerInteractionWrapper.onColorPickerPresetButton +import org.catrobat.catroid.uiespresso.formulaeditor.utils.FormulaEditorDataListWrapper.onDataList +import org.catrobat.catroid.uiespresso.formulaeditor.utils.FormulaEditorWrapper.onFormulaEditor +import org.catrobat.catroid.uiespresso.util.UiTestUtils +import org.catrobat.catroid.uiespresso.util.rules.FragmentActivityTestRule +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.experimental.categories.Category +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class FormulaEditorUndoTest { + + private var brickPosition: Int = 0 + + private var userVariable: UserVariable? = null + + @get:Rule + var baseActivityTestRule = FragmentActivityTestRule( + SpriteActivity::class.java, SpriteActivity.EXTRA_FRAGMENT_POSITION, SpriteActivity.FRAGMENT_SCRIPTS + ) + + @After + @Throws(Exception::class) + fun tearDown() { + TestUtils.deleteProjects(FormulaEditorUndoTest::class.java.name) + } + + @Before + @Throws(Exception::class) + fun setUp() { + brickPosition = 1 + val script = UiTestUtils.createProjectAndGetStartScript(FormulaEditorUndoTest::class.java.name) + script.addBrick(PlaceAtBrick()) + userVariable = UserVariable(VARIABLE_NAME, VARIABLE_VALUE) + ProjectManager.getInstance().currentProject.addUserVariable(userVariable) + script.addBrick(SetVariableBrick(Formula(0), userVariable)) + baseActivityTestRule.launchActivity() + } + + @Category(Cat.AppUi::class, Level.Smoke::class) + @Test + fun testUndoFormulaChangesOnPressBack() { + onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at) + onView(withId(R.id.brick_place_at_edit_text_x)) + .perform(click()) + onView(withText(R.string.brick_context_dialog_formula_edit_brick)) + .perform(click()) + onFormulaEditor() + .performEnterFormula("1234") + + onView(withId(R.id.brick_place_at_edit_text_y)) + .perform(click()) + onView(withText(R.string.brick_context_dialog_formula_edit_brick)) + .perform(click()) + onFormulaEditor() + .performEnterFormula("745") + + pressBack() + + onView(withId(R.id.menu_undo)) + .check(matches(isDisplayed())) + onBrickAtPosition(brickPosition) + .onFormulaTextField(R.id.brick_place_at_edit_text_x) + .checkShowsNumber(1234) + onBrickAtPosition(brickPosition) + .onFormulaTextField(R.id.brick_place_at_edit_text_y) + .checkShowsNumber(745) + + onView(withId(R.id.menu_undo)) + .perform(click()) + onView(withId(R.id.menu_undo)) + .check(doesNotExist()) + + onBrickAtPosition(brickPosition) + .onFormulaTextField(R.id.brick_place_at_edit_text_x) + .checkShowsNumber(0) + onBrickAtPosition(brickPosition) + .onFormulaTextField(R.id.brick_place_at_edit_text_y) + .checkShowsNumber(0) + } + + @Category(Cat.AppUi::class, Level.Smoke::class) + @Test + fun testUndoFormulaWithNoChanges() { + onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at) + + onView(withId(R.id.brick_place_at_edit_text_x)) + .perform(click()) + onView(withText(R.string.brick_context_dialog_formula_edit_brick)) + .perform(click()) + + pressBack() + + onView(withId(R.id.menu_undo)) + .check(doesNotExist()) + + onBrickAtPosition(brickPosition) + .onFormulaTextField(R.id.brick_place_at_edit_text_x) + .checkShowsNumber(0) + onBrickAtPosition(brickPosition) + .onFormulaTextField(R.id.brick_place_at_edit_text_y) + .checkShowsNumber(0) + } + + @Category(Cat.AppUi::class, Level.Smoke::class) + @Test + fun testUndoFormulaWithRevertedChanges() { + onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at) + onView(withId(R.id.brick_place_at_edit_text_x)) + .perform(click()) + onView(withText(R.string.brick_context_dialog_formula_edit_brick)) + .perform(click()) + onFormulaEditor() + .performEnterFormula("1234") + + onView(withId(R.id.brick_place_at_edit_text_y)) + .perform(click()) + onView(withText(R.string.brick_context_dialog_formula_edit_brick)) + .perform(click()) + + onView(withId(R.id.brick_place_at_edit_text_x)) + .perform(click()) + onView(withText(R.string.brick_context_dialog_formula_edit_brick)).perform(click()) + onFormulaEditor().performEnterFormula("0") + + pressBack() + + onView(withId(R.id.menu_undo)) + .check(doesNotExist()) + + onBrickAtPosition(brickPosition) + .onFormulaTextField(R.id.brick_place_at_edit_text_x) + .checkShowsNumber(0) + onBrickAtPosition(brickPosition) + .onFormulaTextField(R.id.brick_place_at_edit_text_y) + .checkShowsNumber(0) + } + + @Category(Cat.AppUi::class, Level.Smoke::class) + @Test + fun testUndoFormulaAddVariable() { + onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at) + + onView(withId(R.id.brick_place_at_edit_text_x)) + .perform(click()) + onView(withText(R.string.brick_context_dialog_formula_edit_brick)) + .perform(click()) + + onFormulaEditor() + .performOpenDataFragment() + + onDataList() + .performAdd(NEW_VARIABLE_NAME) + + onDataList() + .performClose() + + pressBack() + + onView(withId(R.id.menu_undo)) + .check(matches(isDisplayed())) + + onView(withId(R.id.menu_undo)) + .perform(click()) + onView(withId(R.id.menu_undo)) + .check(doesNotExist()) + + assertNull(ProjectManager.getInstance().currentProject.getUserVariable(NEW_VARIABLE_NAME)) + + onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at) + + onView(withId(R.id.brick_place_at_edit_text_x)) + .perform(click()) + onView(withText(R.string.brick_context_dialog_formula_edit_brick)) + .perform(click()) + + onFormulaEditor() + .performOpenDataFragment() + + onDataList() + .performAdd(NEW_VARIABLE_NAME) + + onDataList() + .performClose() + + pressBack() + + onView(withId(R.id.menu_undo)) + .check(matches(isDisplayed())) + + assertNotNull(ProjectManager.getInstance().currentProject.getUserVariable(NEW_VARIABLE_NAME)) + } + + @Category(Cat.AppUi::class, Level.Smoke::class) + @Test + fun testUndoFormulaDeleteVariable() { + onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at) + + onView(withId(R.id.brick_place_at_edit_text_x)) + .perform(click()) + onView(withText(R.string.brick_context_dialog_formula_edit_brick)) + .perform(click()) + + onFormulaEditor() + .performOpenDataFragment() + + onDataList().onVariableAtPosition(0) + .performDelete() + + onDataList() + .performClose() + + pressBack() + + onView(withId(R.id.menu_undo)) + .check(matches(isDisplayed())) + + onView(withId(R.id.menu_undo)) + .perform(click()) + onView(withId(R.id.menu_undo)) + .check(doesNotExist()) + + assertNotNull(ProjectManager.getInstance().currentProject.getUserVariable(VARIABLE_NAME)) + + assertEquals(ProjectManager.getInstance().currentProject.getUserVariable(VARIABLE_NAME), userVariable) + assertEquals(ProjectManager.getInstance().currentProject.getUserVariable(VARIABLE_NAME).value, VARIABLE_VALUE) + } + + @Category(Cat.AppUi::class, Level.Smoke::class) + @Test + fun testUndoFormulaRenameVariable() { + onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at) + onView(withId(R.id.brick_place_at_edit_text_x)).perform(click()) + onView(withText(R.string.brick_context_dialog_formula_edit_brick)).perform(click()) + onFormulaEditor().performOpenDataFragment() + onDataList().onVariableAtPosition(0).performRename(NEW_VARIABLE_NAME) + onDataList().performClose() + pressBack() + + onView(withId(R.id.menu_undo)).check(matches(isDisplayed())) + + assertNull(ProjectManager.getInstance().currentProject.getUserVariable(VARIABLE_NAME)) + assertNotNull(ProjectManager.getInstance().currentProject.getUserVariable(NEW_VARIABLE_NAME)) + assertEquals(ProjectManager.getInstance().currentProject.getUserVariable(NEW_VARIABLE_NAME), userVariable) + assertEquals(ProjectManager.getInstance().currentProject.getUserVariable(NEW_VARIABLE_NAME).value, VARIABLE_VALUE) + + onView(withId(R.id.menu_undo)) + .perform(click()) + userVariable!!.name = VARIABLE_NAME + onView(withId(R.id.menu_undo)) + .check(doesNotExist()) + + assertNull(ProjectManager.getInstance().currentProject.getUserVariable(NEW_VARIABLE_NAME)) + assertNotNull(ProjectManager.getInstance().currentProject.getUserVariable(VARIABLE_NAME)) + assertEquals(ProjectManager.getInstance().currentProject.getUserVariable(VARIABLE_NAME), userVariable) + assertEquals(ProjectManager.getInstance().currentProject.getUserVariable(VARIABLE_NAME).value, VARIABLE_VALUE) + } + + @Category(Cat.AppUi::class, Level.Smoke::class) + @Test + fun testUndoFormulaEditVariable() { + onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at) + + onView(withId(R.id.brick_place_at_edit_text_x)) + .perform(click()) + onView(withText(R.string.brick_context_dialog_formula_edit_brick)) + .perform(click()) + + onFormulaEditor() + .performOpenDataFragment() + + onDataList().onVariableAtPosition(0) + .performEdit(NEW_VARIABLE_VALUE) + + onDataList() + .performClose() + + pressBack() + + onView(withId(R.id.menu_undo)) + .check(matches(isDisplayed())) + + assertEquals(ProjectManager.getInstance().currentProject.getUserVariable(VARIABLE_NAME), userVariable) + assertEquals(ProjectManager.getInstance().currentProject.getUserVariable(VARIABLE_NAME).value, NEW_VARIABLE_VALUE) + + onView(withId(R.id.menu_undo)) + .perform(click()) + onView(withId(R.id.menu_undo)) + .check(doesNotExist()) + + assertEquals(ProjectManager.getInstance().currentProject.getUserVariable(VARIABLE_NAME), userVariable) + assertEquals(ProjectManager.getInstance().currentProject.getUserVariable(VARIABLE_NAME).value, VARIABLE_VALUE) + } + + @Category(Cat.AppUi::class, Level.Smoke::class) + @Test + fun testUndoFormulaWithSpinnerVariable() { + onBrickAtPosition(2).checkShowsText(R.string.brick_set_variable) + onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at) + + onBrickAtPosition(2) + .onSpinner(R.id.set_variable_spinner) + .checkShowsText(VARIABLE_NAME) + + onView(withId(R.id.brick_place_at_edit_text_x)) + .perform(click()) + onView(withText(R.string.brick_context_dialog_formula_edit_brick)) + .perform(click()) + + onFormulaEditor() + .performOpenDataFragment() + + onDataList().onVariableAtPosition(0) + .performDelete() + + onDataList() + .performClose() + + pressBack() + + onBrickAtPosition(2) + .onSpinner(R.id.set_variable_spinner) + .checkShowsText(R.string.new_option) + + onView(withId(R.id.menu_undo)) + .check(matches(isDisplayed())) + + onView(withId(R.id.menu_undo)) + .perform(click()) + onView(withId(R.id.menu_undo)) + .check(doesNotExist()) + + onBrickAtPosition(2) + .onSpinner(R.id.set_variable_spinner) + .checkShowsText(VARIABLE_NAME) + } + + @Category(Cat.AppUi::class, Level.Smoke::class) + @Test + fun testUndoFormulaWithColorPicker() { + onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at) + + onView(withId(R.id.brick_place_at_edit_text_x)) + .perform(click()) + onView(withText(R.string.brick_context_dialog_formula_edit_brick)) + .perform(click()) + + onView(withId(R.id.formula_editor_keyboard_color_picker)).perform(click()) + + onColorPickerPresetButton(0, 0) + .perform(click()) + + closeSoftKeyboard() + + onView(withText(R.string.color_picker_apply)) + .perform(click()) + + pressBack() + + onView(withId(R.id.brick_place_at_edit_text_x)) + .check(matches(withText("'#0074CD ' "))) + onBrickAtPosition(brickPosition) + .onFormulaTextField(R.id.brick_place_at_edit_text_y) + .checkShowsNumber(0) + + onView(withId(R.id.menu_undo)) + .check(matches(isDisplayed())) + + onView(withId(R.id.menu_undo)) + .perform(click()) + onView(withId(R.id.menu_undo)) + .check(doesNotExist()) + + onBrickAtPosition(brickPosition) + .onFormulaTextField(R.id.brick_place_at_edit_text_x) + .checkShowsNumber(0) + onBrickAtPosition(brickPosition) + .onFormulaTextField(R.id.brick_place_at_edit_text_y) + .checkShowsNumber(0) + } + + @Category(Cat.AppUi::class, Level.Smoke::class) + @Test + fun testUndoAfterActivityRecreationDoesNotCrash() { + onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at) + + onView(withId(R.id.brick_place_at_edit_text_x)) + .perform(click()) + onView(withText(R.string.brick_context_dialog_formula_edit_brick)) + .perform(click()) + + onFormulaEditor() + .performOpenDataFragment() + + onDataList() + .performAdd(NEW_VARIABLE_NAME) + + onDataList() + .performClose() + + pressBack() + + onView(withId(R.id.menu_undo)) + .check(matches(isDisplayed())) + + assertNotNull(ProjectManager.getInstance().currentProject.getUserVariable(NEW_VARIABLE_NAME)) + + InstrumentationRegistry.getInstrumentation().runOnMainSync { + baseActivityTestRule.activity.recreate() + } + InstrumentationRegistry.getInstrumentation().waitForIdleSync() + + onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at) + + onView(withId(R.id.menu_undo)) + .perform(waitFor(isDisplayed(), waitThreshold)) + + onView(withId(R.id.menu_undo)) + .perform(click()) + + onView(withId(R.id.menu_undo)) + .check(doesNotExist()) + + assertNull(ProjectManager.getInstance().currentProject.getUserVariable(NEW_VARIABLE_NAME)) + + onBrickAtPosition(brickPosition) + .onFormulaTextField(R.id.brick_place_at_edit_text_x) + .checkShowsNumber(0) + } + + @Category(Cat.AppUi::class, Level.Smoke::class) + @Test + fun testUndoButtonHiddenAfterFormulaEditorUndo() { + onBrickAtPosition(brickPosition).checkShowsText(R.string.brick_place_at) + onView(withId(R.id.brick_place_at_edit_text_x)) + .perform(click()) + onView(withText(R.string.brick_context_dialog_formula_edit_brick)) + .perform(click()) + onFormulaEditor() + .performEnterFormula("1234") + + pressBack() + + onView(withId(R.id.menu_undo)) + .perform(waitFor(isDisplayed(), waitThreshold)) + + onView(withId(R.id.menu_undo)) + .perform(click()) + + onView(withId(R.id.menu_undo)) + .check(doesNotExist()) + + onBrickAtPosition(brickPosition) + .onFormulaTextField(R.id.brick_place_at_edit_text_x) + .checkShowsNumber(0) + + onView(withId(R.id.menu_undo)) + .check(doesNotExist()) + } + + companion object { + private const val VARIABLE_NAME = "TestVariable" + private const val NEW_VARIABLE_NAME = "NewVariable" + private const val VARIABLE_VALUE = 5 + private const val NEW_VARIABLE_VALUE = "10" + private const val waitThreshold = 5000L + } +} diff --git a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/ui/fragment/UndoTest.java b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/ui/fragment/UndoTest.java index b8f5be45018..72b2412e88d 100644 --- a/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/ui/fragment/UndoTest.java +++ b/catroid/src/androidTest/java/org/catrobat/catroid/uiespresso/ui/fragment/UndoTest.java @@ -136,6 +136,41 @@ public void checkScriptAfterUndo() { onBrickAtPosition(brickPosition).checkShowsText(brickText); } + @Test + public void testImmediateUndoRefresh() { + onBrickAtPosition(brickPosition).performDeleteBrick(); + + onView(withId(R.id.menu_undo)) + .perform(waitFor(isDisplayed(), waitThreshold)); + + onView(withId(R.id.menu_undo)) + .perform(click()); + + onView(withId(R.id.menu_undo)) + .check(doesNotExist()); + + onBrickAtPosition(brickPosition).checkShowsText(brickText); + } + + @Test + public void testUndoButtonHiddenAfterScriptViewUndo() { + onBrickAtPosition(brickPosition).performDeleteBrick(); + + onView(withId(R.id.menu_undo)) + .perform(waitFor(isDisplayed(), waitThreshold)); + + onView(withId(R.id.menu_undo)) + .perform(click()); + + onView(withId(R.id.menu_undo)) + .check(doesNotExist()); + + onBrickAtPosition(brickPosition).checkShowsText(brickText); + + onView(withId(R.id.menu_undo)) + .check(doesNotExist()); + } + public String getProjectAsXmlString() { return XstreamSerializer.getInstance().getXmlAsStringFromProject(ProjectManager.getInstance().getCurrentProject()); } diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/BaseActivity.kt b/catroid/src/main/java/org/catrobat/catroid/ui/BaseActivity.kt index 3f781db0e27..2d6143b2d99 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/BaseActivity.kt +++ b/catroid/src/main/java/org/catrobat/catroid/ui/BaseActivity.kt @@ -55,7 +55,7 @@ internal const val RECOVERED_FROM_CRASH = "RECOVERED_FROM_CRASH" abstract class BaseActivity : AppCompatActivity(), PermissionHandlingActivity { lateinit var optionsMenu: Menu private val permissionRequestActivityExtension = PermissionRequestActivityExtension() - private var savedInstanceStateExpected = false + protected var savedInstanceStateExpected = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) 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 865c2ab9e37..f499330634c 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/SpriteActivity.java +++ b/catroid/src/main/java/org/catrobat/catroid/ui/SpriteActivity.java @@ -139,6 +139,7 @@ public class SpriteActivity extends BaseActivity { public static final String EXTRA_FRAGMENT_POSITION = "fragmentPosition"; public static final String EXTRA_BRICK_HASH = "BRICK_HASH"; + private static final String BUNDLE_IS_UNDO_MENU_ITEM_VISIBLE = "isUndoMenuItemVisible"; public static final String EXTRA_X_TRANSFORM = "X"; public static final String EXTRA_Y_TRANSFORM = "Y"; @@ -162,6 +163,9 @@ public class SpriteActivity extends BaseActivity { @Override public void onCreate(Bundle savedInstanceState) { + if (savedInstanceState != null) { + setSavedInstanceStateExpected(true); + } super.onCreate(savedInstanceState); if (isFinishing()) { @@ -188,7 +192,13 @@ public void onCreate(Bundle savedInstanceState) { if (bundle != null) { fragmentPosition = bundle.getInt(EXTRA_FRAGMENT_POSITION, FRAGMENT_SCRIPTS); } - loadFragment(this, fragmentPosition); + + if (savedInstanceState != null) { + isUndoMenuItemVisible = savedInstanceState.getBoolean(BUNDLE_IS_UNDO_MENU_ITEM_VISIBLE, false); + invalidateOptionsMenu(); + } else { + loadFragment(this, fragmentPosition); + } addTabLayout(this, fragmentPosition); } @@ -277,6 +287,12 @@ protected void onPause() { RecentBrickListManager.getInstance().saveRecentBrickList(); } + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putBoolean(BUNDLE_IS_UNDO_MENU_ITEM_VISIBLE, isUndoMenuItemVisible); + } + @Override public void onBackPressed() { saveProject(); diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/fragment/FormulaEditorFragment.java b/catroid/src/main/java/org/catrobat/catroid/ui/fragment/FormulaEditorFragment.java index f597fbbd29c..ad2765ee517 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/fragment/FormulaEditorFragment.java +++ b/catroid/src/main/java/org/catrobat/catroid/ui/fragment/FormulaEditorFragment.java @@ -932,7 +932,7 @@ public void exitFormulaEditorFragment() { XstreamSerializer.getInstance().saveProject(ProjectManager.getInstance().getCurrentProject()); - if (hasFileChanged() || fragment.checkVariables()) { + if (hasFileChanged() || (fragment != null && fragment.checkVariables())) { ((SpriteActivity) getActivity()).setUndoMenuItemVisibility(true); } } 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 0cdce4084e3..059d7bdb59e 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 @@ -413,7 +413,9 @@ public void onPause() { savedListViewState = listView.onSaveInstanceState(); - ((SpriteActivity) getActivity()).setUndoMenuItemVisibility(false); + if (getActivity() != null && !getActivity().isChangingConfigurations()) { + ((SpriteActivity) getActivity()).setUndoMenuItemVisibility(false); + } } @Override @@ -903,22 +905,76 @@ public void loadProjectAfterUndoOption() { Project project = ProjectManager.getInstance().getCurrentProject(); File currentCodeFile = new File(project.getDirectory(), CODE_XML_FILE_NAME); File undoCodeFile = new File(project.getDirectory(), UNDO_CODE_XML_FILE_NAME); + Context context = getContext(); + + if (!isAdded() || context == null) { + Log.w(TAG, "Cannot load project after undo because fragment is not attached."); + return; + } + + if (!undoCodeFile.exists()) { + Log.e(TAG, "Undo code file " + UNDO_CODE_XML_FILE_NAME + " does not exist."); + ToastUtil.showError(context, R.string.error_load_project); + return; + } if (currentCodeFile.exists()) { try { StorageOperations.transferData(undoCodeFile, currentCodeFile); - new ProjectLoader(project.getDirectory(), getContext()).setListener(this).loadProjectAsync(); + SpriteActivity spriteActivity = (SpriteActivity) getActivity(); + if (spriteActivity != null) { + spriteActivity.setUndoMenuItemVisibility(false); + spriteActivity.showUndo(false); + } + new ProjectLoader(project.getDirectory(), context).setListener(this).loadProjectAsync(); } catch (IOException exception) { - Log.e(TAG, "Replaceing project " + project.getName() + " failed.", exception); + Log.e(TAG, "Replacing project " + project.getName() + " failed.", exception); + ToastUtil.showError(context, R.string.error_load_project); + SpriteActivity spriteActivity = (SpriteActivity) getActivity(); + if (spriteActivity != null && undoCodeFile.exists()) { + spriteActivity.setUndoMenuItemVisibility(true); + spriteActivity.showUndo(true); + } } } } + @Override public void onLoadFinished(boolean success) { - ProjectManager.getInstance().setCurrentSceneAndSprite(currentSceneName, currentSpriteName); - if (checkVariables()) { - loadVariables(); + if (!isAdded() || getContext() == null) { + return; + } + + SpriteActivity spriteActivity = (SpriteActivity) getActivity(); + if (!success) { + Log.e(TAG, "Loading project after undo failed."); + ToastUtil.showError(getContext(), R.string.error_load_project); + if (spriteActivity != null) { + spriteActivity.setUndoMenuItemVisibility(true); + spriteActivity.showUndo(true); + } + return; + } + + if (!ProjectManager.getInstance().setCurrentSceneAndSprite(currentSceneName, currentSpriteName)) { + Log.e(TAG, "Could not set scene/sprite after undo: " + currentSceneName + "/" + currentSpriteName); + } + + loadVariables(); + + if (spriteActivity != null) { + spriteActivity.setUndoMenuItemVisibility(false); + spriteActivity.showUndo(false); + } + + File undoCodeFile = new File(ProjectManager.getInstance().getCurrentProject().getDirectory(), UNDO_CODE_XML_FILE_NAME); + if (undoCodeFile.exists() && !undoCodeFile.delete()) { + Log.w(TAG, "Could not delete undo code file: " + undoCodeFile.getAbsolutePath()); + } + + if (getView() == null || listView == null) { + return; } refreshFragmentAfterUndo(); } @@ -940,7 +996,33 @@ public boolean checkVariables() { Sprite currentSprite = projectManager.getCurrentSprite(); Project project = projectManager.getCurrentProject(); - return (project.hasUserDataChanged(project.getUserVariables(), savedUserVariables) || project.hasUserDataChanged(project.getMultiplayerVariables(), savedMultiplayerVariables) || project.hasUserDataChanged(project.getUserLists(), savedUserLists) || currentSprite.hasUserDataChanged(currentSprite.getUserVariables(), savedLocalUserVariables) || currentSprite.hasUserDataChanged(currentSprite.getUserLists(), savedLocalLists)); + return (project != null && hasProjectVariablesChanged(project)) + || (currentSprite != null && hasSpriteVariablesChanged(currentSprite)); + } + + private boolean hasProjectVariablesChanged(Project project) { + boolean changed = false; + if (savedUserVariables != null && project.getUserVariables() != null) { + changed |= project.hasUserDataChanged(project.getUserVariables(), savedUserVariables); + } + if (savedMultiplayerVariables != null && project.getMultiplayerVariables() != null) { + changed |= project.hasUserDataChanged(project.getMultiplayerVariables(), savedMultiplayerVariables); + } + if (savedUserLists != null && project.getUserLists() != null) { + changed |= project.hasUserDataChanged(project.getUserLists(), savedUserLists); + } + return changed; + } + + private boolean hasSpriteVariablesChanged(Sprite currentSprite) { + boolean changed = false; + if (savedLocalUserVariables != null && currentSprite.getUserVariables() != null) { + changed |= currentSprite.hasUserDataChanged(currentSprite.getUserVariables(), savedLocalUserVariables); + } + if (savedLocalLists != null && currentSprite.getUserLists() != null) { + changed |= currentSprite.hasUserDataChanged(currentSprite.getUserLists(), savedLocalLists); + } + return changed; } private void loadVariables() { @@ -948,20 +1030,49 @@ private void loadVariables() { Sprite currentSprite = projectManager.getCurrentSprite(); Project project = projectManager.getCurrentProject(); - project.restoreUserDataValues(project.getUserVariables(), savedUserVariables); - project.restoreUserDataValues(project.getMultiplayerVariables(), savedMultiplayerVariables); - project.restoreUserDataValues(project.getUserLists(), savedUserLists); - currentSprite.restoreUserDataValues(currentSprite.getUserVariables(), savedLocalUserVariables); - currentSprite.restoreUserDataValues(currentSprite.getUserLists(), savedLocalLists); + if (project != null) { + if (savedUserVariables != null) { + project.restoreUserDataValues(project.getUserVariables(), savedUserVariables); + } + if (savedMultiplayerVariables != null) { + project.restoreUserDataValues(project.getMultiplayerVariables(), savedMultiplayerVariables); + } + if (savedUserLists != null) { + project.restoreUserDataValues(project.getUserLists(), savedUserLists); + } + } + if (currentSprite != null) { + if (savedLocalUserVariables != null) { + currentSprite.restoreUserDataValues(currentSprite.getUserVariables(), savedLocalUserVariables); + } + if (savedLocalLists != null) { + currentSprite.restoreUserDataValues(currentSprite.getUserLists(), savedLocalLists); + } + } } private void refreshFragmentAfterUndo() { - Fragment scriptFragment = getActivity().getSupportFragmentManager().findFragmentByTag(TAG); - final FragmentTransaction fragmentTransaction = getActivity().getSupportFragmentManager().beginTransaction(); + if (!isAdded() || getActivity() == null || getParentFragmentManager().isStateSaved()) { + return; + } + Fragment scriptFragment = getParentFragmentManager().findFragmentByTag(TAG); + if (scriptFragment == null) { + return; + } + + Sprite currentSprite = ProjectManager.getInstance().getCurrentSprite(); + if (adapter != null && currentSprite != null) { + adapter.updateItems(currentSprite); + } + + final FragmentTransaction fragmentTransaction = getParentFragmentManager().beginTransaction(); fragmentTransaction.detach(scriptFragment); fragmentTransaction.attach(scriptFragment); - fragmentTransaction.commit(); - if (undoBrickPosition < listView.getFirstVisiblePosition() || undoBrickPosition > listView.getLastVisiblePosition()) { + fragmentTransaction.commitNow(); + + if (listView != null + && (undoBrickPosition < listView.getFirstVisiblePosition() + || undoBrickPosition > listView.getLastVisiblePosition())) { listView.post(() -> listView.setSelection(undoBrickPosition)); } }