Integrates with wryk/tusky-api, but only partially working.

Registers to the web-service fine but loses connection when subscribing with the broker.
This commit is contained in:
Vavassor 2017-05-20 02:39:29 -04:00
parent e282f13fdc
commit c90c909ca6
5 changed files with 75 additions and 291 deletions

View File

@ -31,6 +31,7 @@ import android.view.Menu;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.keylesspalace.tusky.entity.Session;
import com.keylesspalace.tusky.json.SpannedTypeAdapter;
import com.keylesspalace.tusky.json.StringWithEmoji;
import com.keylesspalace.tusky.json.StringWithEmojiTypeAdapter;
@ -162,8 +163,9 @@ public class BaseActivity extends AppCompatActivity {
protected void createTuskyApi() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://" + getString(R.string.tusky_api_domain))
.baseUrl("http://" + getString(R.string.tusky_api_domain) + ":8080")
.client(OkHttpUtils.getCompatibleClient())
.addConverterFactory(GsonConverterFactory.create())
.build();
tuskyApi = retrofit.create(TuskyApi.class);
@ -172,7 +174,7 @@ public class BaseActivity extends AppCompatActivity {
protected void createPushNotificationClient() {
// TODO: Switch to ssl:// when TLS support is added.
pushNotificationClient = new PushNotificationClient(getApplicationContext(),
"tcp://" + getString(R.string.tusky_api_domain));
"tcp://" + getString(R.string.tusky_api_domain) + ":8000");
}
protected void redirectIfNotLoggedIn() {
@ -212,6 +214,7 @@ public class BaseActivity extends AppCompatActivity {
retrofit2.Response<ResponseBody> response) {
if (response.isSuccessful()) {
pushNotificationClient.subscribeToTopic(getPushNotificationTopic());
pushNotificationClient.connect();
} else {
onEnablePushNotificationsFailure();
}
@ -222,7 +225,9 @@ public class BaseActivity extends AppCompatActivity {
onEnablePushNotificationsFailure();
}
};
tuskyApi.register(getBaseUrl(), getAccessToken(), pushNotificationClient.getDeviceToken())
String deviceToken = pushNotificationClient.getDeviceToken();
Session session = new Session(getDomain(), getAccessToken(), deviceToken);
tuskyApi.register(session)
.enqueue(callback);
}
@ -247,7 +252,9 @@ public class BaseActivity extends AppCompatActivity {
onDisablePushNotificationsFailure();
}
};
tuskyApi.unregister(getBaseUrl(), getAccessToken(), pushNotificationClient.getDeviceToken())
String deviceToken = pushNotificationClient.getDeviceToken();
Session session = new Session(getDomain(), getAccessToken(), deviceToken);
tuskyApi.unregister(session)
.enqueue(callback);
}
@ -256,7 +263,11 @@ public class BaseActivity extends AppCompatActivity {
}
private String getPushNotificationTopic() {
return String.format("%s/%s/%s", getBaseUrl(), getAccessToken(),
pushNotificationClient.getDeviceToken());
return String.format("%s/%s/#", getDomain(), getAccessToken());
}
private String getDomain() {
return getPrivatePreferences()
.getString("domain", null);
}
}

View File

