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:
parent
e282f13fdc
commit
c90c909ca6
5 changed files with 75 additions and 291 deletions
|
@ -31,6 +31,7 @@ import android.view.Menu;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.keylesspalace.tusky.entity.Session;
|
||||||
import com.keylesspalace.tusky.json.SpannedTypeAdapter;
|
import com.keylesspalace.tusky.json.SpannedTypeAdapter;
|
||||||
import com.keylesspalace.tusky.json.StringWithEmoji;
|
import com.keylesspalace.tusky.json.StringWithEmoji;
|
||||||
import com.keylesspalace.tusky.json.StringWithEmojiTypeAdapter;
|
import com.keylesspalace.tusky.json.StringWithEmojiTypeAdapter;
|
||||||
|
@ -162,8 +163,9 @@ public class BaseActivity extends AppCompatActivity {
|
||||||
|
|
||||||
protected void createTuskyApi() {
|
protected void createTuskyApi() {
|
||||||
Retrofit retrofit = new Retrofit.Builder()
|
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())
|
.client(OkHttpUtils.getCompatibleClient())
|
||||||
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
tuskyApi = retrofit.create(TuskyApi.class);
|
tuskyApi = retrofit.create(TuskyApi.class);
|
||||||
|
@ -172,7 +174,7 @@ public class BaseActivity extends AppCompatActivity {
|
||||||
protected void createPushNotificationClient() {
|
protected void createPushNotificationClient() {
|
||||||
// TODO: Switch to ssl:// when TLS support is added.
|
// TODO: Switch to ssl:// when TLS support is added.
|
||||||
pushNotificationClient = new PushNotificationClient(getApplicationContext(),
|
pushNotificationClient = new PushNotificationClient(getApplicationContext(),
|
||||||
"tcp://" + getString(R.string.tusky_api_domain));
|
"tcp://" + getString(R.string.tusky_api_domain) + ":8000");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void redirectIfNotLoggedIn() {
|
protected void redirectIfNotLoggedIn() {
|
||||||
|
@ -212,6 +214,7 @@ public class BaseActivity extends AppCompatActivity {
|
||||||
retrofit2.Response<ResponseBody> response) {
|
retrofit2.Response<ResponseBody> response) {
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
pushNotificationClient.subscribeToTopic(getPushNotificationTopic());
|
pushNotificationClient.subscribeToTopic(getPushNotificationTopic());
|
||||||
|
pushNotificationClient.connect();
|
||||||
} else {
|
} else {
|
||||||
onEnablePushNotificationsFailure();
|
onEnablePushNotificationsFailure();
|
||||||
}
|
}
|
||||||
|
@ -222,7 +225,9 @@ public class BaseActivity extends AppCompatActivity {
|
||||||
onEnablePushNotificationsFailure();
|
onEnablePushNotificationsFailure();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
tuskyApi.register(getBaseUrl(), getAccessToken(), pushNotificationClient.getDeviceToken())
|
String deviceToken = pushNotificationClient.getDeviceToken();
|
||||||
|
Session session = new Session(getDomain(), getAccessToken(), deviceToken);
|
||||||
|
tuskyApi.register(session)
|
||||||
.enqueue(callback);
|
.enqueue(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +252,9 @@ public class BaseActivity extends AppCompatActivity {
|
||||||
onDisablePushNotificationsFailure();
|
onDisablePushNotificationsFailure();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
tuskyApi.unregister(getBaseUrl(), getAccessToken(), pushNotificationClient.getDeviceToken())
|
String deviceToken = pushNotificationClient.getDeviceToken();
|
||||||
|
Session session = new Session(getDomain(), getAccessToken(), deviceToken);
|
||||||
|
tuskyApi.unregister(session)
|
||||||
.enqueue(callback);
|
.enqueue(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,7 +263,11 @@ public class BaseActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getPushNotificationTopic() {
|
private String getPushNotificationTopic() {
|
||||||
return String.format("%s/%s/%s", getBaseUrl(), getAccessToken(),
|
return String.format("%s/%s/#", getDomain(), getAccessToken());
|
||||||
pushNotificationClient.getDeviceToken());
|
}
|
||||||
|
|
||||||
|
private String getDomain() {
|
||||||
|
return getPrivatePreferences()
|
||||||
|
.getString("domain", null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,16 +15,16 @@
|
||||||
|
|
||||||
package com.keylesspalace.tusky.network;
|
package com.keylesspalace.tusky.network;
|
||||||
|
|
||||||
|
import com.keylesspalace.tusky.entity.Session;
|
||||||
|
|
||||||
import okhttp3.ResponseBody;
|
import okhttp3.ResponseBody;
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.http.FormUrlEncoded;
|
import retrofit2.http.Body;
|
||||||
import retrofit2.http.POST;
|
import retrofit2.http.POST;
|
||||||
|
|
||||||
public interface TuskyApi {
|
public interface TuskyApi {
|
||||||
@FormUrlEncoded
|
|
||||||
@POST("/register")
|
@POST("/register")
|
||||||
Call<ResponseBody> register(String instanceUrl, String accessToken, String deviceToken);
|
Call<ResponseBody> register(@Body Session session);
|
||||||
@FormUrlEncoded
|
|
||||||
@POST("/unregister")
|
@POST("/unregister")
|
||||||
Call<ResponseBody> unregister(String instanceUrl, String accessToken, String deviceToken);
|
Call<ResponseBody> unregister(@Body Session session);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -105,8 +105,21 @@ public class PushNotificationClient {
|
||||||
// This client is read-only, so this is unused.
|
// 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();
|
MqttConnectOptions options = new MqttConnectOptions();
|
||||||
options.setAutomaticReconnect(true);
|
options.setAutomaticReconnect(true);
|
||||||
options.setCleanSession(false);
|
options.setCleanSession(false);
|
||||||
|
@ -140,20 +153,21 @@ public class PushNotificationClient {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (MqttException e) {
|
} catch (MqttException e) {
|
||||||
Log.e(TAG, "An exception occurred while connecting. " + e.getMessage());
|
Log.e(TAG, "An exception occurred while connecpting. " + e.getMessage());
|
||||||
onConnectionFailure();
|
onConnectionFailure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void flushQueuedActions() {
|
private void onConnectionSuccess() {
|
||||||
while (!queuedActions.isEmpty()) {
|
Log.v(TAG, "The connection succeeded.");
|
||||||
QueuedAction action = queuedActions.pop();
|
}
|
||||||
switch (action.type) {
|
|
||||||
case SUBSCRIBE: subscribeToTopic(action.topic); break;
|
private void onConnectionFailure() {
|
||||||
case UNSUBSCRIBE: unsubscribeToTopic(action.topic); break;
|
Log.v(TAG, "The connection failed.");
|
||||||
case DISCONNECT: disconnect(); break;
|
}
|
||||||
}
|
|
||||||
}
|
private void onConnectionLost() {
|
||||||
|
Log.v(TAG, "The connection was lost.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Disconnect from the MQTT broker. */
|
/** 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) {
|
private void onMessageReceived(final Context context, String message) {
|
||||||
String notificationId = message; // TODO: finalize the form the messages will be received
|
String notificationId = message; // TODO: finalize the form the messages will be received
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue