Mholloway has uploaded a new change for review.
https://gerrit.wikimedia.org/r/317281
Change subject: Retrofit editing
......................................................................
Retrofit editing
Change-Id: I223090e67bb6206817041ce3669281dbae3cfdff
---
D app/src/androidTest/java/org/wikipedia/editing/EditPreviewTaskTest.java
A app/src/androidTest/java/org/wikipedia/editing/EditPreviewTest.java
A app/src/androidTest/java/org/wikipedia/editing/EditTest.java
A app/src/androidTest/java/org/wikipedia/editing/FetchSectionWikitextTests.java
M app/src/androidTest/java/org/wikipedia/test/CreateAccountTaskTest.java
D app/src/androidTest/java/org/wikipedia/test/EditTaskTest.java
D app/src/androidTest/java/org/wikipedia/test/FetchSectionWikitextTaskTests.java
A app/src/main/java/org/wikipedia/captcha/Captcha.java
A app/src/main/java/org/wikipedia/captcha/CaptchaClient.java
R app/src/main/java/org/wikipedia/captcha/CaptchaHandler.java
R app/src/main/java/org/wikipedia/captcha/CaptchaResult.java
A app/src/main/java/org/wikipedia/editing/Edit.java
A app/src/main/java/org/wikipedia/editing/EditClient.java
A app/src/main/java/org/wikipedia/editing/EditPreview.java
M app/src/main/java/org/wikipedia/editing/EditPreviewFragment.java
D app/src/main/java/org/wikipedia/editing/EditPreviewTask.java
M app/src/main/java/org/wikipedia/editing/EditSectionActivity.java
D app/src/main/java/org/wikipedia/editing/EditTask.java
A app/src/main/java/org/wikipedia/editing/EditToken.java
M app/src/main/java/org/wikipedia/editing/EditTokenStorage.java
D app/src/main/java/org/wikipedia/editing/FetchEditTokenTask.java
D app/src/main/java/org/wikipedia/editing/FetchSectionWikitextTask.java
D app/src/main/java/org/wikipedia/editing/RefreshCaptchaTask.java
A app/src/main/java/org/wikipedia/editing/Wikitext.java
A app/src/main/java/org/wikipedia/server/mwapi/ApiResponsePage.java
25 files changed, 890 insertions(+), 653 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/apps/android/wikipedia
refs/changes/81/317281/1
diff --git
a/app/src/androidTest/java/org/wikipedia/editing/EditPreviewTaskTest.java
b/app/src/androidTest/java/org/wikipedia/editing/EditPreviewTaskTest.java
deleted file mode 100644
index cc24ec9..0000000
--- a/app/src/androidTest/java/org/wikipedia/editing/EditPreviewTaskTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package org.wikipedia.editing;
-
-import android.support.annotation.NonNull;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.wikipedia.dataclient.WikiSite;
-import org.wikipedia.WikipediaApp;
-import org.wikipedia.page.PageTitle;
-import org.wikipedia.testlib.TestLatch;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsString;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class EditPreviewTaskTest {
- @Test
- public void testPreview() throws Throwable {
- final PageTitle title = new PageTitle(null,
"Test_page_for_app_testing/Section1", WikiSite.forLanguageCode("test"));
- long randomTime = System.currentTimeMillis();
- final String wikiText = "== Section 2 ==\n\nEditing section INSERT
RANDOM & HERE test at " + randomTime;
-
- String result = Subject.execute(wikiText, title);
- assertThat(result, containsString(String.valueOf(randomTime)));
- }
-
- private static class Subject extends EditPreviewTask {
- public static String execute(String wikiText, PageTitle title) {
- Subject subject = new Subject(wikiText, title);
- subject.execute();
- return subject.await();
- }
-
- @NonNull private final TestLatch latch = new TestLatch();
- private String result;
-
- Subject(String wikiText, PageTitle title) {
- super(WikipediaApp.getInstance(), wikiText, title);
- }
-
- @Override
- public void onFinish(String result) {
- super.onFinish(result);
- this.result = result;
- latch.countDown();
- }
-
- public String await() {
- latch.await();
- return result;
- }
- }
-}
diff --git
a/app/src/androidTest/java/org/wikipedia/editing/EditPreviewTest.java
b/app/src/androidTest/java/org/wikipedia/editing/EditPreviewTest.java
new file mode 100644
index 0000000..8ac3187
--- /dev/null
+++ b/app/src/androidTest/java/org/wikipedia/editing/EditPreviewTest.java
@@ -0,0 +1,36 @@
+package org.wikipedia.editing;
+
+import android.support.annotation.NonNull;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.wikipedia.dataclient.WikiSite;
+import org.wikipedia.page.PageTitle;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertThat;
+
+@RunWith(AndroidJUnit4.class)
+public class EditPreviewTest {
+ @Test
+ public void testPreview() throws Throwable {
+ final WikiSite wiki = new WikiSite("test.wikipedia.org");
+ final PageTitle title = new PageTitle(null,
"Test_page_for_app_testing/Section1", wiki);
+ final long randomTime = System.currentTimeMillis();
+ final String wikiText = "== Section 2 ==\n\nEditing section INSERT
RANDOM & HERE test at " + randomTime;
+
+ new EditClient().previewEdit(wiki, title, wikiText,
+ new EditPreview.Callback() {
+ @Override
+ public void success(@NonNull String preview) {
+ assertThat(preview,
containsString(String.valueOf(randomTime)));
+ }
+
+ @Override
+ public void failure(@NonNull Throwable caught) {
+ throw new RuntimeException(caught);
+ }
+ });
+ }
+}
diff --git a/app/src/androidTest/java/org/wikipedia/editing/EditTest.java
b/app/src/androidTest/java/org/wikipedia/editing/EditTest.java
new file mode 100644
index 0000000..d11e966
--- /dev/null
+++ b/app/src/androidTest/java/org/wikipedia/editing/EditTest.java
@@ -0,0 +1,191 @@
+package org.wikipedia.editing;
+
+import android.support.annotation.NonNull;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.wikipedia.WikipediaApp;
+import org.wikipedia.captcha.CaptchaResult;
+import org.wikipedia.dataclient.WikiSite;
+import org.wikipedia.page.PageTitle;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+@RunWith(AndroidJUnit4.class)
+public class EditTest {
+ private static final WikiSite TEST_WIKI_SITE = new
WikiSite("test.wikipedia.org");
+ private static final String ABUSE_FILTER_ERROR_PAGE_TITLE =
"Test_page_for_app_testing/AbuseFilter";
+ private static final int DEFAULT_SECTION_ID = 0;
+ // https://www.mediawiki.org/wiki/Manual:Edit_token#The_edit_token_suffix
+ private static final String ANONYMOUS_TOKEN = "+\\";
+ private static final String DEFAULT_SUMMARY = "";
+
+ @Before
+ public void setUp() {
+ // Cookies for a logged in session cannot be used with the anonymous
edit token.
+ app().getCookieManager().clearAllCookies();
+ }
+
+ @Test
+ public void testEdit() {
+ PageTitle title = new PageTitle(null,
"Test_page_for_app_testing/Section1", TEST_WIKI_SITE);
+ String wikitext = "== Section 2 ==\n\nEditing section INSERT RANDOM &
HERE test at "
+ + System.currentTimeMillis();
+ final int sectionId = 3;
+
+ service.edit(TEST_WIKI_SITE, title, sectionId, wikitext,
ANONYMOUS_TOKEN, DEFAULT_SUMMARY,
+ false, null, null, new Edit.Callback() {
+ @Override
+ public void success(@NonNull EditingResult result) {
+ assertThat(result,
instanceOf(SuccessEditResult.class));
+ }
+
+ @Override
+ public void failure(@NonNull Throwable caught) {
+ throw new RuntimeException(caught);
+ }
+ });
+ }
+
+ @Test
+ public void testCaptcha() {
+ PageTitle title = new PageTitle(null,
"Test_page_for_app_testing/Captcha", TEST_WIKI_SITE);
+ String wikitext = "== Section 2 ==\n\nEditing by inserting an external
link https://"
+ + System.currentTimeMillis();
+
+ service.edit(TEST_WIKI_SITE, title, DEFAULT_SECTION_ID, wikitext,
ANONYMOUS_TOKEN,
+ DEFAULT_SUMMARY, false, null, null, new Edit.Callback() {
+ @Override
+ public void success(@NonNull EditingResult result) {
+ validateCaptcha(result);
+ }
+
+ @Override
+ public void failure(@NonNull Throwable caught) {
+ throw new RuntimeException(caught);
+ }
+ });
+ }
+
+ /**
+ * Test handling of abuse filter warnings which warn users about making
edits of a certain kind.
+ *
+ * Type: warn
+ * Action: editing any userspace page while logged out
+ * Filter: https://test.wikipedia.org/wiki/Special:AbuseFilter/94
+ */
+ @Test
+ public void testAbuseFilterTriggerWarn() {
+ PageTitle title = new PageTitle(null, "User:Yuvipandaaaaaaaa",
TEST_WIKI_SITE);
+
+ // Rule 94 is only a warning so the initial attempt may be successful.
The second is
+ // guaranteed to be a warning if different content is used.
@FlakyTest(tolerance = 2)
+ // doesn't work with JUnit 4.
+ for (int i = 0; i < 2; ++i) {
+ String wikitext = "Testing Abusefilter by simply editing this
page. Triggering rule 94"
+ + "at " + System.nanoTime();
+
+ service.edit(TEST_WIKI_SITE, title, DEFAULT_SECTION_ID, wikitext,
ANONYMOUS_TOKEN,
+ DEFAULT_SUMMARY, false, null, null, new Edit.Callback() {
+ @Override
+ public void success(@NonNull EditingResult result) {
+ if (!(result instanceof SuccessEditResult)) {
+ assertThat(result,
instanceOf(AbuseFilterEditResult.class));
+ //noinspection ConstantConditions
+ assertThat(((AbuseFilterEditResult)
result).getType(),
+
is(AbuseFilterEditResult.TYPE_WARNING));
+ }
+ }
+
+ @Override
+ public void failure(@NonNull Throwable caught) {
+ throw new RuntimeException(caught);
+ }
+ });
+ }
+ }
+
+ /**
+ * Test handling of abuse filter errors which completely disallow edits of
a certain kind.
+ *
+ * Type: disallow
+ * Action: adding string "poop" to page text
+ * Filter: https://test.wikipedia.org/wiki/Special:AbuseFilter/2
+ */
+ @Test
+ public void testAbuseFilterTriggerStop() {
+ PageTitle title = new PageTitle(null, ABUSE_FILTER_ERROR_PAGE_TITLE,
TEST_WIKI_SITE);
+ String wikitext = "== Section 2 ==\n\nTriggering AbuseFilter number 2
by saying poop many"
+ + "times at " + System.currentTimeMillis();
+
+ service.edit(TEST_WIKI_SITE, title, DEFAULT_SECTION_ID, wikitext,
ANONYMOUS_TOKEN,
+ DEFAULT_SUMMARY, false, null, null, new Edit.Callback() {
+ @Override
+ public void success(@NonNull EditingResult result) {
+ assertThat(result,
instanceOf(AbuseFilterEditResult.class));
+ assertThat(((AbuseFilterEditResult) result).getType(),
+ is(AbuseFilterEditResult.TYPE_ERROR));
+ }
+
+ @Override
+ public void failure(@NonNull Throwable caught) {
+ throw new RuntimeException(caught);
+ }
+ });
+ }
+
+ /**
+ * Test the app's handling of the abuse filter emitting arbitrary error
codes.
+ *
+ * Type: warn
+ * Action: adding string "appcrashtest" to page text
+ * Filter: https://test.wikipedia.org/wiki/Special:AbuseFilter/152
+ */
+ @Test
+ public void testAbuseFilterTriggerStopOnArbitraryErrorCode() {
+ PageTitle title = new PageTitle(null, ABUSE_FILTER_ERROR_PAGE_TITLE,
TEST_WIKI_SITE);
+ String wikitext = "== Section 2 ==\n\nTriggering AbuseFilter number
152 by saying"
+ + "appcrashtest many times at " + System.currentTimeMillis();
+
+ service.edit(TEST_WIKI_SITE, title, DEFAULT_SECTION_ID, wikitext,
ANONYMOUS_TOKEN,
+ DEFAULT_SUMMARY, false, null, null, new Edit.Callback() {
+ @Override
+ public void success(@NonNull EditingResult result) {
+ assertThat(result,
instanceOf(AbuseFilterEditResult.class));
+ assertThat(((AbuseFilterEditResult) result).getType(),
+ is(AbuseFilterEditResult.TYPE_WARNING));
+ }
+
+ @Override
+ public void failure(@NonNull Throwable caught) {
+ throw new RuntimeException(caught);
+ }
+ });
+ }
+
+ private void validateCaptcha(EditingResult result) {
+ assertThat(result, instanceOf(CaptchaResult.class));
+ CaptchaResult captchaResult = (CaptchaResult) result;
+ String url = captchaResult.getCaptchaUrl(TEST_WIKI_SITE);
+ assertThat(isValidCaptchaUrl(url), is(true));
+ }
+
+ private boolean isValidCaptchaUrl(String url) {
+ return url.startsWith(getNetworkProtocol()
+ +
"://test.wikipedia.org/w/index.php?title=Special:Captcha/image");
+ }
+
+ private String getNetworkProtocol() {
+ return app().getWikiSite().scheme();
+ }
+
+ private WikipediaApp app() {
+ return WikipediaApp.getInstance();
+ }
+
+ private EditClient service = new EditClient();
+}
diff --git
a/app/src/androidTest/java/org/wikipedia/editing/FetchSectionWikitextTests.java
b/app/src/androidTest/java/org/wikipedia/editing/FetchSectionWikitextTests.java
new file mode 100644
index 0000000..30d1e9d
--- /dev/null
+++
b/app/src/androidTest/java/org/wikipedia/editing/FetchSectionWikitextTests.java
@@ -0,0 +1,31 @@
+package org.wikipedia.editing;
+
+import android.support.annotation.NonNull;
+import android.test.ActivityUnitTestCase;
+
+import org.wikipedia.dataclient.WikiSite;
+import org.wikipedia.page.PageTitle;
+import org.wikipedia.test.TestDummyActivity;
+
+public class FetchSectionWikitextTests extends
ActivityUnitTestCase<TestDummyActivity> {
+ public FetchSectionWikitextTests() {
+ super(TestDummyActivity.class);
+ }
+
+ public void testPageFetch() throws Throwable {
+ WikiSite wiki = new WikiSite("test.wikipedia.org");
+ PageTitle title = new PageTitle(null,
"Test_page_for_app_testing/Section1", wiki);
+ new EditClient().wikitext(wiki, title, 2,
+ new Wikitext.Callback() {
+ @Override
+ public void success(@NonNull String wikitext) {
+ assertNotNull(wikitext);
+ }
+
+ @Override
+ public void failure(@NonNull Throwable caught) {
+ throw new RuntimeException(caught);
+ }
+ });
+ }
+}
diff --git
a/app/src/androidTest/java/org/wikipedia/test/CreateAccountTaskTest.java
b/app/src/androidTest/java/org/wikipedia/test/CreateAccountTaskTest.java
index 82851a2..90e53d1 100644
--- a/app/src/androidTest/java/org/wikipedia/test/CreateAccountTaskTest.java
+++ b/app/src/androidTest/java/org/wikipedia/test/CreateAccountTaskTest.java
@@ -16,7 +16,7 @@
import org.wikipedia.dataclient.WikiSite;
import org.wikipedia.createaccount.CreateAccountInfoResult;
import org.wikipedia.createaccount.CreateAccountInfoTask;
-import org.wikipedia.editing.CaptchaResult;
+import org.wikipedia.captcha.CaptchaResult;
import org.wikipedia.testlib.TestLatch;
import org.wikipedia.util.log.L;
diff --git a/app/src/androidTest/java/org/wikipedia/test/EditTaskTest.java
b/app/src/androidTest/java/org/wikipedia/test/EditTaskTest.java
deleted file mode 100644
index edb5c45..0000000
--- a/app/src/androidTest/java/org/wikipedia/test/EditTaskTest.java
+++ /dev/null
@@ -1,187 +0,0 @@
-package org.wikipedia.test;
-
-import android.support.annotation.NonNull;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.wikipedia.WikipediaApp;
-import org.wikipedia.dataclient.WikiSite;
-import org.wikipedia.editing.AbuseFilterEditResult;
-import org.wikipedia.editing.CaptchaResult;
-import org.wikipedia.editing.EditTask;
-import org.wikipedia.editing.EditingResult;
-import org.wikipedia.editing.SuccessEditResult;
-import org.wikipedia.page.PageTitle;
-import org.wikipedia.testlib.TestLatch;
-
-import static android.support.test.InstrumentationRegistry.getTargetContext;
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class EditTaskTest {
- private static final WikiSite TEST_WIKI = WikiSite.forLanguageCode("test");
-
- private static final String ABUSE_FILTER_ERROR_PAGE_TITLE =
"Test_page_for_app_testing/AbuseFilter";
-
- @Before
- public void setUp() {
- // Cookies for a logged in session cannot be used with the anonymous
edit token.
- app().getCookieManager().clearAllCookies();
- }
-
- @Test
- public void testEdit() {
- PageTitle title = new PageTitle(null,
"Test_page_for_app_testing/Section1", TEST_WIKI);
- String wikitext = "== Section 2 ==\n\nEditing section INSERT RANDOM &
HERE test at "
- + System.currentTimeMillis();
- final int sectionId = 3;
-
- EditingResult result = Subject.execute(title, wikitext, sectionId);
- assertThat(result, instanceOf(SuccessEditResult.class));
- }
-
- @Test
- public void testCaptcha() {
- PageTitle title = new PageTitle(null,
"Test_page_for_app_testing/Captcha", TEST_WIKI);
- String wikitext = "== Section 2 ==\n\nEditing by inserting an external
link https://"
- + System.currentTimeMillis();
-
- EditingResult result = Subject.execute(title, wikitext);
- validateCaptcha(result);
- }
-
- /**
- * Test handling of abuse filter warnings which warn users about making
edits of a certain kind.
- *
- * Type: warn
- * Action: editing any userspace page while logged out
- * Filter: https://test.wikipedia.org/wiki/Special:AbuseFilter/94
- */
- @Test
- public void testAbuseFilterTriggerWarn() {
- PageTitle title = new PageTitle(null, "User:Yuvipandaaaaaaaa",
TEST_WIKI);
-
- // Rule 94 is only a warning so the initial attempt may be successful.
The second is
- // guaranteed to be a warning if different content is used.
@FlakyTest(tolerance = 2)
- // doesn't work with JUnit 4.
- EditingResult result = null;
- for (int i = 0; !(result instanceof AbuseFilterEditResult) && i < 2;
++i) {
- String wikitext = "Testing Abusefilter by simply editing this
page. Triggering rule 94 at "
- + System.nanoTime();
- result = Subject.execute(title, wikitext);
- }
-
- assertThat(result, instanceOf(AbuseFilterEditResult.class));
- // todo: test against a warning, not an error. Rule 94 is currently
returning
- // abusefilter-disallowed instead of abusefilter-warning.
EditTask needs to be
- // reworked to a data client that can consume mock data
- //noinspection ConstantConditions
- assertThat(((AbuseFilterEditResult) result).getType(),
is(AbuseFilterEditResult.TYPE_ERROR));
- }
-
- /**
- * Test handling of abuse filter errors which completely disallow edits of
a certain kind.
- *
- * Type: disallow
- * Action: adding string "poop" to page text
- * Filter: https://test.wikipedia.org/wiki/Special:AbuseFilter/2
- */
- @Test
- public void testAbuseFilterTriggerStop() {
- PageTitle title = new PageTitle(null, ABUSE_FILTER_ERROR_PAGE_TITLE,
TEST_WIKI);
- String wikitext = "== Section 2 ==\n\nTriggering AbuseFilter number 2
by saying poop many times at "
- + System.currentTimeMillis();
-
- EditingResult result = Subject.execute(title, wikitext);
- assertThat(result, instanceOf(AbuseFilterEditResult.class));
- assertThat(((AbuseFilterEditResult) result).getType(),
is(AbuseFilterEditResult.TYPE_ERROR));
- }
-
- /**
- * Test the app's handling of the abuse filter emitting arbitrary error
codes.
- *
- * Type: warn
- * Action: adding string "appcrashtest" to page text
- * Filter: https://test.wikipedia.org/wiki/Special:AbuseFilter/152
- */
- @Test
- public void testAbuseFilterTriggerStopOnArbitraryErrorCode() {
- PageTitle title = new PageTitle(null, ABUSE_FILTER_ERROR_PAGE_TITLE,
TEST_WIKI);
- String wikitext = "== Section 2 ==\n\nTriggering AbuseFilter number
152 by saying appcrashtest many times at "
- + System.currentTimeMillis();
-
- EditingResult result = Subject.execute(title, wikitext);
- assertThat(result, instanceOf(AbuseFilterEditResult.class));
- assertThat(((AbuseFilterEditResult) result).getType(),
is(AbuseFilterEditResult.TYPE_ERROR));
- }
-
- private void validateCaptcha(EditingResult result) {
- assertThat(result, instanceOf(CaptchaResult.class));
- CaptchaResult captchaResult = (CaptchaResult) result;
- String url = captchaResult.getCaptchaUrl(TEST_WIKI);
- assertThat(isValidCaptchaUrl(url), is(true));
- }
-
- private boolean isValidCaptchaUrl(String url) {
- return url.startsWith(getNetworkProtocol()
- +
"://test.wikipedia.org/w/index.php?title=Special:Captcha/image");
- }
-
- private String getNetworkProtocol() {
- return app().getWikiSite().scheme();
- }
-
- private WikipediaApp app() {
- return WikipediaApp.getInstance();
- }
-
- private static class Subject extends EditTask {
- private static final int DEFAULT_SECTION_ID = 0;
-
- //
https://www.mediawiki.org/wiki/Manual:Edit_token#The_edit_token_suffix
- private static final String ANONYMOUS_TOKEN = "+\\";
-
- private static final String DEFAULT_SUMMARY = "";
-
- public static EditingResult execute(PageTitle title, String
sectionWikitext) {
- return execute(title, sectionWikitext, DEFAULT_SECTION_ID);
- }
-
- public static EditingResult execute(PageTitle title, String
sectionWikitext, int sectionId) {
- return execute(title, sectionWikitext, sectionId, ANONYMOUS_TOKEN);
- }
-
- public static EditingResult execute(PageTitle title, String
sectionWikitext, int sectionId,
- String token) {
- Subject subject = new Subject(title, sectionWikitext, sectionId,
token);
- subject.execute();
- return subject.await();
- }
-
- @NonNull private final TestLatch latch = new TestLatch();
- private EditingResult result;
-
- Subject(PageTitle title, String sectionWikitext, int sectionId, String
token) {
- super(getTargetContext(), title, sectionWikitext, sectionId,
token, DEFAULT_SUMMARY,
- false);
- }
-
- @Override
- public void onFinish(EditingResult result) {
- super.onFinish(result);
- this.result = result;
- latch.countDown();
- }
-
- public EditingResult await() {
- latch.await();
- return result;
- }
- }
-}
diff --git
a/app/src/androidTest/java/org/wikipedia/test/FetchSectionWikitextTaskTests.java
b/app/src/androidTest/java/org/wikipedia/test/FetchSectionWikitextTaskTests.java
deleted file mode 100644
index 3e2a0e6..0000000
---
a/app/src/androidTest/java/org/wikipedia/test/FetchSectionWikitextTaskTests.java
+++ /dev/null
@@ -1,41 +0,0 @@
-
-package org.wikipedia.test;
-
-import android.support.test.filters.SmallTest;
-import android.test.ActivityUnitTestCase;
-
-import org.wikipedia.dataclient.WikiSite;
-import org.wikipedia.page.PageTitle;
-import org.wikipedia.editing.FetchSectionWikitextTask;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-@SmallTest
-public class FetchSectionWikitextTaskTests extends
ActivityUnitTestCase<TestDummyActivity> {
- private static final int TASK_COMPLETION_TIMEOUT = 20_000;
-
- public FetchSectionWikitextTaskTests() {
- super(TestDummyActivity.class);
- }
-
- public void testPageFetch() throws Throwable {
- final CountDownLatch completionLatch = new CountDownLatch(1);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- PageTitle title = new PageTitle(null,
"Test_page_for_app_testing/Section1", WikiSite.forLanguageCode("test"));
- new
FetchSectionWikitextTask(getInstrumentation().getTargetContext(), title, 2) {
- @Override
- public void onFinish(String result) {
- assertNotNull(result);
- assertEquals(result, "=== Section1.2 ===\nThis is a
subsection");
- completionLatch.countDown();
- }
- }.execute();
- }
- });
- assertTrue(completionLatch.await(TASK_COMPLETION_TIMEOUT,
TimeUnit.MILLISECONDS));
- }
-}
-
diff --git a/app/src/main/java/org/wikipedia/captcha/Captcha.java
b/app/src/main/java/org/wikipedia/captcha/Captcha.java
new file mode 100644
index 0000000..805bd9c
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/captcha/Captcha.java
@@ -0,0 +1,22 @@
+package org.wikipedia.captcha;
+
+import android.support.annotation.NonNull;
+
+public class Captcha {
+ @SuppressWarnings("unused,NullableProblems") @NonNull private
FancyCaptchaReload fancycaptchareload;
+ @NonNull protected FancyCaptchaReload content() {
+ return fancycaptchareload;
+ }
+
+ protected class FancyCaptchaReload {
+ @SuppressWarnings("unused,NullableProblems") @NonNull private String
index;
+ @NonNull protected String index() {
+ return index;
+ }
+ }
+
+ public interface Callback {
+ void success(@NonNull CaptchaResult result);
+ void failure(@NonNull Throwable caught);
+ }
+}
diff --git a/app/src/main/java/org/wikipedia/captcha/CaptchaClient.java
b/app/src/main/java/org/wikipedia/captcha/CaptchaClient.java
new file mode 100644
index 0000000..67dfc16
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/captcha/CaptchaClient.java
@@ -0,0 +1,45 @@
+package org.wikipedia.captcha;
+
+
+import android.support.annotation.NonNull;
+
+import org.wikipedia.dataclient.WikiSite;
+import org.wikipedia.dataclient.retrofit.MwCachedService;
+import org.wikipedia.dataclient.retrofit.RetrofitException;
+import org.wikipedia.server.restbase.RbPageEndpointsCache;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+import retrofit2.Retrofit;
+import retrofit2.http.GET;
+
+public class CaptchaClient {
+ @NonNull private final MwCachedService<Service> cachedService = new
MwCachedService<>(Service.class);
+ @NonNull private final Retrofit retrofit =
RbPageEndpointsCache.INSTANCE.getRetrofit();
+
+ public void refreshCaptcha(@NonNull final WikiSite wiki, @NonNull final
Captcha.Callback cb) {
+ Call<Captcha> call = cachedService.service(wiki).refreshCaptcha();
+ call.enqueue(new Callback<Captcha>() {
+ @Override
+ public void onResponse(Call<Captcha> call, Response<Captcha>
response) {
+ if (response.isSuccessful()) {
+ cb.success(new
CaptchaResult(response.body().content().index()));
+ } else {
+ cb.failure(RetrofitException.httpError(response,
retrofit));
+ }
+ }
+
+ @Override
+ public void onFailure(Call<Captcha> call, Throwable t) {
+ cb.failure(t);
+ }
+ });
+ }
+
+ private interface Service {
+ /* Get a fresh Captcha ID. */
+ @GET("w/api.php?action=fancycaptchareload&format=json")
+ Call<Captcha> refreshCaptcha();
+ }
+}
diff --git a/app/src/main/java/org/wikipedia/editing/CaptchaHandler.java
b/app/src/main/java/org/wikipedia/captcha/CaptchaHandler.java
similarity index 89%
rename from app/src/main/java/org/wikipedia/editing/CaptchaHandler.java
rename to app/src/main/java/org/wikipedia/captcha/CaptchaHandler.java
index 3f07ca0..9f0db82 100644
--- a/app/src/main/java/org/wikipedia/editing/CaptchaHandler.java
+++ b/app/src/main/java/org/wikipedia/captcha/CaptchaHandler.java
@@ -1,4 +1,4 @@
-package org.wikipedia.editing;
+package org.wikipedia.captcha;
import android.app.Activity;
import android.app.ProgressDialog;
@@ -59,26 +59,24 @@
captchaImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- new RefreshCaptchaTask(activity, wiki) {
- @Override
- public void onBeforeExecute() {
- captchaProgress.setVisibility(View.VISIBLE);
- }
+ captchaProgress.setVisibility(View.VISIBLE);
+ CaptchaClient apiService = new CaptchaClient();
+ apiService.refreshCaptcha(wiki, new Captcha.Callback() {
@Override
- public void onFinish(@NonNull CaptchaResult result) {
+ public void success(@NonNull CaptchaResult result) {
captchaResult = result;
captchaProgress.setVisibility(View.GONE);
handleCaptcha(true);
}
@Override
- public void onCatch(Throwable caught) {
+ public void failure(@NonNull Throwable caught) {
cancelCaptcha();
captchaProgress.setVisibility(View.GONE);
FeedbackUtil.showError(activity, caught);
}
- }.execute();
+ });
}
});
}
@@ -88,6 +86,22 @@
return token;
}
+ @Nullable
+ public String captchaId() {
+ if (captchaResult != null) {
+ return captchaResult.getCaptchaId();
+ }
+ return null;
+ }
+
+ @Nullable
+ public String captchaWord() {
+ if (captchaText != null) {
+ return captchaText.getText().toString();
+ }
+ return null;
+ }
+
public void restoreState(Bundle savedInstanceState) {
if (savedInstanceState != null
&& savedInstanceState.containsKey("token")
diff --git a/app/src/main/java/org/wikipedia/editing/CaptchaResult.java
b/app/src/main/java/org/wikipedia/captcha/CaptchaResult.java
similarity index 93%
rename from app/src/main/java/org/wikipedia/editing/CaptchaResult.java
rename to app/src/main/java/org/wikipedia/captcha/CaptchaResult.java
index 9d82d13..9b175e4 100644
--- a/app/src/main/java/org/wikipedia/editing/CaptchaResult.java
+++ b/app/src/main/java/org/wikipedia/captcha/CaptchaResult.java
@@ -1,8 +1,9 @@
-package org.wikipedia.editing;
+package org.wikipedia.captcha;
import android.os.Parcel;
import android.os.Parcelable;
import org.wikipedia.dataclient.WikiSite;
+import org.wikipedia.editing.EditingResult;
// Handles only Image Captchas
public class CaptchaResult extends EditingResult {
diff --git a/app/src/main/java/org/wikipedia/editing/Edit.java
b/app/src/main/java/org/wikipedia/editing/Edit.java
new file mode 100644
index 0000000..6a84a66
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/editing/Edit.java
@@ -0,0 +1,58 @@
+package org.wikipedia.editing;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+public class Edit {
+ @SuppressWarnings("unused,NullableProblems") @NonNull private Result edit;
+ @NonNull protected Result edit() {
+ return edit;
+ }
+
+ protected class Result {
+ @Nullable private String result;
+ @Nullable protected String status() {
+ return result;
+ }
+
+ private int newrevid;
+ protected int newRevId() {
+ return newrevid;
+ }
+
+ @Nullable private Captcha captcha;
+ @Nullable protected Captcha captcha() {
+ return captcha;
+ }
+
+ @Nullable private String code;
+ @Nullable protected String code() {
+ return code;
+ }
+
+ protected boolean hasErrorCode() {
+ return code != null;
+ }
+
+ @Nullable private String spamblacklist;
+ @Nullable protected String spamblacklist() {
+ return spamblacklist;
+ }
+
+ protected boolean hasSpamBlacklistResponse() {
+ return spamblacklist != null;
+ }
+ }
+
+ protected class Captcha {
+ @SuppressWarnings("unused,NullableProblems") @NonNull private String
id;
+ @NonNull protected String id() {
+ return id;
+ }
+ }
+
+ public interface Callback {
+ void success(@NonNull EditingResult result);
+ void failure(@NonNull Throwable caught);
+ }
+}
diff --git a/app/src/main/java/org/wikipedia/editing/EditClient.java
b/app/src/main/java/org/wikipedia/editing/EditClient.java
new file mode 100644
index 0000000..00d57ac
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/editing/EditClient.java
@@ -0,0 +1,197 @@
+package org.wikipedia.editing;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.wikipedia.dataclient.WikiSite;
+import org.wikipedia.dataclient.retrofit.MwCachedService;
+import org.wikipedia.dataclient.retrofit.RetrofitException;
+import org.wikipedia.json.GsonMarshaller;
+import org.wikipedia.json.GsonUnmarshaller;
+import org.wikipedia.page.PageTitle;
+import org.wikipedia.server.mwapi.ApiResponsePage;
+import org.wikipedia.server.restbase.RbPageEndpointsCache;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+import retrofit2.Retrofit;
+import retrofit2.http.Field;
+import retrofit2.http.FormUrlEncoded;
+import retrofit2.http.GET;
+import retrofit2.http.POST;
+import retrofit2.http.Query;
+
+public class EditClient {
+ @NonNull private final MwCachedService<Service> cachedService = new
MwCachedService<>(Service.class);
+ @NonNull private final Retrofit retrofit =
RbPageEndpointsCache.INSTANCE.getRetrofit();
+
+ public void editToken(@NonNull final WikiSite wiki, @NonNull final
EditToken.Callback cb) {
+ Call<EditToken> call = cachedService.service(wiki).editToken();
+ call.enqueue(new Callback<EditToken>() {
+ @Override
+ public void onResponse(Call<EditToken> call, Response<EditToken>
response) {
+ if (response.isSuccessful()) {
+ cb.success(response.body().query().tokens().token());
+ } else {
+ cb.failure(RetrofitException.httpError(response,
retrofit));
+ }
+ }
+
+ @Override
+ public void onFailure(Call<EditToken> call, Throwable t) {
+ cb.failure(t);
+ }
+ });
+ }
+
+ @NonNull
+ public String editTokenSync(@NonNull final WikiSite wiki) throws
IOException {
+ Call<EditToken> call = cachedService.service(wiki).editToken();
+ return call.execute().body().query().tokens().token();
+ }
+
+ @SuppressWarnings("checkstyle:parameternumber")
+ public void edit(@NonNull final WikiSite wiki, @NonNull final PageTitle
title,
+ final int section, @NonNull final String text, @NonNull
final String token,
+ @NonNull final String summary, final boolean loggedIn,
+ @Nullable final String captchaId, @Nullable final String
captchaWord,
+ @NonNull final Edit.Callback cb) {
+ Call<Edit> call = cachedService.service(wiki).edit("edit", "json",
title.getPrefixedText(),
+ section, text, token, summary, loggedIn ? "user" : null,
captchaId, captchaWord);
+ call.enqueue(new Callback<Edit>() {
+ @Override
+ public void onResponse(Call<Edit> call, Response<Edit> response) {
+ if (response.isSuccessful()) {
+ Edit.Result result = response.body().edit();
+ if ("Success".equals(result.status())) {
+ try {
+ // TODO: remove when the server reflects the
updated page content
+ // immediately after submitting the edit, instead
of a short while after.
+ Thread.sleep(TimeUnit.SECONDS.toMillis(2));
+ cb.success(new
SuccessEditResult(result.newRevId()));
+ } catch (InterruptedException e) {
+ cb.failure(e);
+ }
+ } else if (result.hasErrorCode()) {
+ try {
+ JSONObject json = new
JSONObject(GsonMarshaller.marshal(result));
+ cb.success(new AbuseFilterEditResult(json));
+ } catch (JSONException e) {
+ cb.failure(e);
+ }
+ } else if (result.hasSpamBlacklistResponse()) {
+ cb.success(new
SpamBlacklistEditResult(result.spamblacklist()));
+ } else {
+ cb.failure(new RuntimeException("Received unrecognized
edit response"));
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(Call<Edit> call, Throwable t) {
+
+ }
+ });
+ }
+
+ public void wikitext(@NonNull final WikiSite wiki, @NonNull final
PageTitle title,
+ final int sectionID, @NonNull final Wikitext.Callback
cb) {
+ Call<Wikitext> call =
cachedService.service(wiki).wikitext(title.getPrefixedText(), sectionID);
+ call.enqueue(new Callback<Wikitext>() {
+ @Override
+ public void onResponse(Call<Wikitext> call, Response<Wikitext>
response) {
+ if (response.isSuccessful()) {
+ JsonObject pages = response.body().query().pages();
+ Set<Map.Entry<String, JsonElement>> entries =
pages.entrySet();
+ if (!entries.isEmpty()) {
+ Iterator<Map.Entry<String, JsonElement>> i =
entries.iterator();
+ Map.Entry<String, JsonElement> entry = i.next();
+ String value =
entry.getValue().getAsJsonObject().toString();
+ ApiResponsePage page =
GsonUnmarshaller.unmarshal(ApiResponsePage.class, value);
+ ArrayList<ApiResponsePage.Revision> revisions = new
ArrayList(page.revisions());
+ cb.success(revisions.get(0).content());
+ }
+ } else {
+ cb.failure(RetrofitException.httpError(response,
retrofit));
+ }
+ }
+
+ @Override
+ public void onFailure(Call<Wikitext> call, Throwable t) {
+ cb.failure(t);
+ }
+ });
+ }
+
+
+ public void previewEdit(@NonNull final WikiSite wiki, @NonNull final
PageTitle title,
+ @NonNull final String wikitext, @NonNull final
EditPreview.Callback cb) {
+ Call<EditPreview> call =
cachedService.service(wiki).previewEdit("parse", "json", true,
+ true, true, "text", title.getPrefixedText(), wikitext);
+ call.enqueue(new Callback<EditPreview>() {
+ @Override
+ public void onResponse(Call<EditPreview> call,
Response<EditPreview> response) {
+ if (response.isSuccessful()) {
+ cb.success(response.body().parse().text().result());
+ } else {
+ cb.failure(RetrofitException.httpError(response,
retrofit));
+ }
+ }
+
+ @Override
+ public void onFailure(Call<EditPreview> call, Throwable t) {
+ cb.failure(t);
+ }
+ });
+ }
+
+ private interface Service {
+ @GET("w/api.php?action=query&format=json&meta=tokens&type=csrf")
+ Call<EditToken> editToken();
+
+ @FormUrlEncoded
+ @POST("w/api.php")
+ @SuppressWarnings("checkstyle:parameternumber")
+ Call<Edit> edit(@NonNull @Field("action") String action,
+ @NonNull @Field("format") String format,
+ @NonNull @Field("title") String title,
+ @Field("section") int section,
+ @NonNull @Field("text") String text,
+ @NonNull @Field("token") String token,
+ @NonNull @Field("summary") String summary,
+ @Nullable @Field("assert") String user,
+ @Nullable @Field("captchaId") String captchaId,
+ @Nullable @Field("captchaWord") String captchaWord);
+
+
+
@GET("w/api.php?action=query&format=json&prop=revisions&rvprop=content&rvlimit=1")
+ Call<Wikitext> wikitext(@NonNull @Query("titles") String title,
+ @Query("rvsection") int section);
+
+ @FormUrlEncoded
+ @POST("w/api.php")
+ @SuppressWarnings("checkstyle:parameternumber")
+ Call<EditPreview> previewEdit(@NonNull @Field("action") String action,
+ @NonNull @Field("format") String format,
+ @Field("sectionpreview") boolean
sectionPreview,
+ @Field("pst") boolean pst,
+ @Field("mobileformat") boolean
mobileformat,
+ @NonNull @Field("prop") String prop,
+ @NonNull @Field("title") String title,
+ @NonNull @Field("text") String text);
+
+ }
+}
diff --git a/app/src/main/java/org/wikipedia/editing/EditPreview.java
b/app/src/main/java/org/wikipedia/editing/EditPreview.java
new file mode 100644
index 0000000..158a261
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/editing/EditPreview.java
@@ -0,0 +1,31 @@
+package org.wikipedia.editing;
+
+import android.support.annotation.NonNull;
+
+import com.google.gson.annotations.SerializedName;
+
+public class EditPreview {
+ @SuppressWarnings("unused,NullableProblems") private Parse parse;
+ @NonNull protected Parse parse() {
+ return parse;
+ }
+
+ protected class Parse {
+ @SuppressWarnings("unused,NullableProblems") private Text text;
+ @NonNull protected Text text() {
+ return text;
+ }
+ }
+
+ protected class Text {
+ @SuppressWarnings("unused,NullableProblems") @SerializedName("*")
private String result;
+ @NonNull protected String result() {
+ return result;
+ }
+ }
+
+ public interface Callback {
+ void success(@NonNull String preview);
+ void failure(@NonNull Throwable caught);
+ }
+}
diff --git a/app/src/main/java/org/wikipedia/editing/EditPreviewFragment.java
b/app/src/main/java/org/wikipedia/editing/EditPreviewFragment.java
index 4681b1e..b335961 100644
--- a/app/src/main/java/org/wikipedia/editing/EditPreviewFragment.java
+++ b/app/src/main/java/org/wikipedia/editing/EditPreviewFragment.java
@@ -11,7 +11,6 @@
import android.support.v4.app.Fragment;
import android.support.v7.app.AlertDialog;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -29,6 +28,7 @@
import org.wikipedia.page.PageTitle;
import org.wikipedia.util.L10nUtil;
import org.wikipedia.util.UriUtil;
+import org.wikipedia.util.log.L;
import org.wikipedia.views.ObservableWebView;
import org.wikipedia.bridge.CommunicationBridge;
import org.wikipedia.editing.summaries.EditSummaryTag;
@@ -291,27 +291,24 @@
*/
public void showPreview(final PageTitle title, final String wikiText) {
hideSoftKeyboard(getActivity());
+ progressDialog.show();
- new EditPreviewTask(WikipediaApp.getInstance(), wikiText, title) {
+ new
EditClient().previewEdit(parentActivity.getPageTitle().getWikiSite(), title,
wikiText,
+ new EditPreview.Callback() {
@Override
- public void onBeforeExecute() {
- progressDialog.show();
- }
-
- @Override
- public void onFinish(String result) {
+ public void success(@NonNull String preview) {
if (!progressDialog.isShowing()) {
// no longer attached to activity!
return;
}
- displayPreview(result);
- previewHTML = result;
+ displayPreview(preview);
+ previewHTML = preview;
parentActivity.supportInvalidateOptionsMenu();
progressDialog.dismiss();
}
@Override
- public void onCatch(Throwable caught) {
+ public void failure(@NonNull Throwable caught) {
if (!progressDialog.isShowing()) {
// no longer attached to activity!
return;
@@ -321,25 +318,27 @@
if (!(caught instanceof ApiException)) {
throw new RuntimeException(caught);
}
- Log.d("Wikipedia", "Caught " + caught.toString());
+ L.d("Caught " + caught.toString());
final AlertDialog retryDialog = new
AlertDialog.Builder(getActivity())
.setMessage(R.string.error_network_error)
-
.setPositiveButton(R.string.dialog_message_edit_failed_retry, new
DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int
which) {
- showPreview(title, wikiText);
- dialog.dismiss();
- }
- })
-
.setNegativeButton(R.string.dialog_message_edit_failed_cancel, new
DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int
which) {
- dialog.dismiss();
- }
- }).create();
+
.setPositiveButton(R.string.dialog_message_edit_failed_retry,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface
dialog, int which) {
+ showPreview(title, wikiText);
+ dialog.dismiss();
+ }
+ })
+
.setNegativeButton(R.string.dialog_message_edit_failed_cancel,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface
dialog, int which) {
+ dialog.dismiss();
+ }
+ }).create();
retryDialog.show();
}
- }.execute();
+ });
}
/**
diff --git a/app/src/main/java/org/wikipedia/editing/EditPreviewTask.java
b/app/src/main/java/org/wikipedia/editing/EditPreviewTask.java
deleted file mode 100644
index cef89d3..0000000
--- a/app/src/main/java/org/wikipedia/editing/EditPreviewTask.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package org.wikipedia.editing;
-
-import android.support.annotation.NonNull;
-
-import org.mediawiki.api.json.Api;
-import org.mediawiki.api.json.ApiException;
-import org.mediawiki.api.json.ApiResult;
-import org.mediawiki.api.json.RequestBuilder;
-import org.wikipedia.dataclient.ApiTask;
-import org.wikipedia.WikipediaApp;
-import org.wikipedia.page.PageTitle;
-
-public class EditPreviewTask extends ApiTask<String> {
- private final String wikiText;
- @NonNull private final PageTitle title;
-
- public EditPreviewTask(@NonNull WikipediaApp app, String wikiText,
@NonNull PageTitle title) {
- super(app.getAPIForSite(title.getWikiSite()));
- this.wikiText = wikiText;
- this.title = title;
- }
-
- @Override
- public RequestBuilder buildRequest(Api api) {
- return api.action("parse")
- .param("sectionpreview", "true")
- .param("pst", "true")
- .param("mobileformat", "true")
- .param("prop", "text")
- .param("title", title.getPrefixedText())
- .param("text", wikiText);
- }
-
- @Override
- protected ApiResult makeRequest(RequestBuilder builder) throws
ApiException {
- return builder.post();
- }
-
- @Override
- public String processResult(ApiResult result) throws Throwable {
- return
result.asObject().optJSONObject("parse").optJSONObject("text").optString("*");
- }
-}
diff --git a/app/src/main/java/org/wikipedia/editing/EditSectionActivity.java
b/app/src/main/java/org/wikipedia/editing/EditSectionActivity.java
index 7227d6c..16855fa 100644
--- a/app/src/main/java/org/wikipedia/editing/EditSectionActivity.java
+++ b/app/src/main/java/org/wikipedia/editing/EditSectionActivity.java
@@ -8,6 +8,9 @@
import org.wikipedia.activity.ThemedActionBarActivity;
import org.wikipedia.analytics.EditFunnel;
import org.wikipedia.analytics.LoginFunnel;
+import org.wikipedia.auth.AccountUtil;
+import org.wikipedia.captcha.CaptchaHandler;
+import org.wikipedia.captcha.CaptchaResult;
import org.wikipedia.editing.richtext.SyntaxHighlighter;
import org.wikipedia.editing.summaries.EditSummaryFragment;
import org.wikipedia.login.LoginActivity;
@@ -21,10 +24,10 @@
import org.wikipedia.util.StringUtil;
import org.wikipedia.util.log.L;
-import org.mediawiki.api.json.Api;
import org.mediawiki.api.json.ApiException;
-import org.mediawiki.api.json.RequestBuilder;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
@@ -62,6 +65,8 @@
public static final String EXTRA_HIGHLIGHT_TEXT =
"org.wikipedia.edit_section.highlight";
private WikipediaApp app;
+ private EditClient apiService;
+ private LoginClient loginService;
private PageTitle title;
public PageTitle getPageTitle() {
@@ -111,6 +116,8 @@
}
app = (WikipediaApp)getApplicationContext();
+ apiService = new EditClient();
+ loginService = new LoginClient();
title = getIntent().getParcelableExtra(EXTRA_TITLE);
sectionID = getIntent().getIntExtra(EXTRA_SECTION_ID, 0);
@@ -273,79 +280,73 @@
// Summaries are plaintext, so remove any HTML that's made its
way into the summary
summaryText = StringUtil.fromHtml(summaryText).toString();
- new EditTask(EditSectionActivity.this, title,
sectionText.getText().toString(),
- sectionID, token, summaryText, User.isLoggedIn()) {
- @Override
- public void onBeforeExecute() {
- if (!isFinishing()) {
- progressDialog.show();
- }
- }
+ if (!isFinishing()) {
+ progressDialog.show();
+ }
- @Override
- public RequestBuilder buildRequest(Api api) {
- return
captchaHandler.populateBuilder(super.buildRequest(api));
- }
+ apiService.edit(title.getWikiSite(), title, sectionID,
+ sectionText.getText().toString(), token, summaryText,
User.isLoggedIn(),
+ captchaHandler.isActive() ? captchaHandler.captchaId()
: "null",
+ captchaHandler.isActive() ?
captchaHandler.captchaWord() : "null",
+ new Edit.Callback() {
+ @Override
+ public void success(@NonNull EditingResult result)
{
+ if (isFinishing() ||
!progressDialog.isShowing()) {
+ // no longer attached to activity!
+ return;
+ }
+ if (result instanceof SuccessEditResult) {
+ funnel.logSaved(((SuccessEditResult)
result).getRevID());
+ progressDialog.dismiss();
- @Override
- public void onCatch(Throwable caught) {
- if (isFinishing() || !progressDialog.isShowing()) {
- // no longer attached to activity!
- return;
- }
- if (caught instanceof ApiException) {
- // This is a fairly standard editing exception.
Handle it appropriately.
- handleEditingException((ApiException) caught);
- } else {
- // If it's not an API exception, we have no idea
what's wrong.
- // Show the user a generic error message.
- L.w("Caught " + caught.toString());
- showRetryDialog();
- }
- }
-
- @Override
- public void onFinish(EditingResult result) {
- if (isFinishing() || !progressDialog.isShowing()) {
- // no longer attached to activity!
- return;
- }
- if (result instanceof SuccessEditResult) {
- funnel.logSaved(((SuccessEditResult)
result).getRevID());
- progressDialog.dismiss();
-
- //Build intent that includes the section we were
editing, so we can scroll to it later
- Intent data = new Intent();
- data.putExtra(EXTRA_SECTION_ID, sectionID);
- setResult(EditHandler.RESULT_REFRESH_PAGE, data);
- hideSoftKeyboard(EditSectionActivity.this);
- finish();
- } else if (result instanceof CaptchaResult) {
- if (captchaHandler.isActive()) {
- // Captcha entry failed!
- funnel.logCaptchaFailure();
+ //Build intent that includes the section
we were editing, so we can scroll to it later
+ Intent data = new Intent();
+ data.putExtra(EXTRA_SECTION_ID, sectionID);
+ setResult(EditHandler.RESULT_REFRESH_PAGE,
data);
+ hideSoftKeyboard(EditSectionActivity.this);
+ finish();
+ } else if (result instanceof CaptchaResult) {
+ if (captchaHandler.isActive()) {
+ // Captcha entry failed!
+ funnel.logCaptchaFailure();
+ }
+ captchaHandler.handleCaptcha(null,
(CaptchaResult) result);
+ funnel.logCaptchaShown();
+ } else if (result instanceof
AbuseFilterEditResult) {
+ abusefilterEditResult =
(AbuseFilterEditResult) result;
+ handleAbuseFilter();
+ if (abusefilterEditResult.getType() ==
AbuseFilterEditResult.TYPE_ERROR) {
+ editPreviewFragment.hide();
+ }
+ } else if (result instanceof
SpamBlacklistEditResult) {
+
FeedbackUtil.showMessage(EditSectionActivity.this,
+
R.string.editing_error_spamblacklist);
+ progressDialog.dismiss();
+ editPreviewFragment.hide();
+ } else {
+ funnel.logError(result.getResult());
+ // Expand to do everything.
+ failure(new Throwable());
+ }
}
- captchaHandler.handleCaptcha(null, (CaptchaResult)
result);
- funnel.logCaptchaShown();
- } else if (result instanceof AbuseFilterEditResult) {
- abusefilterEditResult = (AbuseFilterEditResult)
result;
- handleAbuseFilter();
- if (abusefilterEditResult.getType() ==
AbuseFilterEditResult.TYPE_ERROR) {
- editPreviewFragment.hide();
- }
- } else if (result instanceof SpamBlacklistEditResult) {
- FeedbackUtil.showMessage(EditSectionActivity.this,
- R.string.editing_error_spamblacklist);
- progressDialog.dismiss();
- editPreviewFragment.hide();
- } else {
- funnel.logError(result.getResult());
- // Expand to do everything.
- onCatch(null);
- }
- }
- }.execute();
+ @Override
+ public void failure(@NonNull Throwable caught) {
+ if (isFinishing() ||
!progressDialog.isShowing()) {
+ // no longer attached to activity!
+ return;
+ }
+ if (caught instanceof ApiException) {
+ // This is a fairly standard editing
exception. Handle it appropriately.
+ handleEditingException((ApiException)
caught);
+ } else {
+ // If it's not an API exception, we have
no idea what's wrong.
+ // Show the user a generic error message.
+ L.w("Caught " + caught.toString());
+ showRetryDialog();
+ }
+ }
+ });
}
@Override
@@ -439,34 +440,55 @@
}
private void doLoginAndSave(final User user) {
- new LoginClient().request(WikipediaApp.getInstance().getWikiSite(),
- user.getUsername(),
- user.getPassword(),
- new LoginClient.LoginCallback() {
- @Override
- public void success(@NonNull LoginResult result) {
- if (result.pass()) {
- doSave();
- } else {
- onLoginError();
- }
- }
+ final String username = user.getUsername();
+ final String password = user.getPassword();
+ final LoginFunnel loginFunnel = new
LoginFunnel(WikipediaApp.getInstance());
+ loginService.request(title.getWikiSite(), username, password, new
LoginClient.LoginCallback() {
+ @Override
+ public void success(@NonNull LoginResult result) {
+ if (result.pass() && result.getUser() != null) {
+ User.setUser(result.getUser());
- @Override
- public void error(@NonNull Throwable caught) {
- onLoginError();
+ if (!progressDialog.isShowing()) {
+ // no longer attached to activity!
+ return;
}
+ progressDialog.dismiss();
- private void onLoginError() {
- if (!progressDialog.isShowing()) {
- // no longer attached to activity!
- return;
- }
- progressDialog.dismiss();
- ViewAnimations.crossFade(sectionText, sectionError);
- sectionError.setVisibility(View.VISIBLE);
- }
- });
+ Bundle extras = getIntent().getExtras();
+ AccountAuthenticatorResponse response = extras == null
+ ? null
+ :
extras.<AccountAuthenticatorResponse>getParcelable(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
+ AccountUtil.createAccount(response, username, password);
+
+ setResult(LoginActivity.RESULT_LOGIN_SUCCESS);
+ loginFunnel.logSuccess();
+
+ doSave();
+ } else {
+ progressDialog.dismiss();
+ FeedbackUtil.showMessage(EditSectionActivity.this,
result.getMessage());
+ ViewAnimations.crossFade(sectionText, sectionError);
+ sectionError.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public void error(@NonNull Throwable t) {
+ handleError(t);
+ }
+ });
+ }
+
+ private void handleError(Throwable t) {
+ if (!progressDialog.isShowing()) {
+ // no longer attached to activity!
+ return;
+ }
+ progressDialog.dismiss();
+ FeedbackUtil.showError(EditSectionActivity.this, t);
+ funnel.logError(t.getMessage());
+ L.e("Login failed with result " + t.getMessage());
}
private void handleAbuseFilter() {
@@ -610,22 +632,22 @@
private void fetchSectionText() {
if (sectionWikitext == null) {
- new FetchSectionWikitextTask(this, title, sectionID) {
+ apiService.wikitext(title.getWikiSite(), title, sectionID, new
Wikitext.Callback() {
@Override
- public void onFinish(String result) {
- sectionWikitext = result;
+ public void success(@NonNull String wikitext) {
+ sectionWikitext = wikitext;
displaySectionText();
}
@Override
- public void onCatch(Throwable caught) {
+ public void failure(@NonNull Throwable throwable) {
ViewAnimations.crossFade(sectionProgress, sectionError);
// Not sure why this is required, but without it tapping
retry hides langLinksError
// FIXME: INVESTIGATE WHY THIS HAPPENS!
// Also happens in {@link PageFragment}
sectionError.setVisibility(View.VISIBLE);
}
- }.execute();
+ });
} else {
displaySectionText();
}
diff --git a/app/src/main/java/org/wikipedia/editing/EditTask.java
b/app/src/main/java/org/wikipedia/editing/EditTask.java
deleted file mode 100644
index 27a444f..0000000
--- a/app/src/main/java/org/wikipedia/editing/EditTask.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package org.wikipedia.editing;
-
-import android.content.Context;
-import org.json.JSONObject;
-import org.mediawiki.api.json.Api;
-import org.mediawiki.api.json.ApiException;
-import org.mediawiki.api.json.ApiResult;
-import org.mediawiki.api.json.RequestBuilder;
-import org.wikipedia.dataclient.ApiTask;
-import org.wikipedia.page.PageTitle;
-import org.wikipedia.WikipediaApp;
-
-import java.util.concurrent.TimeUnit;
-
-public class EditTask extends ApiTask<EditingResult> {
- private final PageTitle title;
- private final String sectionWikitext;
- private final int sectionID;
- private final String summary;
- private final String editToken;
- private final boolean loggedIn;
-
- public EditTask(Context context, PageTitle title, String sectionWikitext,
int sectionID,
- String editToken, String summary, boolean loggedIn) {
-
super(((WikipediaApp)context.getApplicationContext()).getAPIForSite(title.getWikiSite()));
- this.title = title;
- this.sectionWikitext = sectionWikitext;
- this.sectionID = sectionID;
- this.editToken = editToken;
- this.summary = summary;
- this.loggedIn = loggedIn;
- }
-
- @Override
- public RequestBuilder buildRequest(Api api) {
- RequestBuilder req = api.action("edit")
- .param("title", title.getPrefixedText())
- .param("section", String.valueOf(sectionID))
- .param("text", sectionWikitext)
- .param("token", editToken)
- .param("summary", summary);
- if (loggedIn) {
- // if the app believes that the user is logged in, then make sure
to send an "assert"
- // parameter to the API, so that it will notify us if the user was
actually logged
- // out on another device, and we'll need to log back in behind the
scenes.
- req = req.param("assert", "user");
- }
- return req;
- }
-
- @Override
- protected ApiResult makeRequest(RequestBuilder builder) throws
ApiException {
- return builder.post(); // Editing requires POST requests
- }
-
- @Override
- public EditingResult processResult(ApiResult result) throws Throwable {
- JSONObject resultJSON = result.asObject();
- JSONObject edit = resultJSON.optJSONObject("edit");
- String status = edit.optString("result");
- if (status.equals("Success")) {
-
- // TODO: remove when the server reflects the updated page content
immediately
- // after submitting the edit, instead of a short while after.
- Thread.sleep(TimeUnit.SECONDS.toMillis(2));
-
- return new SuccessEditResult(edit.optInt("newrevid"));
- } else if (status.equals("Failure")) {
- if (edit.has("captcha")) {
- return new CaptchaResult(
- edit.optJSONObject("captcha").optString("id")
- );
- }
- if (edit.has("code")) {
- return new AbuseFilterEditResult(edit);
- }
- if (edit.has("spamblacklist")) {
- return new
SpamBlacklistEditResult(edit.optString("spamblacklist"));
- }
- }
- // Handle other type of return codes here
- throw new RuntimeException("Failure to recognise edit status: " +
resultJSON.toString());
- }
-}
diff --git a/app/src/main/java/org/wikipedia/editing/EditToken.java
b/app/src/main/java/org/wikipedia/editing/EditToken.java
new file mode 100644
index 0000000..f679480
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/editing/EditToken.java
@@ -0,0 +1,29 @@
+package org.wikipedia.editing;
+
+import android.support.annotation.NonNull;
+
+public class EditToken {
+ @SuppressWarnings("unused,NullableProblems") @NonNull private Query query;
+ @NonNull protected Query query() {
+ return query;
+ }
+
+ protected class Query {
+ @SuppressWarnings("unused,NullableProblems") @NonNull private Tokens
tokens;
+ @NonNull protected Tokens tokens() {
+ return tokens;
+ }
+ }
+
+ protected class Tokens {
+ @SuppressWarnings("unused,NullableProblems") @NonNull private String
csrftoken;
+ @NonNull protected String token() {
+ return csrftoken;
+ }
+ }
+
+ public interface Callback {
+ void success(@NonNull String token);
+ void failure(@NonNull Throwable caught);
+ }
+}
diff --git a/app/src/main/java/org/wikipedia/editing/EditTokenStorage.java
b/app/src/main/java/org/wikipedia/editing/EditTokenStorage.java
index 197285c..da4f397 100644
--- a/app/src/main/java/org/wikipedia/editing/EditTokenStorage.java
+++ b/app/src/main/java/org/wikipedia/editing/EditTokenStorage.java
@@ -50,18 +50,19 @@
return;
}
- new FetchEditTokenTask(context, wiki) {
+ EditClient apiService = new EditClient();
+ apiService.editToken(wiki, new EditToken.Callback() {
@Override
- public void onFinish(String result) {
- token(wiki, result);
- callback.onTokenRetrieved(result);
+ public void success(@NonNull String token) {
+ token(wiki, token);
+ callback.onTokenRetrieved(token);
}
@Override
- public void onCatch(Throwable caught) {
+ public void failure(@NonNull Throwable caught) {
callback.onTokenFailed(caught);
}
- }.execute();
+ });
}
public void clearEditTokenForDomain(String wiki) {
diff --git a/app/src/main/java/org/wikipedia/editing/FetchEditTokenTask.java
b/app/src/main/java/org/wikipedia/editing/FetchEditTokenTask.java
deleted file mode 100644
index 159c763..0000000
--- a/app/src/main/java/org/wikipedia/editing/FetchEditTokenTask.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package org.wikipedia.editing;
-
-import android.content.Context;
-import org.mediawiki.api.json.Api;
-import org.mediawiki.api.json.ApiResult;
-import org.mediawiki.api.json.RequestBuilder;
-import org.wikipedia.dataclient.ApiTask;
-import org.wikipedia.dataclient.WikiSite;
-import org.wikipedia.WikipediaApp;
-
-public class FetchEditTokenTask extends ApiTask<String> {
- public FetchEditTokenTask(Context context, WikiSite wiki) {
-
super(((WikipediaApp)context.getApplicationContext()).getAPIForSite(wiki));
- }
-
- @Override
- public RequestBuilder buildRequest(Api api) {
- return api.action("tokens")
- .param("type", "edit");
- }
-
- @Override
- public String processResult(ApiResult result) throws Throwable {
- return
result.asObject().optJSONObject("tokens").optString("edittoken");
- }
-}
diff --git
a/app/src/main/java/org/wikipedia/editing/FetchSectionWikitextTask.java
b/app/src/main/java/org/wikipedia/editing/FetchSectionWikitextTask.java
deleted file mode 100644
index 6309f39..0000000
--- a/app/src/main/java/org/wikipedia/editing/FetchSectionWikitextTask.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package org.wikipedia.editing;
-
-import android.content.Context;
-import org.json.JSONObject;
-import org.mediawiki.api.json.Api;
-import org.mediawiki.api.json.ApiResult;
-import org.mediawiki.api.json.RequestBuilder;
-import org.wikipedia.dataclient.ApiTask;
-import org.wikipedia.page.PageTitle;
-import org.wikipedia.WikipediaApp;
-
-public class FetchSectionWikitextTask extends ApiTask<String> {
- private final PageTitle title;
- private final int sectionID;
-
- public FetchSectionWikitextTask(Context context, PageTitle title, int
sectionID) {
-
super(((WikipediaApp)context.getApplicationContext()).getAPIForSite(title.getWikiSite()));
- this.title = title;
- this.sectionID = sectionID;
- }
-
- @Override
- public RequestBuilder buildRequest(Api api) {
- return api.action("query")
- .param("prop", "revisions")
- .param("rvprop", "content")
- .param("rvlimit", "1")
- .param("titles", title.getPrefixedText())
- .param("rvsection", String.valueOf(sectionID));
- }
-
- @Override
- public String processResult(ApiResult result) throws Throwable {
- JSONObject pagesJSON = result.asObject()
- .optJSONObject("query")
- .optJSONObject("pages");
- String pageId = pagesJSON.keys().next();
-
- JSONObject revisionJSON =
pagesJSON.optJSONObject(pageId).optJSONArray("revisions").getJSONObject(0);
-
- return revisionJSON.optString("*");
- }
-}
diff --git a/app/src/main/java/org/wikipedia/editing/RefreshCaptchaTask.java
b/app/src/main/java/org/wikipedia/editing/RefreshCaptchaTask.java
deleted file mode 100644
index 4f2dc67..0000000
--- a/app/src/main/java/org/wikipedia/editing/RefreshCaptchaTask.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.wikipedia.editing;
-
-import android.content.Context;
-import org.mediawiki.api.json.Api;
-import org.mediawiki.api.json.ApiResult;
-import org.mediawiki.api.json.RequestBuilder;
-import org.wikipedia.dataclient.ApiTask;
-import org.wikipedia.dataclient.WikiSite;
-import org.wikipedia.WikipediaApp;
-
-public class RefreshCaptchaTask extends ApiTask<CaptchaResult> {
- public RefreshCaptchaTask(Context context, WikiSite wiki) {
-
super(((WikipediaApp)context.getApplicationContext()).getAPIForSite(wiki));
- }
-
- @Override
- public RequestBuilder buildRequest(Api api) {
- return api.action("fancycaptchareload");
- }
-
- @Override
- public CaptchaResult processResult(ApiResult result) throws Throwable {
- return new CaptchaResult(
- result.asObject()
- .optJSONObject("fancycaptchareload")
- .optString("index")
- );
- }
-}
diff --git a/app/src/main/java/org/wikipedia/editing/Wikitext.java
b/app/src/main/java/org/wikipedia/editing/Wikitext.java
new file mode 100644
index 0000000..33e5fdd
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/editing/Wikitext.java
@@ -0,0 +1,24 @@
+package org.wikipedia.editing;
+
+import android.support.annotation.NonNull;
+
+import com.google.gson.JsonObject;
+
+public class Wikitext {
+ @SuppressWarnings("unused,NullableProblems") @NonNull private Query query;
+ @NonNull protected Query query() {
+ return query;
+ }
+
+ protected class Query {
+ @SuppressWarnings("unused,NullableProblems") @NonNull private
JsonObject pages;
+ @NonNull protected JsonObject pages() {
+ return pages;
+ }
+ }
+
+ public interface Callback {
+ void success(@NonNull String wikitext);
+ void failure(@NonNull Throwable caught);
+ }
+}
diff --git a/app/src/main/java/org/wikipedia/server/mwapi/ApiResponsePage.java
b/app/src/main/java/org/wikipedia/server/mwapi/ApiResponsePage.java
new file mode 100644
index 0000000..8de3398
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/server/mwapi/ApiResponsePage.java
@@ -0,0 +1,45 @@
+package org.wikipedia.server.mwapi;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.util.List;
+
+public class ApiResponsePage {
+ @SuppressWarnings("unused") private int pageid;
+ @SuppressWarnings("unused") private int ns;
+ @SuppressWarnings("unused,NullableProblems") @NonNull private String title;
+
+ @SuppressWarnings("unused") @Nullable private List<LangLink> langlinks;
+ @Nullable public List<LangLink> langLinks() {
+ return langlinks;
+ }
+
+ @SuppressWarnings("unused") @Nullable private List<Revision> revisions;
+ @Nullable public List<Revision> revisions() {
+ return revisions;
+ }
+
+ public class Revision {
+ @SuppressWarnings("unused,NullableProblems") @NonNull private String
contentformat;
+ @SuppressWarnings("unused,NullableProblems") @NonNull private String
contentmodel;
+ @SerializedName("*") @SuppressWarnings("unused,NullableProblems")
@NonNull private String content;
+ @NonNull public String content() {
+ return content;
+ }
+ }
+
+ public class LangLink {
+ @SuppressWarnings("unused,NullableProblems") @NonNull private String
lang;
+ @NonNull public String lang() {
+ return lang;
+ }
+
+ @SerializedName("*") @SuppressWarnings("unused,NullableProblems")
@NonNull private String localizedTitle;
+ @NonNull public String localizedTitle() {
+ return localizedTitle;
+ }
+ }
+}
--
To view, visit https://gerrit.wikimedia.org/r/317281
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I223090e67bb6206817041ce3669281dbae3cfdff
Gerrit-PatchSet: 1
Gerrit-Project: apps/android/wikipedia
Gerrit-Branch: master
Gerrit-Owner: Mholloway <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits