Skip to content

Commit 9f5d72b

Browse files
authored
Merge pull request #458 from opendocument-app/issue/447-docx-editing
Investigate DOCX editing
2 parents c4a0775 + b50641b commit 9f5d72b

File tree

3 files changed

+171
-1
lines changed

3 files changed

+171
-1
lines changed
79.6 KB
Binary file not shown.

app/src/androidTest/java/at/tomtasche/reader/test/CoreTest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public class CoreTest {
3030
private File m_testFile;
3131
private File m_passwordTestFile;
3232
private File m_spreadsheetTestFile;
33+
private File m_docxTestFile;
3334

3435
@BeforeClass
3536
public static void startServer() throws InterruptedException {
@@ -89,6 +90,10 @@ public void extractTestFile() throws IOException {
8990
try (InputStream inputStream = assetManager.open("spreadsheet-test.ods")) {
9091
copy(inputStream, m_spreadsheetTestFile);
9192
}
93+
m_docxTestFile = new File(appCtx.getCacheDir(), "style-various-1.docx");
94+
try (InputStream inputStream = assetManager.open("style-various-1.docx")) {
95+
copy(inputStream, m_docxTestFile);
96+
}
9297
}
9398

9499
@After
@@ -102,6 +107,9 @@ public void cleanupTestFile() {
102107
if (null != m_spreadsheetTestFile) {
103108
m_spreadsheetTestFile.delete();
104109
}
110+
if (null != m_docxTestFile) {
111+
m_docxTestFile.delete();
112+
}
105113
}
106114

107115
private static void copy(InputStream src, File dst) throws IOException {
@@ -138,6 +146,30 @@ public void test() {
138146
Assert.assertEquals(0, result.errorCode);
139147
}
140148

149+
@Test
150+
public void testDocxEdit() {
151+
File cacheDir = InstrumentationRegistry.getInstrumentation().getTargetContext().getCacheDir();
152+
File outputPath = new File(cacheDir, "core_output_docx");
153+
File cachePath = new File(cacheDir, "core_cache");
154+
155+
CoreWrapper.CoreOptions coreOptions = new CoreWrapper.CoreOptions();
156+
coreOptions.inputPath = m_docxTestFile.getAbsolutePath();
157+
coreOptions.outputPath = outputPath.getPath();
158+
coreOptions.editable = true;
159+
coreOptions.cachePath = cachePath.getPath();
160+
161+
CoreWrapper.CoreResult coreResult = CoreWrapper.hostFile("docx-edit", coreOptions);
162+
Assert.assertEquals(0, coreResult.errorCode);
163+
164+
File resultFile = new File(cacheDir, "result_docx");
165+
coreOptions.outputPath = resultFile.getPath();
166+
167+
String htmlDiff = "{\"modifiedText\":{\"/child:16/child:0/child:0\":\"Outasdfsdafdline\",\"/child:24/child:0/child:0\":\"Colorasdfasdfasdfed Line\",\"/child:6/child:0/child:0\":\"Text hello world!\"}}";
168+
169+
CoreWrapper.CoreResult result = CoreWrapper.backtranslate(coreOptions, htmlDiff);
170+
Assert.assertEquals(0, result.errorCode);
171+
}
172+
141173
@Test
142174
public void testPasswordProtectedDocumentWithoutPassword() {
143175
File cacheDir = InstrumentationRegistry.getInstrumentation().getTargetContext().getCacheDir();

app/src/androidTest/java/at/tomtasche/reader/test/MainActivityTests.java

Lines changed: 139 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import android.content.Intent;
2323
import android.content.res.AssetManager;
2424
import android.net.Uri;
25+
import android.os.SystemClock;
2526
import android.util.ArrayMap;
2627
import android.util.Log;
2728

@@ -49,9 +50,15 @@
4950
import java.io.InputStream;
5051
import java.io.OutputStream;
5152
import java.util.Map;
53+
import java.util.concurrent.CountDownLatch;
54+
import java.util.concurrent.TimeUnit;
55+
import java.util.concurrent.atomic.AtomicReference;
5256

5357
import at.tomtasche.reader.R;
58+
import at.tomtasche.reader.ui.EditActionModeCallback;
5459
import at.tomtasche.reader.ui.activity.MainActivity;
60+
import at.tomtasche.reader.ui.activity.DocumentFragment;
61+
import at.tomtasche.reader.ui.widget.PageView;
5562

5663
@LargeTest
5764
@RunWith(AndroidJUnit4.class)
@@ -126,7 +133,7 @@ public static void extractTestFiles() throws IOException {
126133

127134
AssetManager testAssetManager = instrumentation.getContext().getAssets();
128135

129-
for (String filename: new String[] {"test.odt", "dummy.pdf", "password-test.odt"}) {
136+
for (String filename: new String[] {"test.odt", "dummy.pdf", "password-test.odt", "style-various-1.docx"}) {
130137
File targetFile = new File(testDocumentsDir, filename);
131138
try (InputStream inputStream = testAssetManager.open(filename)) {
132139
copy(inputStream, targetFile);
@@ -277,4 +284,135 @@ public void testPasswordProtectedODT() {
277284
.perform(click());
278285
});
279286
}
287+
288+
@Test
289+
public void testODTEditMode() throws InterruptedException {
290+
File testFile = s_testFiles.get("test.odt");
291+
Assert.assertNotNull(testFile);
292+
MainActivity activity = mainActivityActivityTestRule.getActivity();
293+
DocumentFragment documentFragment = loadDocument(activity, testFile);
294+
enterEditMode(activity, documentFragment);
295+
296+
PageView pageView = documentFragment.getPageView();
297+
Assert.assertNotNull(pageView);
298+
Assert.assertTrue(
299+
"ODT should become editable after entering edit mode",
300+
waitForEditableState(pageView, true, 10000)
301+
);
302+
}
303+
304+
@Test
305+
public void testDOCXEditMode() throws InterruptedException {
306+
File testFile = s_testFiles.get("style-various-1.docx");
307+
Assert.assertNotNull(testFile);
308+
MainActivity activity = mainActivityActivityTestRule.getActivity();
309+
DocumentFragment documentFragment = loadDocument(activity, testFile);
310+
enterEditMode(activity, documentFragment);
311+
312+
PageView pageView = documentFragment.getPageView();
313+
Assert.assertNotNull(pageView);
314+
315+
Assert.assertTrue(
316+
"DOCX should become editable after entering edit mode",
317+
waitForEditableState(pageView, true, 10000)
318+
);
319+
}
320+
321+
private DocumentFragment loadDocument(MainActivity activity, File testFile) throws InterruptedException {
322+
Context appCtx = InstrumentationRegistry.getInstrumentation().getTargetContext();
323+
Uri testFileUri = FileProvider.getUriForFile(appCtx, appCtx.getPackageName() + ".provider", testFile);
324+
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> activity.loadUri(testFileUri));
325+
326+
DocumentFragment fragment = waitForDocumentFragment(activity, 10000);
327+
Assert.assertNotNull(fragment);
328+
Assert.assertTrue("Timed out waiting for document to load", waitForLastResult(fragment, 10000));
329+
return fragment;
330+
}
331+
332+
private DocumentFragment waitForDocumentFragment(MainActivity activity, long timeoutMs)
333+
throws InterruptedException {
334+
long startMs = SystemClock.elapsedRealtime();
335+
DocumentFragment fragment;
336+
do {
337+
fragment = (DocumentFragment) activity.getSupportFragmentManager()
338+
.findFragmentByTag("document_fragment");
339+
if (fragment != null) {
340+
return fragment;
341+
}
342+
SystemClock.sleep(100);
343+
} while (SystemClock.elapsedRealtime() - startMs < timeoutMs);
344+
return null;
345+
}
346+
347+
private boolean waitForLastResult(DocumentFragment fragment, long timeoutMs) throws InterruptedException {
348+
long startMs = SystemClock.elapsedRealtime();
349+
while (SystemClock.elapsedRealtime() - startMs < timeoutMs) {
350+
if (fragment.hasLastResult()) {
351+
return true;
352+
}
353+
SystemClock.sleep(100);
354+
}
355+
return false;
356+
}
357+
358+
private void enterEditMode(MainActivity activity, DocumentFragment documentFragment) {
359+
AtomicReference<Boolean> started = new AtomicReference<>(false);
360+
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
361+
started.set(activity.startSupportActionMode(
362+
new EditActionModeCallback(activity, documentFragment)) != null);
363+
});
364+
Assert.assertTrue("Failed to enter edit mode", started.get());
365+
}
366+
367+
private boolean waitForEditableState(PageView pageView, boolean expected, long timeoutMs)
368+
throws InterruptedException {
369+
long startMs = SystemClock.elapsedRealtime();
370+
while (SystemClock.elapsedRealtime() - startMs < timeoutMs) {
371+
if (expected == isEditableDom(pageView)) {
372+
return true;
373+
}
374+
SystemClock.sleep(250);
375+
}
376+
return false;
377+
}
378+
379+
private boolean waitForNonEditableState(PageView pageView, long timeoutMs) throws InterruptedException {
380+
long startMs = SystemClock.elapsedRealtime();
381+
while (SystemClock.elapsedRealtime() - startMs < timeoutMs) {
382+
if (isEditableDom(pageView)) {
383+
return false;
384+
}
385+
SystemClock.sleep(250);
386+
}
387+
return true;
388+
}
389+
390+
private boolean isEditableDom(PageView pageView) throws InterruptedException {
391+
String result = evaluateJavascript(pageView,
392+
"(function(){"
393+
+ "var bodyEditable = document.body && document.body.isContentEditable;"
394+
+ "var editableNode = document.querySelector('[contenteditable=\"true\"], [contenteditable=\"plaintext-only\"]');"
395+
+ "return !!(bodyEditable || editableNode);"
396+
+ "})()");
397+
if (result == null) {
398+
return false;
399+
}
400+
String normalized = result.replace("\"", "");
401+
return "true".equalsIgnoreCase(normalized);
402+
}
403+
404+
private String evaluateJavascript(PageView pageView, String script) throws InterruptedException {
405+
AtomicReference<String> result = new AtomicReference<>();
406+
CountDownLatch latch = new CountDownLatch(1);
407+
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
408+
pageView.evaluateJavascript(script, value -> {
409+
result.set(value);
410+
latch.countDown();
411+
});
412+
});
413+
if (!latch.await(10, TimeUnit.SECONDS)) {
414+
Assert.fail("Timed out waiting for JS evaluation result");
415+
}
416+
return result.get();
417+
}
280418
}

0 commit comments

Comments
 (0)