Skip to content

Commit 5d75ac2

Browse files
authored
Paintroid-160: Create file format .catrobat-image / save/load .catrobat-image. (#800)
Saving files as Openraster file format and also being able to load them. Implemented it into the Save dialog. (https://www.openraster.org/baseline/file-layout-spec.html) Also added new test class for OraFile testing. Behaviour: When saving a file you have a new option in the spinner menu called "ora". It saves layers. And on load opens the layers again. Can also load other ora files created by other application like mypaint. Apis: If the api < 29 the picture will be saved in the "normal" image folder. otherwise it will be saved into the downloads folder in a subdirectory called "Pocket_Paint" since it is not really possible to store it in the image folder. (this was already discussed) ### Your checklist for this pull request Please review the [contributing guidelines](https://github.com/Catrobat/Paintroid/blob/develop/README.md) and [wiki pages](https://github.com/Catrobat/Catroid/wiki/) of this repository. - [x] Include the name of the Jira ticket in the PR’s title - [x] Include a summary of the changes plus the relevant context - [x] Choose the proper base branch (*develop*) - [x] Confirm that the changes follow the project’s coding guidelines - [x] Verify that the changes generate no compiler or linter warnings - [x] Perform a self-review of the changes - [x] Verify to commit no other files than the intentionally changed ones - [x] Include reasonable and readable tests verifying the added or changed behavior - [x] Confirm that new and existing unit tests pass locally - [x] Check that the commits’ message style matches the [project’s guideline](https://github.com/Catrobat/Catroid/wiki/Commit-Message-Guidelines) - [x] Stick to the project’s gitflow workflow - [x] Verify that your changes do not have any conflicts with the base branch - [x] After the PR, verify that all CI checks have passed - [x] Post a message in the *#paintroid* [Slack channel](https://catrobat.slack.com) and ask for a code reviewer
1 parent 062e4e1 commit 5d75ac2

26 files changed

+907
-98
lines changed

Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/LayerIntegrationTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* Paintroid: An image manipulation application for Android.
3-
* Copyright (C) 2010-2015 The Catrobat Team
3+
* Copyright (C) 2010-2021 The Catrobat Team
44
* (<http://developer.catrobat.org/credits>)
55
*
66
* This program is free software: you can redistribute it and/or modify
@@ -59,6 +59,7 @@
5959

6060
@RunWith(AndroidJUnit4.class)
6161
public class LayerIntegrationTest {
62+
6263
@Rule
6364
public ActivityTestRule<MainActivity> launchActivityRule = new ActivityTestRule<>(MainActivity.class);
6465

Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MenuFileActivityIntegrationTest.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* Paintroid: An image manipulation application for Android.
3-
* Copyright (C) 2010-2015 The Catrobat Team
3+
* Copyright (C) 2010-2021 The Catrobat Team
44
* (<http://developer.catrobat.org/credits>)
55
*
66
* This program is free software: you can redistribute it and/or modify
@@ -348,15 +348,15 @@ public void testSaveCopy() {
348348

349349
onView(withText(R.string.save_button_text)).perform(click());
350350

351-
onView(withText(R.string.pocketpaint_no)).perform(click());
352-
onView(withText(R.string.pocketpaint_ok)).perform(click());
353-
354351
assertNotNull(activity.model.getSavedPictureUri());
355352

356353
addUriToDeletionFileList(activity.model.getSavedPictureUri());
357354

358355
File oldFile = new File(activity.model.getSavedPictureUri().toString());
359356

357+
onView(withText(R.string.pocketpaint_no)).perform(click());
358+
onView(withText(R.string.pocketpaint_ok)).perform(click());
359+
360360
onDrawingSurfaceView()
361361
.perform(touchAt(DrawingSurfaceLocationProvider.HALFWAY_BOTTOM_MIDDLE));
362362

@@ -384,6 +384,10 @@ public void testAskForSaveAfterSavedOnce() {
384384
.performOpenMoreOptions();
385385

386386
onView(withText(R.string.menu_save_image)).perform(click());
387+
388+
onView(withId(R.id.pocketpaint_image_name_save_text))
389+
.perform(replaceText("AskForSaveAfterSavedOnce"));
390+
387391
onView(withText(R.string.save_button_text)).perform(click());
388392

389393
assertNotNull(activity.model.getSavedPictureUri());
@@ -518,6 +522,9 @@ public void testCheckSaveFileWithDifferentFormats() {
518522
onView(withId(R.id.pocketpaint_image_name_save_text))
519523
.perform(replaceText(Constants.TEMP_PICTURE_NAME));
520524

525+
onView(withText(R.string.save_button_text))
526+
.perform(click());
527+
521528
assertNotNull(activity.model.getSavedPictureUri());
522529
addUriToDeletionFileList(activity.model.getSavedPictureUri());
523530

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/*
2+
* Paintroid: An image manipulation application for Android.
3+
* Copyright (C) 2010-2021 The Catrobat Team
4+
* (<http://developer.catrobat.org/credits>)
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU Affero General Public License as
8+
* published by the Free Software Foundation, either version 3 of the
9+
* License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Affero General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Affero General Public License
17+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
package org.catrobat.paintroid.test.espresso;
20+
21+
import android.app.Activity;
22+
import android.app.Instrumentation;
23+
import android.content.Intent;
24+
import android.net.Uri;
25+
26+
import org.catrobat.paintroid.MainActivity;
27+
import org.catrobat.paintroid.R;
28+
import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider;
29+
import org.catrobat.paintroid.test.espresso.util.EspressoUtils;
30+
import org.hamcrest.core.AllOf;
31+
import org.junit.After;
32+
import org.junit.Before;
33+
import org.junit.ClassRule;
34+
import org.junit.Rule;
35+
import org.junit.Test;
36+
import org.junit.runner.RunWith;
37+
38+
import java.io.File;
39+
import java.util.ArrayList;
40+
import java.util.Objects;
41+
42+
import androidx.test.espresso.intent.rule.IntentsTestRule;
43+
import androidx.test.ext.junit.runners.AndroidJUnit4;
44+
import androidx.test.rule.GrantPermissionRule;
45+
46+
import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt;
47+
import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView;
48+
import static org.catrobat.paintroid.test.espresso.util.wrappers.LayerMenuViewInteraction.onLayerMenuView;
49+
import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView;
50+
import static org.hamcrest.Matchers.instanceOf;
51+
import static org.hamcrest.Matchers.is;
52+
import static org.hamcrest.core.AllOf.allOf;
53+
import static org.junit.Assert.assertNotNull;
54+
import static org.junit.Assert.assertTrue;
55+
56+
import static androidx.test.espresso.Espresso.onData;
57+
import static androidx.test.espresso.Espresso.onView;
58+
import static androidx.test.espresso.action.ViewActions.click;
59+
import static androidx.test.espresso.action.ViewActions.replaceText;
60+
import static androidx.test.espresso.assertion.ViewAssertions.matches;
61+
import static androidx.test.espresso.intent.Intents.intending;
62+
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;
63+
import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup;
64+
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
65+
import static androidx.test.espresso.matcher.ViewMatchers.withId;
66+
import static androidx.test.espresso.matcher.ViewMatchers.withText;
67+
68+
@RunWith(AndroidJUnit4.class)
69+
public class OraFileFormatIntegrationTest {
70+
71+
private static ArrayList<File> deletionFileList = null;
72+
73+
@Rule
74+
public IntentsTestRule<MainActivity> launchActivityRule = new IntentsTestRule<>(MainActivity.class);
75+
76+
@ClassRule
77+
public static GrantPermissionRule grantPermissionRule = EspressoUtils.grantPermissionRulesVersionCheck();
78+
79+
private MainActivity activity;
80+
81+
@Before
82+
public void setUp() {
83+
deletionFileList = new ArrayList<>();
84+
activity = launchActivityRule.getActivity();
85+
}
86+
87+
@After
88+
public void tearDown() {
89+
for (File file : deletionFileList) {
90+
if (file != null && file.exists()) {
91+
assertTrue(file.delete());
92+
}
93+
}
94+
}
95+
96+
@Test
97+
public void testSaveAsOraFile() {
98+
onDrawingSurfaceView()
99+
.perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE));
100+
101+
onTopBarView()
102+
.performOpenMoreOptions();
103+
onView(withText(R.string.menu_save_image))
104+
.perform(click());
105+
106+
onView(withId(R.id.pocketpaint_save_dialog_spinner))
107+
.perform(click());
108+
onData(allOf(is(instanceOf(String.class)),
109+
is("ora"))).inRoot(isPlatformPopup()).perform(click());
110+
onView(withId(R.id.pocketpaint_image_name_save_text))
111+
.perform(replaceText("test1337"));
112+
113+
onView(withText(R.string.save_button_text))
114+
.perform(click());
115+
116+
assertNotNull(activity.model.getSavedPictureUri());
117+
addUriToDeletionFileList(activity.model.getSavedPictureUri());
118+
}
119+
120+
@Test
121+
public void testSaveAndOverrideOraFile() {
122+
onDrawingSurfaceView()
123+
.perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE));
124+
125+
onTopBarView()
126+
.performOpenMoreOptions();
127+
onView(withText(R.string.menu_save_image))
128+
.perform(click());
129+
130+
onView(withId(R.id.pocketpaint_save_dialog_spinner))
131+
.perform(click());
132+
onData(allOf(is(instanceOf(String.class)),
133+
is("ora"))).inRoot(isPlatformPopup()).perform(click());
134+
135+
onView(withId(R.id.pocketpaint_image_name_save_text))
136+
.perform(replaceText("OraOverride"));
137+
138+
onView(withText(R.string.save_button_text))
139+
.perform(click());
140+
141+
onView(withText(R.string.pocketpaint_no)).perform(click());
142+
onView(withText(R.string.pocketpaint_ok)).perform(click());
143+
144+
onDrawingSurfaceView()
145+
.perform(touchAt(DrawingSurfaceLocationProvider.BOTTOM_MIDDLE));
146+
147+
onTopBarView()
148+
.performOpenMoreOptions();
149+
onView(withText(R.string.menu_save_image))
150+
.perform(click());
151+
152+
onView(withText(R.string.save_button_text))
153+
.perform(click());
154+
155+
onView(withText(R.string.pocketpaint_overwrite_title))
156+
.check(matches(isDisplayed()));
157+
158+
onView(withText(R.string.overwrite_button_text))
159+
.perform(click());
160+
161+
Uri imageUri = activity.model.getSavedPictureUri();
162+
163+
assertNotNull(imageUri);
164+
addUriToDeletionFileList(imageUri);
165+
}
166+
167+
@Test
168+
public void testOraFileWithMultipleLayersSaveAndLoad() {
169+
onDrawingSurfaceView()
170+
.perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE));
171+
172+
onLayerMenuView()
173+
.performOpen()
174+
.performAddLayer()
175+
.checkLayerCount(2)
176+
.performClose();
177+
178+
onDrawingSurfaceView()
179+
.perform(touchAt(DrawingSurfaceLocationProvider.TOP_MIDDLE));
180+
181+
onLayerMenuView()
182+
.performOpen()
183+
.performAddLayer()
184+
.checkLayerCount(3)
185+
.performClose();
186+
187+
onDrawingSurfaceView()
188+
.perform(touchAt(DrawingSurfaceLocationProvider.BOTTOM_MIDDLE));
189+
190+
onTopBarView()
191+
.performOpenMoreOptions();
192+
193+
onView(withText(R.string.menu_save_image))
194+
.perform(click());
195+
196+
onView(withId(R.id.pocketpaint_save_dialog_spinner))
197+
.perform(click());
198+
onData(AllOf.allOf(is(instanceOf(String.class)),
199+
is("ora"))).inRoot(isPlatformPopup()).perform(click());
200+
onView(withId(R.id.pocketpaint_image_name_save_text))
201+
.perform(replaceText("MoreLayersOraTest"));
202+
203+
onView(withText(R.string.save_button_text))
204+
.perform(click());
205+
206+
Uri fileUri = activity.model.getSavedPictureUri();
207+
208+
assertNotNull(fileUri);
209+
addUriToDeletionFileList(fileUri);
210+
211+
Intent intent = new Intent();
212+
intent.setData(fileUri);
213+
Instrumentation.ActivityResult resultOK = new Instrumentation.ActivityResult(Activity.RESULT_OK, intent);
214+
intending(hasAction(Intent.ACTION_GET_CONTENT)).respondWith(resultOK);
215+
216+
onTopBarView()
217+
.performOpenMoreOptions();
218+
219+
onView(withText(R.string.menu_load_image))
220+
.perform(click());
221+
222+
onLayerMenuView()
223+
.performOpen()
224+
.checkLayerCount(3);
225+
}
226+
227+
private void addUriToDeletionFileList(Uri uri) {
228+
deletionFileList.add(new File(Objects.requireNonNull(uri.getPath())));
229+
}
230+
}

Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/catroid/OpenedFromPocketCodeNewImageTest.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
/*
2-
* Paintroid: An image manipulation application for Android.
3-
* Copyright (C) 2010-2015 The Catrobat Team
4-
* (<http://developer.catrobat.org/credits>)
2+
* Paintroid: An image manipulation application for Android.
3+
* Copyright (C) 2010-2021 The Catrobat Team
4+
* (<http://developer.catrobat.org/credits>)
55
*
6-
* This program is free software: you can redistribute it and/or modify
7-
* it under the terms of the GNU Affero General Public License as
8-
* published by the Free Software Foundation, either version 3 of the
9-
* License, or (at your option) any later version.
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU Affero General Public License as
8+
* published by the Free Software Foundation, either version 3 of the
9+
* License, or (at your option) any later version.
1010
*
11-
* This program is distributed in the hope that it will be useful,
12-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14-
* GNU Affero General Public License for more details.
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Affero General Public License for more details.
1515
*
16-
* You should have received a copy of the GNU Affero General Public License
17-
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
* You should have received a copy of the GNU Affero General Public License
17+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
*/
1919

2020
package org.catrobat.paintroid.test.espresso.catroid;

Paintroid/src/main/AndroidManifest.xml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323

2424
<uses-permission android:name="android.permission.INTERNET"/>
2525
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
26-
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
27-
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
26+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
27+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28"/>
2828

2929
<application
3030
android:largeHeap="true"
@@ -54,6 +54,9 @@
5454
<category android:name="android.intent.category.DEFAULT" />
5555

5656
<data android:mimeType="image/*" />
57+
<data android:scheme="file" />
58+
<data android:host="*"/>
59+
<data android:pathPattern=".*\\.ora" />
5760
</intent-filter>
5861
<intent-filter>
5962
<action android:name="android.intent.action.SEND" />

0 commit comments

Comments
 (0)