@ -0,0 +1,28 @@
/* Copyright 2017 Andrew Dawson
*
* This file is a part of Tusky.
*
* 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.
*
* Tusky 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 Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.entity;
public class Session {
public String instanceUrl;
public String accessToken;
public String deviceToken;
public Session(String instanceUrl, String accessToken, String deviceToken) {
this.instanceUrl = instanceUrl;
this.accessToken = accessToken;
this.deviceToken = deviceToken;
}
}

View File

@ -15,16 +15,16 @@
package com.keylesspalace.tusky.network;
import com.keylesspalace.tusky.entity.Session;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.Body;
import retrofit2.http.POST;
public interface TuskyApi {
@FormUrlEncoded
@POST("/register")
Call<ResponseBody> register(String instanceUrl, String accessToken, String deviceToken);
@FormUrlEncoded
Call<ResponseBody> register(@Body Session session);
@POST("/unregister")
Call<ResponseBody> unregister(String instanceUrl, String accessToken, String deviceToken);
Call<ResponseBody> unregister(@Body Session session);
}

View File

@ -1,257 +0,0 @@
package com.keylesspalace.tusky.service;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Binder;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.text.Spanned;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.entity.Notification;
import com.keylesspalace.tusky.json.SpannedTypeAdapter;
import com.keylesspalace.tusky.json.StringWithEmoji;
import com.keylesspalace.tusky.json.StringWithEmojiTypeAdapter;
import com.keylesspalace.tusky.network.MastodonAPI;
import com.keylesspalace.tusky.util.Log;
import com.keylesspalace.tusky.util.NotificationMaker;
import com.keylesspalace.tusky.util.OkHttpUtils;
import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.DisconnectedBufferOptions;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import java.io.IOException;
import java.util.Locale;
import java.util.UUID;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class PushNotificationService extends Service {
private class LocalBinder extends Binder {
PushNotificationService getService() {
return PushNotificationService.this;
}
}
private static final String TAG = "PushNotificationService";
private static final String CLIENT_NAME = "TuskyMastodonClient";
private static final String TOPIC = "tusky/notification";
private static final int NOTIFY_ID = 666;
private final IBinder binder = new LocalBinder();
private MqttAndroidClient mqttAndroidClient;
private MastodonAPI mastodonApi;
@Override
public void onCreate() {
super.onCreate();
// Create the MQTT client.
String clientId = String.format(Locale.getDefault(), "%s/%s/%s", CLIENT_NAME,
System.currentTimeMillis(), UUID.randomUUID().toString());
String serverUri = getString(R.string.tusky_api_url);
mqttAndroidClient = new MqttAndroidClient(this, serverUri, clientId);
mqttAndroidClient.setCallback(new MqttCallbackExtended() {
@Override
public void connectComplete(boolean reconnect, String serverURI) {
if (reconnect) {
subscribeToTopic();
}
}
@Override
public void connectionLost(Throwable cause) {
onConnectionLost();
}
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
onMessageReceived(new String(message.getPayload()));
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
// This client is read-only, so this is unused.
}
});
// Open the MQTT connection.
MqttConnectOptions options = new MqttConnectOptions();
options.setAutomaticReconnect(true);
options.setCleanSession(false);
try {
mqttAndroidClient.connect(options, null, new IMqttActionListener() {
@Override
public void onSuccess(IMqttToken asyncActionToken) {
DisconnectedBufferOptions options = new DisconnectedBufferOptions();
options.setBufferEnabled(true);
options.setBufferSize(100);
options.setPersistBuffer(false);
options.setDeleteOldestMessages(false);
mqttAndroidClient.setBufferOpts(options);
onConnectionSuccess();
subscribeToTopic();
}
@Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
onConnectionFailure();
}
});
} catch (MqttException e) {
Log.e(TAG, "An exception occurred while connecting. " + e.getMessage());
onConnectionFailure();
}
}
@Override
public void onDestroy() {
super.onDestroy();
disconnect();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
/** Subscribe to the push notification topic. */
public void subscribeToTopic() {
try {
mqttAndroidClient.subscribe(TOPIC, 0, null, new IMqttActionListener() {
@Override
public void onSuccess(IMqttToken asyncActionToken) {
onConnectionSuccess();
}
@Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
onConnectionFailure();
}
});
} catch (MqttException e) {
Log.e(TAG, "An exception occurred while subscribing." + e.getMessage());
onConnectionFailure();
}
}
/** Unsubscribe from the push notification topic. */
public void unsubscribeToTopic() {
try {
mqttAndroidClient.unsubscribe(TOPIC);
} catch (MqttException e) {
Log.e(TAG, "An exception occurred while unsubscribing." + e.getMessage());
onConnectionFailure();
}
}
private void onConnectionSuccess() {
}
private void onConnectionFailure() {
}
private void onConnectionLost() {
}
private void onMessageReceived(String message) {
String notificationId = message; // TODO: finalize the form the messages will be received
Log.d(TAG, "Notification received: " + notificationId);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(
getApplicationContext());
boolean enabled = preferences.getBoolean("notificationsEnabled", true);
if (!enabled) {
return;
}
createMastodonAPI();
mastodonApi.notification(notificationId).enqueue(new Callback<Notification>() {
@Override
public void onResponse(Call<Notification> call, Response<Notification> response) {
if (response.isSuccessful()) {
NotificationMaker.make(PushNotificationService.this, NOTIFY_ID,
response.body());
}
}
@Override
public void onFailure(Call<Notification> call, Throwable t) {}
});
}
/** Disconnect from the MQTT broker. */
public void disconnect() {
try {
mqttAndroidClient.disconnect();
} catch (MqttException ex) {
Log.e(TAG, "An exception occurred while disconnecting.");
onDisconnectFailed();
}
}
private void onDisconnectFailed() {
}
private void createMastodonAPI() {
SharedPreferences preferences = getSharedPreferences(
getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
final String domain = preferences.getString("domain", null);
final String accessToken = preferences.getString("accessToken", null);
OkHttpClient okHttpClient = OkHttpUtils.getCompatibleClientBuilder()
.addInterceptor(new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Request.Builder builder = originalRequest.newBuilder()
.header("Authorization", String.format("Bearer %s", accessToken));
Request newRequest = builder.build();
return chain.proceed(newRequest);
}
})
.build();
Gson gson = new GsonBuilder()
.registerTypeAdapter(Spanned.class, new SpannedTypeAdapter())
.registerTypeAdapter(StringWithEmoji.class, new StringWithEmojiTypeAdapter())
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://" + domain)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
mastodonApi = retrofit.create(MastodonAPI.class);
}
}

