() {
+ loadable.getSite().delete(new DeleteRequest(loadable.getSite(), post, reply, onlyImageDelete), new Site.DeleteListener() {
@Override
- public void onHttpSuccess(DeleteHttpCall httpPost) {
+ public void onDeleteComplete(HttpCall httpPost, DeleteResponse deleteResponse) {
String message;
- if (httpPost.deleted) {
+ if (deleteResponse.deleted) {
message = getString(R.string.delete_success);
- } else if (!TextUtils.isEmpty(httpPost.errorMessage)) {
- message = httpPost.errorMessage;
+ } else if (!TextUtils.isEmpty(deleteResponse.errorMessage)) {
+ message = deleteResponse.errorMessage;
} else {
message = getString(R.string.delete_error);
}
@@ -618,7 +627,7 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
}
@Override
- public void onHttpFail(DeleteHttpCall httpPost) {
+ public void onDeleteError(HttpCall httpCall) {
threadPresenterCallback.hideDeleting(getString(R.string.delete_error));
}
});
diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/Site.java b/Clover/app/src/main/java/org/floens/chan/core/site/Site.java
index 109a1b90..ee124932 100644
--- a/Clover/app/src/main/java/org/floens/chan/core/site/Site.java
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/Site.java
@@ -1,23 +1,65 @@
+/*
+ * Clover - 4chan browser https://github.com/Floens/Clover/
+ * Copyright (C) 2014 Floens
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
package org.floens.chan.core.site;
import org.floens.chan.chan.ChanLoaderRequest;
import org.floens.chan.chan.ChanLoaderRequestParams;
import org.floens.chan.core.model.Board;
+import org.floens.chan.core.model.Loadable;
+import org.floens.chan.core.model.Post;
+import org.floens.chan.core.site.http.DeleteRequest;
+import org.floens.chan.core.site.http.DeleteResponse;
+import org.floens.chan.core.site.http.HttpCall;
+import org.floens.chan.core.site.http.LoginRequest;
+import org.floens.chan.core.site.http.LoginResponse;
+import org.floens.chan.core.site.http.Reply;
+import org.floens.chan.core.site.http.ReplyResponse;
public interface Site {
enum Feature {
/**
* This site supports posting. (Or rather, we've implemented support for it.)
+ *
+ * @see #post(Reply, PostListener)
+ * @see SiteEndpoints#reply(Loadable)
*/
POSTING,
/**
* This site supports deleting posts.
+ *
+ * @see #delete(DeleteRequest, DeleteListener)
+ * @see SiteEndpoints#delete(Post)
*/
POST_DELETE,
+ /**
+ * This site supports reporting posts.
+ *
+ * @see SiteEndpoints#report(Post)
+ */
+ POST_REPORT,
+
/**
* This site supports some sort of login (like 4pass).
+ *
+ * @see #login(LoginRequest, LoginListener)
+ * @see SiteEndpoints#login()
*/
LOGIN
}
@@ -75,11 +117,35 @@ public interface Site {
void boards(BoardsListener boardsListener);
+ interface BoardsListener {
+ void onBoardsReceived(Boards boards);
+ }
+
Board board(String name);
ChanLoaderRequest loaderRequest(ChanLoaderRequestParams request);
- interface BoardsListener {
- void onBoardsReceived(Boards boards);
+ void post(Reply reply, PostListener postListener);
+
+ interface PostListener {
+ void onPostComplete(HttpCall httpCall, ReplyResponse replyResponse);
+
+ void onPostError(HttpCall httpCall);
+ }
+
+ void delete(DeleteRequest deleteRequest, DeleteListener deleteListener);
+
+ interface DeleteListener {
+ void onDeleteComplete(HttpCall httpCall, DeleteResponse deleteResponse);
+
+ void onDeleteError(HttpCall httpCall);
+ }
+
+ void login(LoginRequest loginRequest, LoginListener loginListener);
+
+ interface LoginListener {
+ void onLoginComplete(HttpCall httpCall, LoginResponse loginResponse);
+
+ void onLoginError(HttpCall httpCall);
}
}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/SiteEndpoints.java b/Clover/app/src/main/java/org/floens/chan/core/site/SiteEndpoints.java
index 088cbc67..e184fe5f 100644
--- a/Clover/app/src/main/java/org/floens/chan/core/site/SiteEndpoints.java
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/SiteEndpoints.java
@@ -1,3 +1,20 @@
+/*
+ * Clover - 4chan browser https://github.com/Floens/Clover/
+ * Copyright (C) 2014 Floens
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
package org.floens.chan.core.site;
import org.floens.chan.core.model.Board;
@@ -22,5 +39,11 @@ public interface SiteEndpoints {
String boards();
- String reply(Board board, Loadable thread);
+ String reply(Loadable thread);
+
+ String delete(Post post);
+
+ String report(Post post);
+
+ String login();
}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/http/DeleteRequest.java b/Clover/app/src/main/java/org/floens/chan/core/site/http/DeleteRequest.java
new file mode 100644
index 00000000..44187cce
--- /dev/null
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/http/DeleteRequest.java
@@ -0,0 +1,37 @@
+/*
+ * Clover - 4chan browser https://github.com/Floens/Clover/
+ * Copyright (C) 2014 Floens
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.floens.chan.core.site.http;
+
+
+import org.floens.chan.core.model.Post;
+import org.floens.chan.core.model.SavedReply;
+import org.floens.chan.core.site.Site;
+
+public class DeleteRequest {
+ public final Site site;
+ public final Post post;
+ public final SavedReply savedReply;
+ public final boolean imageOnly;
+
+ public DeleteRequest(Site site, Post post, SavedReply savedReply, boolean imageOnly) {
+ this.site = site;
+ this.post = post;
+ this.savedReply = savedReply;
+ this.imageOnly = imageOnly;
+ }
+}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/http/DeleteResponse.java b/Clover/app/src/main/java/org/floens/chan/core/site/http/DeleteResponse.java
new file mode 100644
index 00000000..1b7d6b74
--- /dev/null
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/http/DeleteResponse.java
@@ -0,0 +1,24 @@
+/*
+ * Clover - 4chan browser https://github.com/Floens/Clover/
+ * Copyright (C) 2014 Floens
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.floens.chan.core.site.http;
+
+
+public class DeleteResponse {
+ public boolean deleted;
+ public String errorMessage;
+}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/http/HttpCall.java b/Clover/app/src/main/java/org/floens/chan/core/site/http/HttpCall.java
similarity index 62%
rename from Clover/app/src/main/java/org/floens/chan/core/http/HttpCall.java
rename to Clover/app/src/main/java/org/floens/chan/core/site/http/HttpCall.java
index 1656f6e7..94b47a90 100644
--- a/Clover/app/src/main/java/org/floens/chan/core/http/HttpCall.java
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/http/HttpCall.java
@@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.floens.chan.core.http;
+package org.floens.chan.core.site.http;
import org.floens.chan.utils.AndroidUtils;
import org.floens.chan.utils.IOUtils;
@@ -28,29 +28,24 @@ import okhttp3.Callback;
import okhttp3.Request;
import okhttp3.Response;
+/**
+ * Http calls are an abstraction over a normal OkHttp call.
+ * These HttpCalls are used for emulating <form> elements used for posting, reporting, deleting, etc.
+ *
Implement {@link #setup(Request.Builder)} and {@link #process(Response, String)}.
+ * {@code setup()} is called on the main thread, set up up the request builder here. {@code execute()} is
+ * called on a worker thread after the response was executed, do something with the response here.
+ */
public abstract class HttpCall implements Callback {
private static final String TAG = "HttpCall";
private boolean successful = false;
- private ReplyManager.HttpCallback callback;
-
- public void setSuccessful(boolean successful) {
- this.successful = successful;
- }
+ private HttpCallback callback;
+ private Exception exception;
public abstract void setup(Request.Builder requestBuilder);
public abstract void process(Response response, String result) throws IOException;
- @SuppressWarnings("unchecked")
- public void postUI(boolean successful) {
- if (successful) {
- callback.onHttpSuccess(this);
- } else {
- callback.onHttpFail(this);
- }
- }
-
@Override
public void onResponse(Call call, Response response) {
try {
@@ -61,31 +56,60 @@ public abstract class HttpCall implements Callback {
} else {
onFailure(call, null);
}
- } catch (IOException e) {
+ } catch (Exception e) {
+ exception = e;
Logger.e(TAG, "IOException processing response", e);
} finally {
IOUtils.closeQuietly(response.body());
}
+ if (successful) {
+ callSuccess();
+ } else {
+ callFail(exception);
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, IOException e) {
+ callFail(e);
+ }
+
+ public Exception getException() {
+ return exception;
+ }
+
+ public boolean isSuccessful() {
+ return successful;
+ }
+
+ private void callSuccess() {
AndroidUtils.runOnUiThread(new Runnable() {
+ @SuppressWarnings("unchecked")
@Override
public void run() {
- postUI(successful);
+ callback.onHttpSuccess(HttpCall.this);
}
});
}
- @Override
- public void onFailure(Call call, IOException e) {
+ private void callFail(final Exception e) {
AndroidUtils.runOnUiThread(new Runnable() {
+ @SuppressWarnings("unchecked")
@Override
public void run() {
- postUI(false);
+ callback.onHttpFail(HttpCall.this, e);
}
});
}
- void setCallback(ReplyManager.HttpCallback extends HttpCall> callback) {
+ public void setCallback(HttpCallback extends HttpCall> callback) {
this.callback = callback;
}
+
+ public interface HttpCallback {
+ void onHttpSuccess(T httpCall);
+
+ void onHttpFail(T httpCall, Exception e);
+ }
}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/http/HttpCallManager.java b/Clover/app/src/main/java/org/floens/chan/core/site/http/HttpCallManager.java
new file mode 100644
index 00000000..998654a0
--- /dev/null
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/http/HttpCallManager.java
@@ -0,0 +1,63 @@
+/*
+ * Clover - 4chan browser https://github.com/Floens/Clover/
+ * Copyright (C) 2014 Floens
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.floens.chan.core.site.http;
+
+
+import org.floens.chan.core.di.UserAgentProvider;
+
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+
+/**
+ * Manages the {@link HttpCall} executions.
+ */
+@Singleton
+public class HttpCallManager {
+ private static final int TIMEOUT = 30000;
+
+ private UserAgentProvider userAgentProvider;
+ private OkHttpClient client;
+
+ @Inject
+ public HttpCallManager(UserAgentProvider userAgentProvider) {
+ this.userAgentProvider = userAgentProvider;
+ client = new OkHttpClient.Builder()
+ .connectTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
+ .readTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
+ .writeTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
+ .build();
+ }
+
+ public void makeHttpCall(HttpCall httpCall, HttpCall.HttpCallback extends HttpCall> callback) {
+ httpCall.setCallback(callback);
+
+ Request.Builder requestBuilder = new Request.Builder();
+
+ httpCall.setup(requestBuilder);
+
+ requestBuilder.header("User-Agent", userAgentProvider.getUserAgent());
+ Request request = requestBuilder.build();
+
+ client.newCall(request).enqueue(httpCall);
+ }
+}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/http/LoginRequest.java b/Clover/app/src/main/java/org/floens/chan/core/site/http/LoginRequest.java
new file mode 100644
index 00000000..d35d3653
--- /dev/null
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/http/LoginRequest.java
@@ -0,0 +1,33 @@
+/*
+ * Clover - 4chan browser https://github.com/Floens/Clover/
+ * Copyright (C) 2014 Floens
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.floens.chan.core.site.http;
+
+
+import org.floens.chan.core.site.Site;
+
+public class LoginRequest {
+ public final Site site;
+ public final String user;
+ public final String pass;
+
+ public LoginRequest(Site site, String user, String pass) {
+ this.site = site;
+ this.user = user;
+ this.pass = pass;
+ }
+}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/http/LoginResponse.java b/Clover/app/src/main/java/org/floens/chan/core/site/http/LoginResponse.java
new file mode 100644
index 00000000..68d16ab9
--- /dev/null
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/http/LoginResponse.java
@@ -0,0 +1,27 @@
+/*
+ * Clover - 4chan browser https://github.com/Floens/Clover/
+ * Copyright (C) 2014 Floens
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.floens.chan.core.site.http;
+
+
+public class LoginResponse {
+ public boolean success;
+ public String message;
+
+ // TODO(multi-site) make this a cookie abstraction
+ public String token;
+}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/model/Reply.java b/Clover/app/src/main/java/org/floens/chan/core/site/http/Reply.java
similarity index 79%
rename from Clover/app/src/main/java/org/floens/chan/core/model/Reply.java
rename to Clover/app/src/main/java/org/floens/chan/core/site/http/Reply.java
index 400b7f52..a87c37cb 100644
--- a/Clover/app/src/main/java/org/floens/chan/core/model/Reply.java
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/http/Reply.java
@@ -15,7 +15,9 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.floens.chan.core.model;
+package org.floens.chan.core.site.http;
+
+import org.floens.chan.core.model.Loadable;
import java.io.File;
@@ -24,17 +26,19 @@ import java.io.File;
*/
public class Reply {
/**
- * Optional. Null when ReCaptcha v2 was used or a 4pass
+ * Optional. {@code null} when ReCaptcha v2 was used or a 4pass
*/
public String captchaChallenge;
/**
- * Optional. Null when a 4pass was used.
+ * Optional. {@code null} when a 4pass was used.
*/
public String captchaResponse;
- public boolean usePass = false;
- public String board;
- public int resto;
+
+ // TODO(multi-site) flip boolean
+ public boolean noVerification = false;
+
+ public Loadable loadable;
public String passId;
public File file;
diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/http/ReplyResponse.java b/Clover/app/src/main/java/org/floens/chan/core/site/http/ReplyResponse.java
new file mode 100644
index 00000000..77559db9
--- /dev/null
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/http/ReplyResponse.java
@@ -0,0 +1,42 @@
+/*
+ * Clover - 4chan browser https://github.com/Floens/Clover/
+ * Copyright (C) 2014 Floens
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.floens.chan.core.site.http;
+
+import org.floens.chan.core.site.Site;
+
+/**
+ * Generic response for {@link Site#post(Reply, Site.PostListener)} that the reply layout uses.
+ */
+public class ReplyResponse {
+ /**
+ * {@code true} if the post when through, {@code false} otherwise.
+ */
+ public boolean posted;
+
+ /**
+ * Error message used to show to the user if {@link #posted} is {@code false}.
+ * Optional
+ */
+ public String errorMessage;
+
+ // TODO(multi-site)
+ public int threadNo;
+ public int postNo;
+ public String password;
+ public boolean probablyBanned;
+}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java
index f4a9596e..f4348f4e 100644
--- a/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java
@@ -1,5 +1,23 @@
+/*
+ * Clover - 4chan browser https://github.com/Floens/Clover/
+ * Copyright (C) 2014 Floens
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
package org.floens.chan.core.site.sites.chan4;
+import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
@@ -11,7 +29,11 @@ import org.floens.chan.core.model.Post;
import org.floens.chan.core.site.Boards;
import org.floens.chan.core.site.Site;
import org.floens.chan.core.site.SiteEndpoints;
-import org.floens.chan.core.site.loaders.Chan4ReaderRequest;
+import org.floens.chan.core.site.http.DeleteRequest;
+import org.floens.chan.core.site.http.HttpCall;
+import org.floens.chan.core.site.http.HttpCallManager;
+import org.floens.chan.core.site.http.LoginRequest;
+import org.floens.chan.core.site.http.Reply;
import org.floens.chan.utils.Logger;
import java.util.ArrayList;
@@ -21,6 +43,8 @@ import java.util.Locale;
import java.util.Map;
import java.util.Random;
+import javax.inject.Inject;
+
import static org.floens.chan.Chan.getGraph;
public class Chan4 implements Site {
@@ -28,6 +52,12 @@ public class Chan4 implements Site {
private static final Random random = new Random();
+ @Inject
+ HttpCallManager httpCallManager;
+
+ @Inject
+ RequestQueue requestQueue;
+
private final SiteEndpoints endpoints = new SiteEndpoints() {
@Override
public String catalog(Board board) {
@@ -69,12 +99,28 @@ public class Chan4 implements Site {
}
@Override
- public String reply(Board board, Loadable thread) {
- return "https://sys.4chan.org/" + board.code + "/post";
+ public String reply(Loadable loadable) {
+ return "https://sys.4chan.org/" + loadable.getBoard().code + "/post";
+ }
+
+ @Override
+ public String delete(Post post) {
+ return "https://sys.4chan.org/" + post.board.code + "/imgboard.php";
+ }
+
+ @Override
+ public String report(Post post) {
+ return "https://sys.4chan.org/" + post.board.code + "/imgboard.php?mode=report&no=" + post.no;
+ }
+
+ @Override
+ public String login() {
+ return "https://sys.4chan.org/auth";
}
};
public Chan4() {
+ getGraph().inject(this);
}
/**
@@ -100,6 +146,9 @@ public class Chan4 implements Site {
case POST_DELETE:
// yes, with the password saved when posting.
return true;
+ case POST_REPORT:
+ // yes, with a custom url
+ return true;
default:
return false;
}
@@ -144,7 +193,7 @@ public class Chan4 implements Site {
@Override
public void boards(final BoardsListener listener) {
- getGraph().getRequestQueue().add(new Chan4BoardsRequest(this, new Response.Listener>() {
+ requestQueue.add(new Chan4BoardsRequest(this, new Response.Listener>() {
@Override
public void onResponse(List response) {
listener.onBoardsReceived(new Boards(response));
@@ -170,4 +219,49 @@ public class Chan4 implements Site {
public ChanLoaderRequest loaderRequest(ChanLoaderRequestParams request) {
return new ChanLoaderRequest(new Chan4ReaderRequest(request));
}
+
+ @Override
+ public void post(Reply reply, final PostListener postListener) {
+ httpCallManager.makeHttpCall(new Chan4ReplyHttpCall(reply), new HttpCall.HttpCallback() {
+ @Override
+ public void onHttpSuccess(Chan4ReplyHttpCall httpPost) {
+ postListener.onPostComplete(httpPost, httpPost.replyResponse);
+ }
+
+ @Override
+ public void onHttpFail(Chan4ReplyHttpCall httpPost, Exception e) {
+ postListener.onPostError(httpPost);
+ }
+ });
+ }
+
+ @Override
+ public void delete(DeleteRequest deleteRequest, final DeleteListener deleteListener) {
+ httpCallManager.makeHttpCall(new Chan4DeleteHttpCall(deleteRequest), new HttpCall.HttpCallback() {
+ @Override
+ public void onHttpSuccess(Chan4DeleteHttpCall httpPost) {
+ deleteListener.onDeleteComplete(httpPost, httpPost.deleteResponse);
+ }
+
+ @Override
+ public void onHttpFail(Chan4DeleteHttpCall httpPost, Exception e) {
+ deleteListener.onDeleteError(httpPost);
+ }
+ });
+ }
+
+ @Override
+ public void login(LoginRequest loginRequest, final LoginListener loginListener) {
+ httpCallManager.makeHttpCall(new Chan4PassHttpCall(loginRequest), new HttpCall.HttpCallback() {
+ @Override
+ public void onHttpSuccess(Chan4PassHttpCall httpCall) {
+ loginListener.onLoginComplete(httpCall, httpCall.loginResponse);
+ }
+
+ @Override
+ public void onHttpFail(Chan4PassHttpCall httpCall, Exception e) {
+ loginListener.onLoginError(httpCall);
+ }
+ });
+ }
}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/http/DeleteHttpCall.java b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4DeleteHttpCall.java
similarity index 63%
rename from Clover/app/src/main/java/org/floens/chan/core/http/DeleteHttpCall.java
rename to Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4DeleteHttpCall.java
index 213eedd1..ae15969e 100644
--- a/Clover/app/src/main/java/org/floens/chan/core/http/DeleteHttpCall.java
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4DeleteHttpCall.java
@@ -15,10 +15,11 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.floens.chan.core.http;
+package org.floens.chan.core.site.sites.chan4;
-import org.floens.chan.chan.ChanUrls;
-import org.floens.chan.core.model.SavedReply;
+import org.floens.chan.core.site.http.DeleteRequest;
+import org.floens.chan.core.site.http.DeleteResponse;
+import org.floens.chan.core.site.http.HttpCall;
import org.jsoup.Jsoup;
import java.io.IOException;
@@ -29,31 +30,27 @@ import okhttp3.FormBody;
import okhttp3.Request;
import okhttp3.Response;
-public class DeleteHttpCall extends HttpCall {
+public class Chan4DeleteHttpCall extends HttpCall {
private static final Pattern ERROR_MESSAGE = Pattern.compile("\"errmsg\"[^>]*>(.*?)<\\/span");
- public boolean deleted;
- public String errorMessage;
+ private final DeleteRequest deleteRequest;
+ public final DeleteResponse deleteResponse = new DeleteResponse();
- private final SavedReply reply;
- private final boolean onlyImageDelete;
-
- public DeleteHttpCall(final SavedReply reply, boolean onlyImageDelete) {
- this.reply = reply;
- this.onlyImageDelete = onlyImageDelete;
+ public Chan4DeleteHttpCall(DeleteRequest deleteRequest) {
+ this.deleteRequest = deleteRequest;
}
@Override
public void setup(Request.Builder requestBuilder) {
FormBody.Builder formBuilder = new FormBody.Builder();
- formBuilder.add(Integer.toString(reply.no), "delete");
- if (onlyImageDelete) {
+ formBuilder.add(Integer.toString(deleteRequest.post.no), "delete");
+ if (deleteRequest.imageOnly) {
formBuilder.add("onlyimgdel", "on");
}
formBuilder.add("mode", "usrdel");
- formBuilder.add("pwd", reply.password);
+ formBuilder.add("pwd", deleteRequest.savedReply.password);
- requestBuilder.url(ChanUrls.getDeleteUrl(reply.board));
+ requestBuilder.url(deleteRequest.site.endpoints().delete(deleteRequest.post));
requestBuilder.post(formBuilder.build());
}
@@ -61,9 +58,9 @@ public class DeleteHttpCall extends HttpCall {
public void process(Response response, String result) throws IOException {
Matcher errorMessageMatcher = ERROR_MESSAGE.matcher(result);
if (errorMessageMatcher.find()) {
- errorMessage = Jsoup.parse(errorMessageMatcher.group(1)).body().ownText();
+ deleteResponse.errorMessage = Jsoup.parse(errorMessageMatcher.group(1)).body().ownText();
} else {
- deleted = true;
+ deleteResponse.deleted = true;
}
}
}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/http/PassHttpCall.java b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4PassHttpCall.java
similarity index 73%
rename from Clover/app/src/main/java/org/floens/chan/core/http/PassHttpCall.java
rename to Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4PassHttpCall.java
index 4d68f1a5..a2cffa19 100644
--- a/Clover/app/src/main/java/org/floens/chan/core/http/PassHttpCall.java
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4PassHttpCall.java
@@ -15,9 +15,11 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.floens.chan.core.http;
+package org.floens.chan.core.site.sites.chan4;
-import org.floens.chan.chan.ChanUrls;
+import org.floens.chan.core.site.http.HttpCall;
+import org.floens.chan.core.site.http.LoginRequest;
+import org.floens.chan.core.site.http.LoginResponse;
import java.io.IOException;
import java.net.HttpCookie;
@@ -27,17 +29,12 @@ import okhttp3.FormBody;
import okhttp3.Request;
import okhttp3.Response;
-public class PassHttpCall extends HttpCall {
- public boolean success;
- public String message;
- public String passId;
+public class Chan4PassHttpCall extends HttpCall {
+ private final LoginRequest loginRequest;
+ public final LoginResponse loginResponse = new LoginResponse();
- private String token;
- private String pin;
-
- public PassHttpCall(String token, String pin) {
- this.token = token;
- this.pin = pin;
+ public Chan4PassHttpCall(LoginRequest loginRequest) {
+ this.loginRequest = loginRequest;
}
@Override
@@ -46,10 +43,10 @@ public class PassHttpCall extends HttpCall {
formBuilder.add("act", "do_login");
- formBuilder.add("id", token);
- formBuilder.add("pin", pin);
+ formBuilder.add("id", loginRequest.user);
+ formBuilder.add("pin", loginRequest.pass);
- requestBuilder.url(ChanUrls.getPassUrl());
+ requestBuilder.url(loginRequest.site.endpoints().login());
requestBuilder.post(formBuilder.build());
}
@@ -59,6 +56,7 @@ public class PassHttpCall extends HttpCall {
if (result.contains("Success! Your device is now authorized")) {
authSuccess = true;
} else {
+ String message;
if (result.contains("Your Token must be exactly 10 characters")) {
message = "Incorrect token";
} else if (result.contains("You have left one or more fields blank")) {
@@ -68,6 +66,7 @@ public class PassHttpCall extends HttpCall {
} else {
message = "Unknown error";
}
+ loginResponse.message = message;
}
if (authSuccess) {
@@ -86,11 +85,11 @@ public class PassHttpCall extends HttpCall {
}
if (passId != null) {
- this.passId = passId;
- message = "Success! Your device is now authorized.";
- success = true;
+ loginResponse.token = passId;
+ loginResponse.message = "Success! Your device is now authorized.";
+ loginResponse.success = true;
} else {
- message = "Could not get pass id";
+ loginResponse.message = "Could not get pass id";
}
}
}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/loaders/Chan4ReaderRequest.java b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4ReaderRequest.java
similarity index 99%
rename from Clover/app/src/main/java/org/floens/chan/core/site/loaders/Chan4ReaderRequest.java
rename to Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4ReaderRequest.java
index ac3a6db9..c773dc2c 100644
--- a/Clover/app/src/main/java/org/floens/chan/core/site/loaders/Chan4ReaderRequest.java
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4ReaderRequest.java
@@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.floens.chan.core.site.loaders;
+package org.floens.chan.core.site.sites.chan4;
import android.util.JsonReader;
diff --git a/Clover/app/src/main/java/org/floens/chan/core/http/ReplyHttpCall.java b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4ReplyHttpCall.java
similarity index 69%
rename from Clover/app/src/main/java/org/floens/chan/core/http/ReplyHttpCall.java
rename to Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4ReplyHttpCall.java
index 5eca9c22..c9c85540 100644
--- a/Clover/app/src/main/java/org/floens/chan/core/http/ReplyHttpCall.java
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4ReplyHttpCall.java
@@ -15,12 +15,14 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.floens.chan.core.http;
+package org.floens.chan.core.site.sites.chan4;
import android.text.TextUtils;
-import org.floens.chan.chan.ChanUrls;
-import org.floens.chan.core.model.Reply;
+import org.floens.chan.core.site.Site;
+import org.floens.chan.core.site.http.HttpCall;
+import org.floens.chan.core.site.http.ReplyResponse;
+import org.floens.chan.core.site.http.Reply;
import org.jsoup.Jsoup;
import java.io.IOException;
@@ -34,40 +36,34 @@ import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
-public class ReplyHttpCall extends HttpCall {
- private static final String TAG = "ReplyHttpCall";
+public class Chan4ReplyHttpCall extends HttpCall {
+ private static final String TAG = "Chan4ReplyHttpCall";
private static final Random RANDOM = new Random();
private static final Pattern THREAD_NO_PATTERN = Pattern.compile("");
private static final Pattern ERROR_MESSAGE = Pattern.compile("\"errmsg\"[^>]*>(.*?)<\\/span");
+ private static final String PROBABLY_BANNED_TEXT = "banned";
- public boolean posted;
- public String errorMessage;
- public String text;
- public String password;
- public int threadNo = -1;
- public int postNo = -1;
- public boolean probablyBanned;
+ public final Reply reply;
+ public final ReplyResponse replyResponse = new ReplyResponse();
- private final Reply reply;
-
- public ReplyHttpCall(Reply reply) {
+ public Chan4ReplyHttpCall(Reply reply) {
this.reply = reply;
}
@Override
public void setup(Request.Builder requestBuilder) {
- boolean thread = reply.resto >= 0;
+ boolean thread = reply.loadable.isThreadMode();
- password = Long.toHexString(RANDOM.nextLong());
+ replyResponse.password = Long.toHexString(RANDOM.nextLong());
MultipartBody.Builder formBuilder = new MultipartBody.Builder();
formBuilder.setType(MultipartBody.FORM);
formBuilder.addFormDataPart("mode", "regist");
- formBuilder.addFormDataPart("pwd", password);
+ formBuilder.addFormDataPart("pwd", replyResponse.password);
if (thread) {
- formBuilder.addFormDataPart("resto", String.valueOf(reply.resto));
+ formBuilder.addFormDataPart("resto", String.valueOf(reply.loadable.no));
}
formBuilder.addFormDataPart("name", reply.name);
@@ -79,7 +75,7 @@ public class ReplyHttpCall extends HttpCall {
formBuilder.addFormDataPart("com", reply.comment);
- if (reply.captchaResponse != null) {
+ if (!reply.noVerification) {
if (reply.captchaChallenge != null) {
formBuilder.addFormDataPart("recaptcha_challenge_field", reply.captchaChallenge);
formBuilder.addFormDataPart("recaptcha_response_field", reply.captchaResponse);
@@ -98,33 +94,32 @@ public class ReplyHttpCall extends HttpCall {
formBuilder.addFormDataPart("spoiler", "on");
}
- requestBuilder.url(ChanUrls.getReplyUrl(reply.board));
+ Site site = reply.loadable.getSite();
+ requestBuilder.url(site.endpoints().reply(reply.loadable));
requestBuilder.post(formBuilder.build());
- if (reply.usePass) {
+ if (reply.noVerification) {
requestBuilder.addHeader("Cookie", "pass_id=" + reply.passId);
}
}
@Override
public void process(Response response, String result) throws IOException {
- text = result;
-
Matcher errorMessageMatcher = ERROR_MESSAGE.matcher(result);
if (errorMessageMatcher.find()) {
- errorMessage = Jsoup.parse(errorMessageMatcher.group(1)).body().text();
- probablyBanned = errorMessage.contains("banned");
+ replyResponse.errorMessage = Jsoup.parse(errorMessageMatcher.group(1)).body().text();
+ replyResponse.probablyBanned = replyResponse.errorMessage.contains(PROBABLY_BANNED_TEXT);
} else {
Matcher threadNoMatcher = THREAD_NO_PATTERN.matcher(result);
if (threadNoMatcher.find()) {
try {
- threadNo = Integer.parseInt(threadNoMatcher.group(1));
- postNo = Integer.parseInt(threadNoMatcher.group(2));
+ replyResponse.threadNo = Integer.parseInt(threadNoMatcher.group(1));
+ replyResponse.postNo = Integer.parseInt(threadNoMatcher.group(2));
} catch (NumberFormatException ignored) {
}
- if (threadNo >= 0 && postNo >= 0) {
- posted = true;
+ if (replyResponse.threadNo >= 0 && replyResponse.postNo >= 0) {
+ replyResponse.posted = true;
}
}
}
diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/loaders/PostParseCallable.java b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/PostParseCallable.java
similarity index 98%
rename from Clover/app/src/main/java/org/floens/chan/core/site/loaders/PostParseCallable.java
rename to Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/PostParseCallable.java
index d969b2f7..16807a3b 100644
--- a/Clover/app/src/main/java/org/floens/chan/core/site/loaders/PostParseCallable.java
+++ b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/PostParseCallable.java
@@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.floens.chan.core.site.loaders;
+package org.floens.chan.core.site.sites.chan4;
import org.floens.chan.chan.ChanParser;
import org.floens.chan.core.database.DatabaseSavedReplyManager;
diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java
index 7ffbf7af..38db03c8 100644
--- a/Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java
+++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java
@@ -26,12 +26,16 @@ import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
-import org.floens.chan.Chan;
import org.floens.chan.R;
import org.floens.chan.controller.Controller;
-import org.floens.chan.core.http.PassHttpCall;
-import org.floens.chan.core.http.ReplyManager;
+import org.floens.chan.core.manager.ReplyManager;
import org.floens.chan.core.settings.ChanSettings;
+import org.floens.chan.core.site.Site;
+import org.floens.chan.core.site.Sites;
+import org.floens.chan.core.site.http.HttpCall;
+import org.floens.chan.core.site.http.HttpCallManager;
+import org.floens.chan.core.site.http.LoginRequest;
+import org.floens.chan.core.site.http.LoginResponse;
import org.floens.chan.ui.view.CrossfadeView;
import org.floens.chan.utils.AndroidUtils;
import org.floens.chan.utils.AnimationUtils;
@@ -41,10 +45,13 @@ import javax.inject.Inject;
import static org.floens.chan.Chan.getGraph;
import static org.floens.chan.utils.AndroidUtils.getString;
-public class PassSettingsController extends Controller implements View.OnClickListener, ReplyManager.HttpCallback {
+public class PassSettingsController extends Controller implements View.OnClickListener, Site.LoginListener {
@Inject
ReplyManager replyManager;
+ @Inject
+ HttpCallManager httpCallManager;
+
private LinearLayout container;
private CrossfadeView crossfadeView;
private TextView errors;
@@ -120,36 +127,37 @@ public class PassSettingsController extends Controller implements View.OnClickLi
}
@Override
- public void onHttpSuccess(PassHttpCall httpPost) {
- if (httpPost.success) {
- authSuccess(httpPost);
+ public void onLoginComplete(HttpCall httpCall, LoginResponse loginResponse) {
+ if (loginResponse.success) {
+ authSuccess(loginResponse);
} else {
- authFail(httpPost);
+ authFail(loginResponse);
}
authAfter();
}
@Override
- public void onHttpFail(PassHttpCall httpPost) {
- authFail(httpPost);
+ public void onLoginError(HttpCall httpCall) {
+ authFail(null);
authAfter();
}
- private void authSuccess(PassHttpCall httpPost) {
+ private void authSuccess(LoginResponse response) {
crossfadeView.toggle(false, true);
button.setText(R.string.setting_pass_logout);
- ChanSettings.passId.set(httpPost.passId);
- authenticated.setText(httpPost.message);
+ ChanSettings.passId.set(response.token);
+ authenticated.setText(response.message);
((PassSettingControllerListener) previousSiblingController).onPassEnabledChanged(true);
}
- private void authFail(PassHttpCall httpPost) {
- if (httpPost.message == null) {
- httpPost.message = getString(R.string.setting_pass_error);
+ private void authFail(LoginResponse response) {
+ String message = getString(R.string.setting_pass_error);
+ if (response != null && response.message != null) {
+ message = response.message;
}
- showError(httpPost.message);
+ showError(message);
button.setText(R.string.setting_pass_login);
}
@@ -170,7 +178,9 @@ public class PassSettingsController extends Controller implements View.OnClickLi
ChanSettings.passToken.set(inputToken.getText().toString());
ChanSettings.passPin.set(inputPin.getText().toString());
- replyManager.makeHttpCall(new PassHttpCall(ChanSettings.passToken.get(), ChanSettings.passPin.get()), this);
+ // TODO(multi-site) some selector of some sorts
+ Site site = Sites.defaultSite();
+ site.login(new LoginRequest(site, ChanSettings.passToken.get(), ChanSettings.passPin.get()), this);
}
private void showError(String error) {
diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ReportController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ReportController.java
index c3ab73e7..44db40e5 100644
--- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ReportController.java
+++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ReportController.java
@@ -20,6 +20,7 @@ package org.floens.chan.ui.controller;
import android.annotation.SuppressLint;
import android.content.Context;
import android.webkit.CookieManager;
+import android.webkit.WebSettings;
import android.webkit.WebView;
import org.floens.chan.R;
@@ -27,8 +28,12 @@ import org.floens.chan.chan.ChanUrls;
import org.floens.chan.controller.Controller;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.settings.ChanSettings;
+import org.floens.chan.core.site.Site;
+import org.floens.chan.core.site.Sites;
import org.floens.chan.ui.helper.PostHelper;
+import okhttp3.HttpUrl;
+
public class ReportController extends Controller {
private Post post;
@@ -44,18 +49,26 @@ public class ReportController extends Controller {
super.onCreate();
navigationItem.title = context.getString(R.string.report_screen, PostHelper.getTitle(post, null));
- CookieManager cookieManager = CookieManager.getInstance();
- cookieManager.removeAllCookie();
- if (ChanSettings.passLoggedIn()) {
- for (String cookie : ChanUrls.getReportCookies(ChanSettings.passId.get())) {
- cookieManager.setCookie(ChanUrls.getReportDomain(), cookie);
+ Site site = post.board.getSite();
+ String url = site.endpoints().report(post);
+
+ if (site == Sites.CHAN4) {
+ CookieManager cookieManager = CookieManager.getInstance();
+ cookieManager.removeAllCookie();
+ if (ChanSettings.passLoggedIn()) {
+ HttpUrl parsed = HttpUrl.parse(url);
+ String domain = parsed.scheme() + "://" + parsed.host() + "/";
+ for (String cookie : ChanUrls.getReportCookies(ChanSettings.passId.get())) {
+ cookieManager.setCookie(domain, cookie);
+ }
}
}
WebView webView = new WebView(context);
- webView.getSettings().setJavaScriptEnabled(true);
- webView.getSettings().setDomStorageEnabled(true);
- webView.loadUrl(ChanUrls.getReportUrl(post.boardId, post.no));
+ WebSettings settings = webView.getSettings();
+ settings.setJavaScriptEnabled(true);
+ settings.setDomStorageEnabled(true);
+ webView.loadUrl(url);
view = webView;
}
}
diff --git a/Clover/app/src/main/java/org/floens/chan/ui/helper/ImagePickDelegate.java b/Clover/app/src/main/java/org/floens/chan/ui/helper/ImagePickDelegate.java
index b266e78c..1cac1c03 100644
--- a/Clover/app/src/main/java/org/floens/chan/ui/helper/ImagePickDelegate.java
+++ b/Clover/app/src/main/java/org/floens/chan/ui/helper/ImagePickDelegate.java
@@ -21,12 +21,10 @@ import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
-import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.provider.OpenableColumns;
-import org.floens.chan.Chan;
-import org.floens.chan.core.http.ReplyManager;
+import org.floens.chan.core.manager.ReplyManager;
import org.floens.chan.utils.IOUtils;
import org.floens.chan.utils.Logger;
diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java
index f096263c..b9d84173 100644
--- a/Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java
+++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java
@@ -37,7 +37,7 @@ import android.widget.Toast;
import org.floens.chan.R;
import org.floens.chan.core.model.ChanThread;
import org.floens.chan.core.model.Loadable;
-import org.floens.chan.core.model.Reply;
+import org.floens.chan.core.site.http.Reply;
import org.floens.chan.core.presenter.ReplyPresenter;
import org.floens.chan.core.settings.ChanSettings;
import org.floens.chan.ui.activity.StartActivity;