Migrate gotify to new client

This commit is contained in:
Jannis Mattheis
2018-11-14 19:39:22 +01:00
parent 3a9da353b4
commit d4c6273214
13 changed files with 209 additions and 117 deletions

View File

@@ -1,9 +1,8 @@
package com.github.gotify;
import com.github.gotify.api.Api;
import com.github.gotify.api.ApiException;
import com.github.gotify.api.Callback;
import com.github.gotify.client.ApiClient;
import com.github.gotify.client.ApiException;
import com.github.gotify.client.api.MessageApi;
import com.github.gotify.client.model.Message;
import com.github.gotify.client.model.PagedMessages;
@@ -12,26 +11,30 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static com.github.gotify.api.Callback.call;
public class MissedMessageUtil {
static final int NO_MESSAGES = 0;
private final MessageApi api;
public MissedMessageUtil(ApiClient client) {
api = new MessageApi(client);
public MissedMessageUtil(MessageApi api) {
this.api = api;
}
public void lastReceivedMessage(Callback.SuccessCallback<Integer> successCallback) {
Api.<PagedMessages>withLogging((cb) -> api.getMessagesAsync(1, 0, cb))
.handle(
(messages) -> {
if (messages.getMessages().size() == 1) {
successCallback.onSuccess(messages.getMessages().get(0).getId());
} else {
successCallback.onSuccess(NO_MESSAGES);
}
},
(e) -> {});
api.getMessages(1, 0)
.enqueue(
call(
(messages) -> {
if (messages.getMessages().size() == 1) {
successCallback.onSuccess(
messages.getMessages().get(0).getId());
} else {
successCallback.onSuccess(NO_MESSAGES);
}
},
(e) -> {}));
}
public List<Message> missingMessages(int till) {
@@ -40,7 +43,7 @@ public class MissedMessageUtil {
Integer since = null;
while (true) {
PagedMessages pagedMessages = api.getMessages(10, since);
PagedMessages pagedMessages = Api.execute(api.getMessages(10, since));
List<Message> messages = pagedMessages.getMessages();
List<Message> filtered = filter(messages, till);
result.addAll(filtered);

View File

@@ -8,10 +8,10 @@ import android.graphics.drawable.Drawable;
import android.text.format.DateUtils;
import android.view.View;
import androidx.annotation.NonNull;
import com.github.gotify.client.ApiClient;
import com.github.gotify.client.JSON;
import com.github.gotify.log.Log;
import com.google.android.material.snackbar.Snackbar;
import com.google.gson.Gson;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
import java.io.BufferedReader;
@@ -22,6 +22,8 @@ import okio.Buffer;
import org.threeten.bp.OffsetDateTime;
public class Utils {
public static final Gson JSON = new JSON().getGson();
public static void showSnackBar(Activity activity, String message) {
View rootView = activity.getWindow().getDecorView().findViewById(android.R.id.content);
Snackbar.make(rootView, message, Snackbar.LENGTH_SHORT).show();
@@ -51,10 +53,6 @@ public class Utils {
};
}
public static JSON json() {
return new ApiClient().getJSON();
}
public static String readFileFromStream(@NonNull InputStream inputStream) {
StringBuilder sb = new StringBuilder();
String currentLine;

View File

@@ -1,4 +1,21 @@
package com.github.gotify.api;
import java.io.IOException;
import retrofit2.Call;
import retrofit2.Response;
public class Api {
public static <T> T execute(Call<T> call) throws ApiException {
try {
Response<T> response = call.execute();
if (response.isSuccessful()) {
return response.body();
} else {
throw new ApiException(response);
}
} catch (IOException e) {
throw new ApiException(e);
}
}
}

View File

@@ -1,4 +1,44 @@
package com.github.gotify.api;
class ApiException {
import java.io.IOException;
import java.util.Locale;
import retrofit2.Response;
public final class ApiException extends Exception {
private String body;
private int code;
ApiException(Response<?> response) {
super("Api Error", null);
try {
this.body = response.errorBody() != null ? response.errorBody().string() : "";
} catch (IOException e) {
this.body = "Error while getting error body :(";
}
this.code = response.code();
}
ApiException(Throwable cause) {
super("Request failed.", cause);
this.body = "";
this.code = 0;
}
public String body() {
return body;
}
public int code() {
return code;
}
@Override
public String toString() {
return String.format(
Locale.ENGLISH,
"Code(%d) Response: %s",
code(),
body().substring(0, Math.min(body().length(), 200)));
}
}

View File

@@ -1,7 +1,9 @@
package com.github.gotify.api;
import android.app.Activity;
import com.github.gotify.client.ApiException;
import com.github.gotify.log.Log;
import retrofit2.Call;
import retrofit2.Response;
public class Callback<T> {
private final SuccessCallback<T> onSuccess;
@@ -12,37 +14,39 @@ public class Callback<T> {
this.onError = onError;
}
void onSuccess(T data) {
this.onSuccess.onSuccess(data);
public static <T> retrofit2.Callback<T> callInUI(
Activity context, SuccessCallback<T> onSuccess, ErrorCallback onError) {
return call(
(data) -> context.runOnUiThread(() -> onSuccess.onSuccess(data)),
(e) -> context.runOnUiThread(() -> onError.onError(e)));
}
void onError(ApiException exception) {
this.onError.onError(exception);
public static <T> retrofit2.Callback<T> call() {
return call((e) -> {}, (e) -> {});
}
public static <T> Callback<T> call(SuccessCallback<T> onSuccess, ErrorCallback onError) {
public static <T> retrofit2.Callback<T> call(
SuccessCallback<T> onSuccess, ErrorCallback onError) {
return new RetrofitCallback<>(merge(of(onSuccess, onError), errorCallback()));
}
private static <T> Callback<T> of(SuccessCallback<T> onSuccess, ErrorCallback onError) {
return new Callback<>(onSuccess, onError);
}
public static <T> Callback<T> merge(Callback<T> left, Callback<T> right) {
return new Callback<>(
(data) -> {
left.onSuccess(data);
right.onSuccess(data);
},
(error) -> {
left.onError(error);
right.onError(error);
});
private static <T> Callback<T> errorCallback() {
return new Callback<>((ignored) -> {}, (error) -> Log.e("Error while api call", error));
}
public static <T> Callback<T> runInUIThread(Activity context, Callback<T> callback) {
return call(
private static <T> Callback<T> merge(Callback<T> left, Callback<T> right) {
return new Callback<>(
(data) -> {
context.runOnUiThread(() -> callback.onSuccess(data));
left.onSuccess.onSuccess(data);
right.onSuccess.onSuccess(data);
},
(e) -> {
context.runOnUiThread(() -> callback.onError(e));
(error) -> {
left.onError.onError(error);
right.onError.onError(error);
});
}
@@ -51,6 +55,29 @@ public class Callback<T> {
}
public interface ErrorCallback {
void onError(ApiException e);
void onError(ApiException t);
}
private static final class RetrofitCallback<T> implements retrofit2.Callback<T> {
private Callback<T> callback;
private RetrofitCallback(Callback<T> callback) {
this.callback = callback;
}
@Override
public void onResponse(Call<T> call, Response<T> response) {
if (response.isSuccessful()) {
callback.onSuccess.onSuccess(response.body());
} else {
callback.onError.onError(new ApiException(response));
}
}
@Override
public void onFailure(Call<T> call, Throwable t) {
callback.onError.onError(new ApiException(t));
}
}
}

View File

@@ -2,7 +2,6 @@ package com.github.gotify.api;
import com.github.gotify.SSLSettings;
import com.github.gotify.Settings;
import com.github.gotify.Utils;
import com.github.gotify.client.ApiClient;
import com.github.gotify.client.api.UserApi;
import com.github.gotify.client.api.VersionApi;
@@ -10,36 +9,41 @@ import com.github.gotify.client.auth.ApiKeyAuth;
import com.github.gotify.client.auth.HttpBasicAuth;
public class ClientFactory {
public static ApiClient unauthorized(String baseUrl, SSLSettings sslSettings) {
ApiClient client = new ApiClient();
client.setVerifyingSsl(sslSettings.validateSSL);
client.setSslCaCert(Utils.stringToInputStream(sslSettings.cert));
client.setBasePath(baseUrl);
return client;
public static com.github.gotify.client.ApiClient unauthorized(
String baseUrl, SSLSettings sslSettings) {
return defaultClient(new String[0], baseUrl, sslSettings);
}
public static ApiClient basicAuth(
String baseUrl, SSLSettings sslSettings, String username, String password) {
ApiClient client = unauthorized(baseUrl, sslSettings);
HttpBasicAuth auth = (HttpBasicAuth) client.getAuthentication("basicAuth");
ApiClient client = defaultClient(new String[] {"basicAuth"}, baseUrl, sslSettings);
HttpBasicAuth auth = (HttpBasicAuth) client.getApiAuthorizations().get("basicAuth");
auth.setUsername(username);
auth.setPassword(password);
return client;
}
public static ApiClient clientToken(String baseUrl, SSLSettings sslSettings, String token) {
ApiClient client = unauthorized(baseUrl, sslSettings);
ApiKeyAuth tokenAuth = (ApiKeyAuth) client.getAuthentication("clientTokenHeader");
ApiClient client = defaultClient(new String[] {"clientTokenHeader"}, baseUrl, sslSettings);
ApiKeyAuth tokenAuth = (ApiKeyAuth) client.getApiAuthorizations().get("clientTokenHeader");
tokenAuth.setApiKey(token);
return client;
}
public static VersionApi versionApi(String baseUrl, SSLSettings sslSettings) {
return new VersionApi(unauthorized(baseUrl, sslSettings));
return unauthorized(baseUrl, sslSettings).createService(VersionApi.class);
}
public static UserApi userApiWithToken(Settings settings) {
return new UserApi(clientToken(settings.url(), settings.sslSettings(), settings.token()));
return clientToken(settings.url(), settings.sslSettings(), settings.token())
.createService(UserApi.class);
}
private static ApiClient defaultClient(
String[] authentications, String baseUrl, SSLSettings sslSettings) {
ApiClient client = new ApiClient(authentications);
CertUtils.applySslSettings(client.getOkBuilder(), sslSettings);
client.getAdapterBuilder().baseUrl(baseUrl);
return client;
}
}

View File

@@ -11,11 +11,9 @@ import androidx.appcompat.app.AppCompatActivity;
import com.github.gotify.NotificationSupport;
import com.github.gotify.R;
import com.github.gotify.Settings;
import com.github.gotify.api.Api;
import com.github.gotify.api.ApiException;
import com.github.gotify.api.Callback;
import com.github.gotify.api.ClientFactory;
import com.github.gotify.client.ApiException;
import com.github.gotify.client.api.VersionApi;
import com.github.gotify.client.model.User;
import com.github.gotify.client.model.VersionInfo;
import com.github.gotify.log.Log;
@@ -24,6 +22,8 @@ import com.github.gotify.login.LoginActivity;
import com.github.gotify.messages.MessagesActivity;
import com.github.gotify.service.WebSocketService;
import static com.github.gotify.api.Callback.callInUI;
public class InitializationActivity extends AppCompatActivity {
private Settings settings;
@@ -55,24 +55,25 @@ public class InitializationActivity extends AppCompatActivity {
}
private void tryAuthenticate() {
Api.withLogging(ClientFactory.userApiWithToken(settings)::currentUserAsync)
.handleInUIThread(this, this::authenticated, this::failed);
ClientFactory.userApiWithToken(settings)
.currentUser()
.enqueue(callInUI(this, this::authenticated, this::failed));
}
private void failed(ApiException exception) {
if (exception.getCode() == 0) {
if (exception.code() == 0) {
dialog(getString(R.string.not_available, settings.url()));
return;
}
if (exception.getCode() == 401) {
if (exception.code() == 401) {
dialog(getString(R.string.auth_failed));
return;
}
String response = exception.getResponseBody();
String response = exception.body();
response = response.substring(0, Math.min(200, response.length()));
dialog(getString(R.string.other_error, settings.url(), exception.getCode(), response));
dialog(getString(R.string.other_error, settings.url(), exception.code(), response));
}
private void dialog(String message) {
@@ -116,8 +117,8 @@ public class InitializationActivity extends AppCompatActivity {
private void requestVersion(
final Callback.SuccessCallback<VersionInfo> callback,
final Callback.ErrorCallback errorCallback) {
VersionApi versionApi = ClientFactory.versionApi(settings.url(), settings.sslSettings());
Api.withLogging(versionApi::getVersionAsync)
.handleInUIThread(this, callback, errorCallback);
ClientFactory.versionApi(settings.url(), settings.sslSettings())
.getVersion()
.enqueue(callInUI(this, callback, errorCallback));
}
}

View File

@@ -22,12 +22,11 @@ import com.github.gotify.R;
import com.github.gotify.SSLSettings;
import com.github.gotify.Settings;
import com.github.gotify.Utils;
import com.github.gotify.api.Api;
import com.github.gotify.api.ApiException;
import com.github.gotify.api.Callback;
import com.github.gotify.api.CertUtils;
import com.github.gotify.api.ClientFactory;
import com.github.gotify.client.ApiClient;
import com.github.gotify.client.ApiException;
import com.github.gotify.client.api.TokenApi;
import com.github.gotify.client.api.UserApi;
import com.github.gotify.client.model.Client;
@@ -36,10 +35,12 @@ import com.github.gotify.init.InitializationActivity;
import com.github.gotify.log.Log;
import com.github.gotify.log.LogsActivity;
import com.github.gotify.log.UncaughtExceptionHandler;
import com.squareup.okhttp.HttpUrl;
import java.io.InputStream;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import okhttp3.HttpUrl;
import static com.github.gotify.api.Callback.callInUI;
public class LoginActivity extends AppCompatActivity {
@@ -111,12 +112,9 @@ public class LoginActivity extends AppCompatActivity {
final String fixedUrl = url.endsWith("/") ? url.substring(0, url.length() - 1) : url;
Api.withLogging(
ClientFactory.versionApi(
fixedUrl,
new SSLSettings(!disableSSLValidation, caCertContents))
::getVersionAsync)
.handleInUIThread(this, onValidUrl(fixedUrl), onInvalidUrl(fixedUrl));
ClientFactory.versionApi(fixedUrl, tempSSLSettings())
.getVersion()
.enqueue(callInUI(this, onValidUrl(fixedUrl), onInvalidUrl(fixedUrl)));
}
@OnClick(R.id.open_logs)
@@ -161,7 +159,7 @@ public class LoginActivity extends AppCompatActivity {
FILE_SELECT_CODE);
} catch (ActivityNotFoundException e) {
// case for user not having a file browser installed
Utils.showSnackBar(LoginActivity.this, getString(R.string.please_install_file_browser));
Utils.showSnackBar(this, getString(R.string.please_install_file_browser));
}
}
@@ -194,8 +192,7 @@ public class LoginActivity extends AppCompatActivity {
advancedDialog.showRemoveCACertificate(name);
}
} catch (Exception e) {
Utils.showSnackBar(
LoginActivity.this, getString(R.string.select_ca_failed, e.getMessage()));
Utils.showSnackBar(this, getString(R.string.select_ca_failed, e.getMessage()));
}
}
@@ -234,13 +231,10 @@ public class LoginActivity extends AppCompatActivity {
loginProgress.setVisibility(View.VISIBLE);
ApiClient client =
ClientFactory.basicAuth(
settings.url(),
new SSLSettings(!disableSSLValidation, caCertContents),
username,
password);
Api.withLogging(new UserApi(client)::currentUserAsync)
.handleInUIThread(this, (user) -> newClientDialog(client), this::onInvalidLogin);
ClientFactory.basicAuth(settings.url(), tempSSLSettings(), username, password);
client.createService(UserApi.class)
.currentUser()
.enqueue(callInUI(this, (user) -> newClientDialog(client), this::onInvalidLogin));
}
private void onInvalidLogin(ApiException e) {
@@ -265,8 +259,9 @@ public class LoginActivity extends AppCompatActivity {
public DialogInterface.OnClickListener doCreateClient(ApiClient client, EditText nameProvider) {
return (a, b) -> {
Client newClient = new Client().name(nameProvider.getText().toString());
Api.<Client>withLogging((cb) -> new TokenApi(client).createClientAsync(newClient, cb))
.handleInUIThread(this, this::onCreatedClient, this::onFailedToCreateClient);
client.createService(TokenApi.class)
.createClient(newClient)
.enqueue(callInUI(this, this::onCreatedClient, this::onFailedToCreateClient));
};
}
@@ -292,6 +287,10 @@ public class LoginActivity extends AppCompatActivity {
}
private String versionError(String url, ApiException exception) {
return getString(R.string.version_failed, url + "/version", exception.getCode());
return getString(R.string.version_failed, url + "/version", exception.code());
}
private SSLSettings tempSSLSettings() {
return new SSLSettings(!disableSSLValidation, caCertContents);
}
}

View File

@@ -27,10 +27,11 @@ import com.github.gotify.MissedMessageUtil;
import com.github.gotify.R;
import com.github.gotify.Settings;
import com.github.gotify.Utils;
import com.github.gotify.api.Api;
import com.github.gotify.api.ApiException;
import com.github.gotify.api.CertUtils;
import com.github.gotify.api.ClientFactory;
import com.github.gotify.client.ApiClient;
import com.github.gotify.client.ApiException;
import com.github.gotify.client.api.MessageApi;
import com.github.gotify.client.api.TokenApi;
import com.github.gotify.client.model.Application;
@@ -46,13 +47,13 @@ import com.github.gotify.messages.provider.MessageState;
import com.github.gotify.messages.provider.MessageWithImage;
import com.github.gotify.service.WebSocketService;
import com.google.android.material.navigation.NavigationView;
import com.squareup.okhttp.HttpUrl;
import com.squareup.picasso.OkHttp3Downloader;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import static java.util.Collections.emptyList;
@@ -65,7 +66,7 @@ public class MessagesActivity extends AppCompatActivity
@Override
public void onReceive(Context context, Intent intent) {
String messageJson = intent.getStringExtra("message");
Message message = Utils.json().deserialize(messageJson, Message.class);
Message message = Utils.JSON.fromJson(messageJson, Message.class);
new NewSingleMessage().execute(message);
}
};
@@ -121,7 +122,7 @@ public class MessagesActivity extends AppCompatActivity
appsHolder.request();
initDrawer();
messages = new MessageFacade(new MessageApi(client), appsHolder);
messages = new MessageFacade(client.createService(MessageApi.class), appsHolder);
messagesView.setOnScrollListener(this);
messagesView.setAdapter(new ListMessageAdapter(this, picasso, emptyList(), this::delete));
@@ -310,7 +311,9 @@ public class MessagesActivity extends AppCompatActivity
return false;
}
List<Message> newMessages = new MissedMessageUtil(client).missingMessages(id);
List<Message> newMessages =
new MissedMessageUtil(client.createService(MessageApi.class))
.missingMessages(id);
messages.addMessages(newMessages);
return !newMessages.isEmpty();
}
@@ -423,12 +426,12 @@ public class MessagesActivity extends AppCompatActivity
@Override
protected Void doInBackground(Void... ignore) {
TokenApi api =
new TokenApi(
ClientFactory.clientToken(
settings.url(), settings.sslSettings(), settings.token()));
ClientFactory.clientToken(
settings.url(), settings.sslSettings(), settings.token())
.createService(TokenApi.class);
stopService(new Intent(MessagesActivity.this, WebSocketService.class));
try {
List<Client> clients = api.getClients();
List<Client> clients = Api.execute(api.getClients());
Client currentClient = null;
for (Client client : clients) {

View File

@@ -2,9 +2,9 @@ package com.github.gotify.messages.provider;
import android.app.Activity;
import com.github.gotify.Utils;
import com.github.gotify.api.Api;
import com.github.gotify.api.ApiException;
import com.github.gotify.api.Callback;
import com.github.gotify.client.ApiClient;
import com.github.gotify.client.ApiException;
import com.github.gotify.client.api.TokenApi;
import com.github.gotify.client.model.Application;
import java.util.Collections;
@@ -28,9 +28,9 @@ public class ApplicationHolder {
}
public void request() {
TokenApi tokenApi = new TokenApi(client);
Api.withLogging(tokenApi::getAppsAsync)
.handleInUIThread(activity, this::onReceiveApps, this::onFailedApps);
client.createService(TokenApi.class)
.getApps()
.enqueue(Callback.callInUI(activity, this::onReceiveApps, this::onFailedApps));
}
private void onReceiveApps(List<Application> apps) {

View File

@@ -1,7 +1,8 @@
package com.github.gotify.messages.provider;
import com.github.gotify.api.Api;
import com.github.gotify.client.ApiException;
import com.github.gotify.api.ApiException;
import com.github.gotify.api.Callback;
import com.github.gotify.client.api.MessageApi;
import com.github.gotify.client.model.Message;
import com.github.gotify.client.model.PagedMessages;
@@ -19,9 +20,9 @@ class MessageRequester {
try {
Log.i("Loading more messages for " + state.appId);
if (MessageState.ALL_MESSAGES == state.appId) {
return messageApi.getMessages(LIMIT, state.nextSince);
return Api.execute(messageApi.getMessages(LIMIT, state.nextSince));
} else {
return messageApi.getAppMessages(state.appId, LIMIT, state.nextSince);
return Api.execute(messageApi.getAppMessages(state.appId, LIMIT, state.nextSince));
}
} catch (ApiException apiException) {
Log.e("failed requesting messages", apiException);
@@ -31,17 +32,16 @@ class MessageRequester {
void asyncRemoveMessage(Message message) {
Log.i("Removing message with id " + message.getId());
Api.<Void>withLogging((cb) -> messageApi.deleteMessageAsync(message.getId(), cb))
.handle((a) -> {}, (e) -> {});
messageApi.deleteMessage(message.getId()).enqueue(Callback.call());
}
boolean deleteAll(Integer appId) {
try {
Log.i("Deleting all messages for " + appId);
if (MessageState.ALL_MESSAGES == appId) {
messageApi.deleteMessages();
Api.execute(messageApi.deleteMessages());
} else {
messageApi.deleteAppMessages(appId);
Api.execute(messageApi.deleteAppMessages(appId));
}
return true;
} catch (ApiException e) {

View File

@@ -5,7 +5,6 @@ import com.github.gotify.SSLSettings;
import com.github.gotify.Utils;
import com.github.gotify.api.Callback;
import com.github.gotify.api.CertUtils;
import com.github.gotify.client.JSON;
import com.github.gotify.client.model.Message;
import com.github.gotify.log.Log;
import java.util.concurrent.TimeUnit;
@@ -18,7 +17,6 @@ import okhttp3.WebSocketListener;
public class WebSocketConnection {
private OkHttpClient client;
private static final JSON gson = Utils.json();
private final Handler handler = new Handler();
private int errorCount = 0;
@@ -125,7 +123,7 @@ public class WebSocketConnection {
public void onMessage(WebSocket webSocket, String text) {
Log.i("WebSocket: received message " + text);
synchronized (this) {
Message message = gson.deserialize(text, Message.class);
Message message = Utils.JSON.fromJson(text, Message.class);
onMessage.onSuccess(message);
}
super.onMessage(webSocket, text);

View File

@@ -19,6 +19,7 @@ import com.github.gotify.R;
import com.github.gotify.Settings;
import com.github.gotify.Utils;
import com.github.gotify.api.ClientFactory;
import com.github.gotify.client.api.MessageApi;
import com.github.gotify.client.model.Message;
import com.github.gotify.log.Log;
import com.github.gotify.log.UncaughtExceptionHandler;
@@ -46,7 +47,8 @@ public class WebSocketService extends Service {
missingMessageUtil =
new MissedMessageUtil(
ClientFactory.clientToken(
settings.url(), settings.sslSettings(), settings.token()));
settings.url(), settings.sslSettings(), settings.token())
.createService(MessageApi.class));
Log.i("Create " + getClass().getSimpleName());
}
@@ -147,7 +149,7 @@ public class WebSocketService extends Service {
private void broadcast(Message message) {
Intent intent = new Intent();
intent.setAction(NEW_MESSAGE_BROADCAST);
intent.putExtra("message", Utils.json().serialize(message));
intent.putExtra("message", Utils.JSON.toJson(message));
sendBroadcast(intent);
}