View File

@ -105,8 +105,21 @@ public class PushNotificationClient {
// This client is read-only, so this is unused.
}
});
}
// Open the MQTT connection.
private void flushQueuedActions() {
while (!queuedActions.isEmpty()) {
QueuedAction action = queuedActions.pop();
switch (action.type) {
case SUBSCRIBE: subscribeToTopic(action.topic); break;
case UNSUBSCRIBE: unsubscribeToTopic(action.topic); break;
case DISCONNECT: disconnect(); break;
}
}
}
/** Connect to the MQTT broker. */
public void connect() {
MqttConnectOptions options = new MqttConnectOptions();
options.setAutomaticReconnect(true);
options.setCleanSession(false);
@ -140,20 +153,21 @@ public class PushNotificationClient {
}
});
} catch (MqttException e) {
Log.e(TAG, "An exception occurred while connecting. " + e.getMessage());
Log.e(TAG, "An exception occurred while connecpting. " + e.getMessage());
onConnectionFailure();
}
}
private void flushQueuedActions() {
while (!queuedActions.isEmpty()) {
QueuedAction action = queuedActions.pop();
switch (action.type) {
case SUBSCRIBE: subscribeToTopic(action.topic); break;
case UNSUBSCRIBE: unsubscribeToTopic(action.topic); break;
case DISCONNECT: disconnect(); break;
}
}
private void onConnectionSuccess() {
Log.v(TAG, "The connection succeeded.");
}
private void onConnectionFailure() {
Log.v(TAG, "The connection failed.");
}
private void onConnectionLost() {
Log.v(TAG, "The connection was lost.");
}
/** Disconnect from the MQTT broker. */
@ -215,18 +229,6 @@ public class PushNotificationClient {
}
}
private void onConnectionSuccess() {
Log.v(TAG, "The connection succeeded.");
}
private void onConnectionFailure() {
Log.v(TAG, "The connection failed.");
}
private void onConnectionLost() {
Log.v(TAG, "The connection was lost.");
}
private void onMessageReceived(final Context context, String message) {
String notificationId = message; // TODO: finalize the form the messages will be received