mirror of https://github.com/tuskyapp/Tusky.git
13 changed files with 420 additions and 360 deletions
@ -0,0 +1,61 @@ |
|||||||
|
package com.keylesspalace.tusky; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.content.SharedPreferences; |
||||||
|
|
||||||
|
import com.google.firebase.iid.FirebaseInstanceId; |
||||||
|
import com.google.firebase.iid.FirebaseInstanceIdService; |
||||||
|
|
||||||
|
import okhttp3.ResponseBody; |
||||||
|
import retrofit2.Call; |
||||||
|
import retrofit2.Callback; |
||||||
|
import retrofit2.Response; |
||||||
|
import retrofit2.Retrofit; |
||||||
|
|
||||||
|
public class MyFirebaseInstanceIdService extends FirebaseInstanceIdService { |
||||||
|
|
||||||
|
private TuskyAPI tuskyAPI; |
||||||
|
|
||||||
|
protected void createTuskyAPI() { |
||||||
|
Retrofit retrofit = new Retrofit.Builder() |
||||||
|
.baseUrl(getString(R.string.tusky_api_url)) |
||||||
|
.build(); |
||||||
|
|
||||||
|
tuskyAPI = retrofit.create(TuskyAPI.class); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onTokenRefresh() { |
||||||
|
createTuskyAPI(); |
||||||
|
|
||||||
|
String refreshedToken = FirebaseInstanceId.getInstance().getToken(); |
||||||
|
SharedPreferences preferences = getSharedPreferences(getString(R.string.preferences_file_key), Context.MODE_PRIVATE); |
||||||
|
String accessToken = preferences.getString("accessToken", null); |
||||||
|
String domain = preferences.getString("domain", null); |
||||||
|
|
||||||
|
if (accessToken != null && domain != null) { |
||||||
|
tuskyAPI.unregister("https://" + domain, accessToken).enqueue(new Callback<ResponseBody>() { |
||||||
|
@Override |
||||||
|
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onFailure(Call<ResponseBody> call, Throwable t) { |
||||||
|
|
||||||
|
} |
||||||
|
}); |
||||||
|
tuskyAPI.register("https://" + domain, accessToken, refreshedToken).enqueue(new Callback<ResponseBody>() { |
||||||
|
@Override |
||||||
|
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onFailure(Call<ResponseBody> call, Throwable t) { |
||||||
|
|
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,189 @@ |
|||||||
|
package com.keylesspalace.tusky; |
||||||
|
|
||||||
|
import android.app.NotificationManager; |
||||||
|
import android.app.PendingIntent; |
||||||
|
import android.content.Context; |
||||||
|
import android.content.Intent; |
||||||
|
import android.content.SharedPreferences; |
||||||
|
import android.graphics.Bitmap; |
||||||
|
import android.graphics.drawable.Drawable; |
||||||
|
import android.os.Build; |
||||||
|
import android.preference.PreferenceManager; |
||||||
|
import android.provider.Settings; |
||||||
|
import android.support.v4.app.NotificationCompat; |
||||||
|
import android.support.v4.app.TaskStackBuilder; |
||||||
|
import android.text.Spanned; |
||||||
|
|
||||||
|
import com.google.firebase.messaging.FirebaseMessagingService; |
||||||
|
import com.google.firebase.messaging.RemoteMessage; |
||||||
|
import com.google.gson.Gson; |
||||||
|
import com.google.gson.GsonBuilder; |
||||||
|
import com.keylesspalace.tusky.entity.Notification; |
||||||
|
import com.squareup.picasso.Picasso; |
||||||
|
import com.squareup.picasso.Target; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
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 MyFirebaseMessagingService extends FirebaseMessagingService { |
||||||
|
private MastodonAPI mastodonAPI; |
||||||
|
private static final String TAG = "MyFirebaseMessagingService"; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onMessageReceived(RemoteMessage remoteMessage) { |
||||||
|
Log.d(TAG, remoteMessage.getFrom()); |
||||||
|
Log.d(TAG, remoteMessage.toString()); |
||||||
|
|
||||||
|
String notificationId = remoteMessage.getData().get("notification_id"); |
||||||
|
|
||||||
|
if (notificationId == null) { |
||||||
|
Log.e(TAG, "No notification ID in payload!!"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
Log.d(TAG, notificationId); |
||||||
|
|
||||||
|
createMastodonAPI(); |
||||||
|
|
||||||
|
mastodonAPI.notification(notificationId).enqueue(new Callback<Notification>() { |
||||||
|
@Override |
||||||
|
public void onResponse(Call<Notification> call, Response<Notification> response) { |
||||||
|
buildNotification(response.body()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onFailure(Call<Notification> call, Throwable t) { |
||||||
|
|
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
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 = new OkHttpClient.Builder() |
||||||
|
.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()) |
||||||
|
.create(); |
||||||
|
|
||||||
|
Retrofit retrofit = new Retrofit.Builder() |
||||||
|
.baseUrl("https://" + domain) |
||||||
|
.client(okHttpClient) |
||||||
|
.addConverterFactory(GsonConverterFactory.create(gson)) |
||||||
|
.build(); |
||||||
|
|
||||||
|
mastodonAPI = retrofit.create(MastodonAPI.class); |
||||||
|
} |
||||||
|
|
||||||
|
private String truncateWithEllipses(String string, int limit) { |
||||||
|
if (string.length() < limit) { |
||||||
|
return string; |
||||||
|
} else { |
||||||
|
return string.substring(0, limit - 3) + "..."; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void buildNotification(Notification body) { |
||||||
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); |
||||||
|
|
||||||
|
Intent resultIntent = new Intent(this, MainActivity.class); |
||||||
|
resultIntent.putExtra("tab_position", 1); |
||||||
|
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); |
||||||
|
stackBuilder.addParentStack(MainActivity.class); |
||||||
|
stackBuilder.addNextIntent(resultIntent); |
||||||
|
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); |
||||||
|
|
||||||
|
final NotificationCompat.Builder builder = new NotificationCompat.Builder(this) |
||||||
|
.setSmallIcon(R.drawable.ic_notify) |
||||||
|
.setAutoCancel(true) |
||||||
|
.setContentIntent(resultPendingIntent); |
||||||
|
|
||||||
|
final Integer mId = (int)(System.currentTimeMillis() / 1000); |
||||||
|
|
||||||
|
Target mTarget = new Target() { |
||||||
|
@Override |
||||||
|
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { |
||||||
|
builder.setLargeIcon(bitmap); |
||||||
|
((NotificationManager) (getSystemService(NOTIFICATION_SERVICE))).notify(mId, builder.build()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onBitmapFailed(Drawable errorDrawable) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onPrepareLoad(Drawable placeHolderDrawable) { |
||||||
|
|
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Picasso.with(this) |
||||||
|
.load(body.account.avatar) |
||||||
|
.placeholder(R.drawable.avatar_default) |
||||||
|
.into(mTarget); |
||||||
|
|
||||||
|
if (preferences.getBoolean("notificationAlertSound", true)) { |
||||||
|
builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI); |
||||||
|
} |
||||||
|
|
||||||
|
if (preferences.getBoolean("notificationStyleVibrate", false)) { |
||||||
|
builder.setVibrate(new long[] { 500, 500 }); |
||||||
|
} |
||||||
|
|
||||||
|
if (preferences.getBoolean("notificationStyleLight", false)) { |
||||||
|
builder.setLights(0xFF00FF8F, 300, 1000); |
||||||
|
} |
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { |
||||||
|
builder.setVisibility(android.app.Notification.VISIBILITY_PRIVATE); |
||||||
|
builder.setCategory(android.app.Notification.CATEGORY_SOCIAL); |
||||||
|
} |
||||||
|
|
||||||
|
switch (body.type) { |
||||||
|
case MENTION: |
||||||
|
builder.setContentTitle(String.format(getString(R.string.notification_mention_format), body.account.getDisplayName())) |
||||||
|
.setContentText(truncateWithEllipses(body.status.content.toString(), 40)); |
||||||
|
break; |
||||||
|
case FOLLOW: |
||||||
|
builder.setContentTitle(String.format(getString(R.string.notification_follow_format), body.account.getDisplayName())) |
||||||
|
.setContentText(truncateWithEllipses(body.account.username, 40)); |
||||||
|
break; |
||||||
|
case FAVOURITE: |
||||||
|
builder.setContentTitle(String.format(getString(R.string.notification_favourite_format), body.account.getDisplayName())) |
||||||
|
.setContentText(truncateWithEllipses(body.status.content.toString(), 40)); |
||||||
|
break; |
||||||
|
case REBLOG: |
||||||
|
builder.setContentTitle(String.format(getString(R.string.notification_reblog_format), body.account.getDisplayName())) |
||||||
|
.setContentText(truncateWithEllipses(body.status.content.toString(), 40)); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
((NotificationManager) (getSystemService(NOTIFICATION_SERVICE))).notify(mId, builder.build()); |
||||||
|
} |
||||||
|
} |
||||||
@ -1,233 +0,0 @@ |
|||||||
/* Copyright 2017 Andrew Dawson |
|
||||||
* |
|
||||||
* This file is part of Tusky. |
|
||||||
* |
|
||||||
* Tusky 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; |
|
||||||
|
|
||||||
import android.app.*; |
|
||||||
import android.content.Context; |
|
||||||
import android.content.Intent; |
|
||||||
import android.content.SharedPreferences; |
|
||||||
import android.graphics.Bitmap; |
|
||||||
import android.graphics.drawable.Drawable; |
|
||||||
import android.os.Build; |
|
||||||
import android.preference.PreferenceManager; |
|
||||||
import android.provider.Settings; |
|
||||||
import android.support.annotation.Nullable; |
|
||||||
import android.support.v4.app.NotificationCompat; |
|
||||||
import android.support.v4.app.TaskStackBuilder; |
|
||||||
import android.text.Spanned; |
|
||||||
|
|
||||||
import com.google.gson.Gson; |
|
||||||
import com.google.gson.GsonBuilder; |
|
||||||
import com.keylesspalace.tusky.entity.*; |
|
||||||
import com.keylesspalace.tusky.entity.Notification; |
|
||||||
import com.squareup.picasso.Picasso; |
|
||||||
import com.squareup.picasso.Target; |
|
||||||
|
|
||||||
import java.io.IOException; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
import okhttp3.Interceptor; |
|
||||||
import okhttp3.OkHttpClient; |
|
||||||
import okhttp3.Request; |
|
||||||
import retrofit2.Call; |
|
||||||
import retrofit2.Callback; |
|
||||||
import retrofit2.Retrofit; |
|
||||||
import retrofit2.converter.gson.GsonConverterFactory; |
|
||||||
|
|
||||||
public class PullNotificationService extends IntentService { |
|
||||||
static final int NOTIFY_ID = 6; // This is an arbitrary number.
|
|
||||||
private static final String TAG = "PullNotifications"; // logging tag
|
|
||||||
|
|
||||||
public PullNotificationService() { |
|
||||||
super("Tusky Pull Notification Service"); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
protected void onHandleIntent(Intent intent) { |
|
||||||
SharedPreferences preferences = getSharedPreferences( |
|
||||||
getString(R.string.preferences_file_key), Context.MODE_PRIVATE); |
|
||||||
String domain = preferences.getString("domain", null); |
|
||||||
String accessToken = preferences.getString("accessToken", null); |
|
||||||
String lastUpdateId = preferences.getString("lastUpdateId", null); |
|
||||||
checkNotifications(domain, accessToken, lastUpdateId); |
|
||||||
} |
|
||||||
|
|
||||||
private void checkNotifications(final String domain, final String accessToken, |
|
||||||
final String lastUpdateId) { |
|
||||||
OkHttpClient okHttpClient = new OkHttpClient.Builder() |
|
||||||
.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()) |
|
||||||
.create(); |
|
||||||
|
|
||||||
Retrofit retrofit = new Retrofit.Builder() |
|
||||||
.baseUrl("https://" + domain) |
|
||||||
.client(okHttpClient) |
|
||||||
.addConverterFactory(GsonConverterFactory.create(gson)) |
|
||||||
.build(); |
|
||||||
|
|
||||||
MastodonAPI api = retrofit.create(MastodonAPI.class); |
|
||||||
|
|
||||||
api.notifications(null, lastUpdateId, null).enqueue(new Callback<List<Notification>>() { |
|
||||||
@Override |
|
||||||
public void onResponse(Call<List<Notification>> call, retrofit2.Response<List<Notification>> response) { |
|
||||||
onCheckNotificationsSuccess(response.body(), lastUpdateId); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onFailure(Call<List<Notification>> call, Throwable t) { |
|
||||||
onCheckNotificationsFailure((Exception) t); |
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
private void onCheckNotificationsSuccess(List<com.keylesspalace.tusky.entity.Notification> notifications, String lastUpdateId) { |
|
||||||
List<MentionResult> mentions = new ArrayList<>(); |
|
||||||
|
|
||||||
for (com.keylesspalace.tusky.entity.Notification notification : notifications) { |
|
||||||
if (notification.type == com.keylesspalace.tusky.entity.Notification.Type.MENTION) { |
|
||||||
Status status = notification.status; |
|
||||||
|
|
||||||
if (status != null) { |
|
||||||
MentionResult mention = new MentionResult(); |
|
||||||
mention.content = status.content.toString(); |
|
||||||
mention.displayName = notification.account.getDisplayName(); |
|
||||||
mention.avatarUrl = status.account.avatar; |
|
||||||
mentions.add(mention); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (notifications.size() > 0) { |
|
||||||
SharedPreferences preferences = getSharedPreferences( |
|
||||||
getString(R.string.preferences_file_key), Context.MODE_PRIVATE); |
|
||||||
SharedPreferences.Editor editor = preferences.edit(); |
|
||||||
editor.putString("lastUpdateId", notifications.get(0).id); |
|
||||||
editor.apply(); |
|
||||||
} |
|
||||||
|
|
||||||
if (mentions.size() > 0) { |
|
||||||
loadAvatar(mentions, mentions.get(0).avatarUrl); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private void onCheckNotificationsFailure(Exception exception) { |
|
||||||
Log.e(TAG, "Failed to check notifications. " + exception.getMessage()); |
|
||||||
} |
|
||||||
|
|
||||||
private static class MentionResult { |
|
||||||
String displayName; |
|
||||||
String content; |
|
||||||
String avatarUrl; |
|
||||||
} |
|
||||||
|
|
||||||
private String truncateWithEllipses(String string, int limit) { |
|
||||||
if (string.length() < limit) { |
|
||||||
return string; |
|
||||||
} else { |
|
||||||
return string.substring(0, limit - 3) + "..."; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private void loadAvatar(final List<MentionResult> mentions, String url) { |
|
||||||
if (url != null) { |
|
||||||
Target target = new Target() { |
|
||||||
@Override |
|
||||||
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { |
|
||||||
updateNotification(mentions, bitmap); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onBitmapFailed(Drawable errorDrawable) { |
|
||||||
updateNotification(mentions, null); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onPrepareLoad(Drawable placeHolderDrawable) {} |
|
||||||
}; |
|
||||||
Picasso.with(this) |
|
||||||
.load(url) |
|
||||||
.into(target); |
|
||||||
} else { |
|
||||||
updateNotification(mentions, null); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private void updateNotification(List<MentionResult> mentions, @Nullable Bitmap icon) { |
|
||||||
final int NOTIFICATION_CONTENT_LIMIT = 40; |
|
||||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); |
|
||||||
String title; |
|
||||||
if (mentions.size() > 1) { |
|
||||||
title = String.format( |
|
||||||
getString(R.string.notification_service_several_mentions), |
|
||||||
mentions.size()); |
|
||||||
} else { |
|
||||||
title = String.format( |
|
||||||
getString(R.string.notification_service_one_mention), |
|
||||||
mentions.get(0).displayName); |
|
||||||
} |
|
||||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this) |
|
||||||
.setSmallIcon(R.drawable.ic_notify) |
|
||||||
.setContentTitle(title); |
|
||||||
if (icon != null) { |
|
||||||
builder.setLargeIcon(icon); |
|
||||||
} |
|
||||||
if (preferences.getBoolean("notificationAlertSound", true)) { |
|
||||||
builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI); |
|
||||||
} |
|
||||||
if (preferences.getBoolean("notificationStyleVibrate", false)) { |
|
||||||
builder.setVibrate(new long[] { 500, 500 }); |
|
||||||
} |
|
||||||
if (preferences.getBoolean("notificationStyleLight", false)) { |
|
||||||
builder.setLights(0xFF00FF8F, 300, 1000); |
|
||||||
} |
|
||||||
for (int i = 0; i < mentions.size(); i++) { |
|
||||||
MentionResult mention = mentions.get(i); |
|
||||||
String text = truncateWithEllipses(mention.content, NOTIFICATION_CONTENT_LIMIT); |
|
||||||
builder.setContentText(text) |
|
||||||
.setNumber(i); |
|
||||||
} |
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { |
|
||||||
builder.setVisibility(android.app.Notification.VISIBILITY_PRIVATE); |
|
||||||
builder.setCategory(android.app.Notification.CATEGORY_SOCIAL); |
|
||||||
} |
|
||||||
Intent resultIntent = new Intent(this, SplashActivity.class); |
|
||||||
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); |
|
||||||
stackBuilder.addParentStack(SplashActivity.class); |
|
||||||
stackBuilder.addNextIntent(resultIntent); |
|
||||||
PendingIntent resultPendingIntent = |
|
||||||
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); |
|
||||||
builder.setContentIntent(resultPendingIntent); |
|
||||||
NotificationManager notificationManager = |
|
||||||
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); |
|
||||||
notificationManager.notify(NOTIFY_ID, builder.build()); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -0,0 +1,16 @@ |
|||||||
|
package com.keylesspalace.tusky; |
||||||
|
|
||||||
|
import okhttp3.ResponseBody; |
||||||
|
import retrofit2.Call; |
||||||
|
import retrofit2.http.Field; |
||||||
|
import retrofit2.http.FormUrlEncoded; |
||||||
|
import retrofit2.http.POST; |
||||||
|
|
||||||
|
public interface TuskyAPI { |
||||||
|
@FormUrlEncoded |
||||||
|
@POST("/register") |
||||||
|
Call<ResponseBody> register(@Field("instance_url") String instanceUrl, @Field("access_token") String accessToken, @Field("device_token") String deviceToken); |
||||||
|
@FormUrlEncoded |
||||||
|
@POST("/unregister") |
||||||
|
Call<ResponseBody> unregister(@Field("instance_url") String instanceUrl, @Field("access_token") String accessToken); |
||||||
|
} |
||||||
Loading…
Reference in new issue