Moves Firebase notifications to a "google" build flavor and adds an "fdroid" flavor that uses polling for notifications.
Also, adds a few missing license notices.
This commit is contained in:
parent
cc8d8f716a
commit
5f6fab2b70
20 changed files with 653 additions and 373 deletions
|
@ -12,6 +12,14 @@ android {
|
|||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary true
|
||||
}
|
||||
productFlavors {
|
||||
google {
|
||||
buildConfigField "boolean", "USES_PUSH_NOTIFICATIONS", "true"
|
||||
}
|
||||
fdroid {
|
||||
buildConfigField "boolean", "USES_PUSH_NOTIFICATIONS", "false"
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
|
@ -51,8 +59,8 @@ dependencies {
|
|||
compile 'com.github.arimorty:floatingsearchview:2.0.3'
|
||||
compile 'com.theartofdev.edmodo:android-image-cropper:2.4.0'
|
||||
compile 'com.jakewharton:butterknife:8.4.0'
|
||||
compile 'com.google.firebase:firebase-messaging:10.0.1'
|
||||
compile 'com.google.firebase:firebase-crash:10.0.1'
|
||||
googleCompile 'com.google.firebase:firebase-messaging:10.0.1'
|
||||
googleCompile 'com.google.firebase:firebase-crash:10.0.1'
|
||||
testCompile 'junit:junit:4.12'
|
||||
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
|
||||
}
|
||||
|
|
12
app/src/fdroid/AndroidManifest.xml
Normal file
12
app/src/fdroid/AndroidManifest.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.keylesspalace.tusky">
|
||||
|
||||
<application>
|
||||
<service
|
||||
android:name=".MessagingService"
|
||||
android:enabled="true"
|
||||
android:exported="true" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,114 @@
|
|||
/* 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;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.Spanned;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import com.keylesspalace.tusky.entity.Notification;
|
||||
import java.util.List;
|
||||
|
||||
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 MessagingService extends IntentService {
|
||||
public static final int NOTIFY_ID = 6; // This is an arbitrary number.
|
||||
|
||||
private MastodonAPI mastodonAPI;
|
||||
|
||||
public MessagingService() {
|
||||
super("Tusky Pull Notification Service");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(
|
||||
getApplicationContext());
|
||||
boolean enabled = preferences.getBoolean("notificationsEnabled", true);
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
createMastodonApi();
|
||||
|
||||
mastodonAPI.notifications(null, null, null).enqueue(new Callback<List<Notification>>() {
|
||||
@Override
|
||||
public void onResponse(Call<List<Notification>> call,
|
||||
Response<List<Notification>> response) {
|
||||
if (response.isSuccessful()) {
|
||||
List<Notification> notificationList = response.body();
|
||||
for (Notification notification : notificationList) {
|
||||
NotificationMaker.make(MessagingService.this, NOTIFY_ID, notification);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<List<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 = 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);
|
||||
}
|
||||
}
|
18
app/src/google/AndroidManifest.xml
Normal file
18
app/src/google/AndroidManifest.xml
Normal file
|
@ -0,0 +1,18 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.keylesspalace.tusky">
|
||||
<application>
|
||||
<meta-data android:name="firebase_analytics_collection_enabled" android:value="false" />
|
||||
|
||||
<service android:name=".MyFirebaseInstanceIdService" android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service android:name=".MessagingService" android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
</application>
|
||||
</manifest>
|
|
@ -0,0 +1,121 @@
|
|||
/* 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>.
|
||||
*
|
||||
* If you modify this Program, or any covered work, by linking or combining it with Firebase Cloud
|
||||
* Messaging and Firebase Crash Reporting (or a modified version of those libraries), containing
|
||||
* parts covered by the Google APIs Terms of Service, the licensors of this Program grant you
|
||||
* additional permission to convey the resulting work. */
|
||||
|
||||
package com.keylesspalace.tusky;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
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 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 MessagingService extends FirebaseMessagingService {
|
||||
private MastodonAPI mastodonAPI;
|
||||
private static final String TAG = "MessagingService";
|
||||
public static final int NOTIFY_ID = 666;
|
||||
|
||||
@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);
|
||||
|
||||
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(MessagingService.this, NOTIFY_ID, 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 = 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);
|
||||
}
|
||||
}
|
|
@ -33,7 +33,7 @@ import retrofit2.Response;
|
|||
import retrofit2.Retrofit;
|
||||
|
||||
public class MyFirebaseInstanceIdService extends FirebaseInstanceIdService {
|
||||
private static final String TAG = "MyFirebaseInstanceIdService";
|
||||
private static final String TAG = "com.keylesspalace.tusky.MyFirebaseInstanceIdService";
|
||||
|
||||
private TuskyAPI tuskyAPI;
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
android:name=".TuskyApplication">
|
||||
<meta-data android:name="firebase_analytics_collection_enabled" android:value="false" />
|
||||
|
||||
<activity android:name=".SplashActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
@ -72,18 +72,6 @@
|
|||
android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
|
||||
android:theme="@style/Base.Theme.AppCompat" />
|
||||
|
||||
<service android:name=".MyFirebaseInstanceIdService" android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service android:name=".MyFirebaseMessagingService" android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver android:name=".NotificationClearBroadcastReceiver" />
|
||||
|
||||
<service
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
package com.keylesspalace.tusky;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
|
@ -22,6 +24,7 @@ import android.graphics.Color;
|
|||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemClock;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
@ -32,7 +35,6 @@ import android.view.Menu;
|
|||
import com.google.firebase.iid.FirebaseInstanceId;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.keylesspalace.tusky.entity.Account;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -47,16 +49,13 @@ import retrofit2.Callback;
|
|||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
|
||||
/* There isn't presently a way to globally change the theme of a whole application at runtime, just
|
||||
* individual activities. So, each activity has to set its theme before any views are created. And
|
||||
* the most expedient way to accomplish this was to put it in a base class and just have every
|
||||
* activity extend from it. */
|
||||
public class BaseActivity extends AppCompatActivity {
|
||||
private static final String TAG = "BaseActivity"; // logging tag
|
||||
|
||||
protected MastodonAPI mastodonAPI;
|
||||
protected TuskyAPI tuskyAPI;
|
||||
protected Dispatcher mastodonApiDispatcher;
|
||||
protected PendingIntent serviceAlarmIntent;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
|
@ -66,6 +65,9 @@ public class BaseActivity extends AppCompatActivity {
|
|||
createMastodonAPI();
|
||||
createTuskyAPI();
|
||||
|
||||
/* There isn't presently a way to globally change the theme of a whole application at
|
||||
* runtime, just individual activities. So, each activity has to set its theme before any
|
||||
* views are created. */
|
||||
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("lightTheme", false)) {
|
||||
setTheme(R.style.AppTheme_Light);
|
||||
}
|
||||
|
@ -154,12 +156,14 @@ public class BaseActivity extends AppCompatActivity {
|
|||
}
|
||||
|
||||
protected void createTuskyAPI() {
|
||||
Retrofit retrofit = new Retrofit.Builder()
|
||||
.baseUrl(getString(R.string.tusky_api_url))
|
||||
.client(OkHttpUtils.getCompatibleClient())
|
||||
.build();
|
||||
if (BuildConfig.USES_PUSH_NOTIFICATIONS) {
|
||||
Retrofit retrofit = new Retrofit.Builder()
|
||||
.baseUrl(getString(R.string.tusky_api_url))
|
||||
.client(OkHttpUtils.getCompatibleClient())
|
||||
.build();
|
||||
|
||||
tuskyAPI = retrofit.create(TuskyAPI.class);
|
||||
tuskyAPI = retrofit.create(TuskyAPI.class);
|
||||
}
|
||||
}
|
||||
|
||||
protected void redirectIfNotLoggedIn() {
|
||||
|
@ -193,30 +197,48 @@ public class BaseActivity extends AppCompatActivity {
|
|||
}
|
||||
|
||||
protected void enablePushNotifications() {
|
||||
tuskyAPI.register(getBaseUrl(), getAccessToken(), FirebaseInstanceId.getInstance().getToken()).enqueue(new Callback<ResponseBody>() {
|
||||
@Override
|
||||
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
|
||||
Log.d(TAG, "Enable push notifications response: " + response.message());
|
||||
}
|
||||
if (BuildConfig.USES_PUSH_NOTIFICATIONS) {
|
||||
tuskyAPI.register(getBaseUrl(), getAccessToken(), FirebaseInstanceId.getInstance().getToken()).enqueue(new Callback<ResponseBody>() {
|
||||
@Override
|
||||
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
|
||||
Log.d(TAG, "Enable push notifications response: " + response.message());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ResponseBody> call, Throwable t) {
|
||||
Log.d(TAG, "Enable push notifications failed: " + t.getMessage());
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onFailure(Call<ResponseBody> call, Throwable t) {
|
||||
Log.d(TAG, "Enable push notifications failed: " + t.getMessage());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Start up the MessagingService on a repeating interval for "pull" notifications.
|
||||
long checkInterval = 60 * 1000; // * 10;
|
||||
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
|
||||
Intent intent = new Intent(this, MessagingService.class);
|
||||
final int SERVICE_REQUEST_CODE = 8574603; // This number is arbitrary.
|
||||
serviceAlarmIntent = PendingIntent.getService(this, SERVICE_REQUEST_CODE, intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
|
||||
SystemClock.elapsedRealtime(), checkInterval, serviceAlarmIntent);
|
||||
}
|
||||
}
|
||||
|
||||
protected void disablePushNotifications() {
|
||||
tuskyAPI.unregister(getBaseUrl(), getAccessToken()).enqueue(new Callback<ResponseBody>() {
|
||||
@Override
|
||||
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
|
||||
Log.d(TAG, "Disable push notifications response: " + response.message());
|
||||
}
|
||||
if (BuildConfig.USES_PUSH_NOTIFICATIONS) {
|
||||
tuskyAPI.unregister(getBaseUrl(), getAccessToken()).enqueue(new Callback<ResponseBody>() {
|
||||
@Override
|
||||
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
|
||||
Log.d(TAG, "Disable push notifications response: " + response.message());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ResponseBody> call, Throwable t) {
|
||||
Log.d(TAG, "Disable push notifications failed: " + t.getMessage());
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onFailure(Call<ResponseBody> call, Throwable t) {
|
||||
Log.d(TAG, "Disable push notifications failed: " + t.getMessage());
|
||||
}
|
||||
});
|
||||
} else if (serviceAlarmIntent != null) {
|
||||
// Cancel the repeating call for "pull" notifications.
|
||||
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
|
||||
alarmManager.cancel(serviceAlarmIntent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
package com.keylesspalace.tusky;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
/* 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;
|
||||
|
||||
import android.Manifest;
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
/* 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;
|
||||
|
||||
import android.preference.PreferenceManager;
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
/* 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;
|
||||
|
||||
interface LinkListener {
|
||||
|
|
|
@ -187,7 +187,11 @@ public class MainActivity extends BaseActivity implements SFragment.OnUserRemove
|
|||
}
|
||||
|
||||
// Setup push notifications
|
||||
if (arePushNotificationsEnabled()) enablePushNotifications();
|
||||
if (arePushNotificationsEnabled()) {
|
||||
enablePushNotifications();
|
||||
} else {
|
||||
disablePushNotifications();
|
||||
}
|
||||
|
||||
composeButton = floatingBtn;
|
||||
}
|
||||
|
@ -203,7 +207,7 @@ public class MainActivity extends BaseActivity implements SFragment.OnUserRemove
|
|||
.apply();
|
||||
|
||||
((NotificationManager) (getSystemService(NOTIFICATION_SERVICE)))
|
||||
.cancel(MyFirebaseMessagingService.NOTIFY_ID);
|
||||
.cancel(MessagingService.NOTIFY_ID);
|
||||
|
||||
/* After editing a profile, the profile header in the navigation drawer needs to be
|
||||
* refreshed */
|
||||
|
|
|
@ -1,319 +0,0 @@
|
|||
/* 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>.
|
||||
*
|
||||
* If you modify this Program, or any covered work, by linking or combining it with Firebase Cloud
|
||||
* Messaging and Firebase Crash Reporting (or a modified version of those libraries), containing
|
||||
* parts covered by the Google APIs Terms of Service, the licensors of this Program grant you
|
||||
* additional permission to convey the resulting work. */
|
||||
|
||||
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 org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
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";
|
||||
public static final int NOTIFY_ID = 666;
|
||||
|
||||
@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);
|
||||
|
||||
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()) {
|
||||
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 = 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);
|
||||
}
|
||||
|
||||
private String truncateWithEllipses(String string, int limit) {
|
||||
if (string.length() < limit) {
|
||||
return string;
|
||||
} else {
|
||||
return string.substring(0, limit - 3) + "...";
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean filterNotification(SharedPreferences preferences,
|
||||
Notification notification) {
|
||||
switch (notification.type) {
|
||||
default:
|
||||
case MENTION: {
|
||||
return preferences.getBoolean("notificationFilterMentions", true);
|
||||
}
|
||||
case FOLLOW: {
|
||||
return preferences.getBoolean("notificationFilterFollows", true);
|
||||
}
|
||||
case REBLOG: {
|
||||
return preferences.getBoolean("notificationFilterReblogs", true);
|
||||
}
|
||||
case FAVOURITE: {
|
||||
return preferences.getBoolean("notificationFilterFavourites", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void buildNotification(Notification body) {
|
||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
final SharedPreferences notificationPreferences = getApplicationContext().getSharedPreferences("Notifications", MODE_PRIVATE);
|
||||
|
||||
if (!filterNotification(preferences, body)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String rawCurrentNotifications = notificationPreferences.getString("current", "[]");
|
||||
JSONArray currentNotifications;
|
||||
|
||||
try {
|
||||
currentNotifications = new JSONArray(rawCurrentNotifications);
|
||||
} catch (JSONException e) {
|
||||
currentNotifications = new JSONArray();
|
||||
}
|
||||
|
||||
boolean alreadyContains = false;
|
||||
|
||||
for(int i = 0; i < currentNotifications.length(); i++) {
|
||||
try {
|
||||
if (currentNotifications.getString(i).equals(body.account.displayName)) {
|
||||
alreadyContains = true;
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (!alreadyContains) {
|
||||
currentNotifications.put(body.account.displayName);
|
||||
}
|
||||
|
||||
SharedPreferences.Editor editor = notificationPreferences.edit();
|
||||
editor.putString("current", currentNotifications.toString());
|
||||
editor.commit();
|
||||
|
||||
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);
|
||||
|
||||
Intent deleteIntent = new Intent(this, NotificationClearBroadcastReceiver.class);
|
||||
PendingIntent deletePendingIntent = PendingIntent.getBroadcast(this, 0, deleteIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
final NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
|
||||
.setSmallIcon(R.drawable.ic_notify)
|
||||
.setContentIntent(resultPendingIntent)
|
||||
.setDeleteIntent(deletePendingIntent)
|
||||
.setDefaults(0); // So it doesn't ring twice, notify only in Target callback
|
||||
|
||||
if (currentNotifications.length() == 1) {
|
||||
builder.setContentTitle(titleForType(body))
|
||||
.setContentText(truncateWithEllipses(bodyForType(body), 40));
|
||||
|
||||
Target mTarget = new Target() {
|
||||
@Override
|
||||
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
|
||||
builder.setLargeIcon(bitmap);
|
||||
|
||||
setupPreferences(preferences, builder);
|
||||
|
||||
((NotificationManager) (getSystemService(NOTIFICATION_SERVICE))).notify(NOTIFY_ID, 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)
|
||||
.transform(new RoundedTransformation(7, 0))
|
||||
.into(mTarget);
|
||||
} else {
|
||||
setupPreferences(preferences, builder);
|
||||
|
||||
try {
|
||||
builder.setContentTitle(String.format(getString(R.string.notification_title_summary), currentNotifications.length()))
|
||||
.setContentText(truncateWithEllipses(joinNames(currentNotifications), 40));
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
builder.setVisibility(android.app.Notification.VISIBILITY_PRIVATE);
|
||||
builder.setCategory(android.app.Notification.CATEGORY_SOCIAL);
|
||||
}
|
||||
|
||||
((NotificationManager) (getSystemService(NOTIFICATION_SERVICE))).notify(NOTIFY_ID, builder.build());
|
||||
}
|
||||
|
||||
private void setupPreferences(SharedPreferences preferences, NotificationCompat.Builder builder) {
|
||||
if (preferences.getBoolean("notificationAlertSound", true)) {
|
||||
builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI);
|
||||
}
|
||||
|
||||
if (preferences.getBoolean("notificationAlertVibrate", false)) {
|
||||
builder.setVibrate(new long[] { 500, 500 });
|
||||
}
|
||||
|
||||
if (preferences.getBoolean("notificationAlertLight", false)) {
|
||||
builder.setLights(0xFF00FF8F, 300, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
private String joinNames(JSONArray array) throws JSONException {
|
||||
if (array.length() > 3) {
|
||||
return String.format(getString(R.string.notification_summary_large), array.get(0), array.get(1), array.get(2), array.length() - 3);
|
||||
} else if (array.length() == 3) {
|
||||
return String.format(getString(R.string.notification_summary_medium), array.get(0), array.get(1), array.get(2));
|
||||
} else if (array.length() == 2) {
|
||||
return String.format(getString(R.string.notification_summary_small), array.get(0), array.get(1));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String titleForType(Notification notification) {
|
||||
switch (notification.type) {
|
||||
case MENTION:
|
||||
return String.format(getString(R.string.notification_mention_format), notification.account.getDisplayName());
|
||||
case FOLLOW:
|
||||
return String.format(getString(R.string.notification_follow_format), notification.account.getDisplayName());
|
||||
case FAVOURITE:
|
||||
return String.format(getString(R.string.notification_favourite_format), notification.account.getDisplayName());
|
||||
case REBLOG:
|
||||
return String.format(getString(R.string.notification_reblog_format), notification.account.getDisplayName());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String bodyForType(Notification notification) {
|
||||
switch (notification.type) {
|
||||
case FOLLOW:
|
||||
return notification.account.username;
|
||||
case MENTION:
|
||||
case FAVOURITE:
|
||||
case REBLOG:
|
||||
return notification.status.content.toString();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
224
app/src/main/java/com/keylesspalace/tusky/NotificationMaker.java
Normal file
224
app/src/main/java/com/keylesspalace/tusky/NotificationMaker.java
Normal file
|
@ -0,0 +1,224 @@
|
|||
/* 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;
|
||||
|
||||
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.annotation.Nullable;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.TaskStackBuilder;
|
||||
|
||||
import com.keylesspalace.tusky.entity.Notification;
|
||||
import com.squareup.picasso.Picasso;
|
||||
import com.squareup.picasso.Target;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
class NotificationMaker {
|
||||
static void make(final Context context, final int notifyId, Notification body) {
|
||||
final SharedPreferences preferences =
|
||||
PreferenceManager.getDefaultSharedPreferences(context);
|
||||
final SharedPreferences notificationPreferences = context.getSharedPreferences(
|
||||
"Notifications", Context.MODE_PRIVATE);
|
||||
|
||||
if (!filterNotification(preferences, body)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String rawCurrentNotifications = notificationPreferences.getString("current", "[]");
|
||||
JSONArray currentNotifications;
|
||||
|
||||
try {
|
||||
currentNotifications = new JSONArray(rawCurrentNotifications);
|
||||
} catch (JSONException e) {
|
||||
currentNotifications = new JSONArray();
|
||||
}
|
||||
|
||||
boolean alreadyContains = false;
|
||||
|
||||
for(int i = 0; i < currentNotifications.length(); i++) {
|
||||
try {
|
||||
if (currentNotifications.getString(i).equals(body.account.getDisplayName())) {
|
||||
alreadyContains = true;
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (!alreadyContains) {
|
||||
currentNotifications.put(body.account.getDisplayName());
|
||||
}
|
||||
|
||||
notificationPreferences.edit()
|
||||
.putString("current", currentNotifications.toString())
|
||||
.commit();
|
||||
|
||||
Intent resultIntent = new Intent(context, MainActivity.class);
|
||||
resultIntent.putExtra("tab_position", 1);
|
||||
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
|
||||
stackBuilder.addParentStack(MainActivity.class);
|
||||
stackBuilder.addNextIntent(resultIntent);
|
||||
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
Intent deleteIntent = new Intent(context, NotificationClearBroadcastReceiver.class);
|
||||
PendingIntent deletePendingIntent = PendingIntent.getBroadcast(context, 0, deleteIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
|
||||
.setSmallIcon(R.drawable.ic_notify)
|
||||
.setContentIntent(resultPendingIntent)
|
||||
.setDeleteIntent(deletePendingIntent)
|
||||
.setDefaults(0); // So it doesn't ring twice, notify only in Target callback
|
||||
|
||||
if (currentNotifications.length() == 1) {
|
||||
builder.setContentTitle(titleForType(context, body))
|
||||
.setContentText(truncateWithEllipses(bodyForType(body), 40));
|
||||
|
||||
Target mTarget = new Target() {
|
||||
@Override
|
||||
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
|
||||
builder.setLargeIcon(bitmap);
|
||||
|
||||
setupPreferences(preferences, builder);
|
||||
|
||||
((NotificationManager) (context.getSystemService(Context.NOTIFICATION_SERVICE)))
|
||||
.notify(notifyId, builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBitmapFailed(Drawable errorDrawable) {}
|
||||
|
||||
@Override
|
||||
public void onPrepareLoad(Drawable placeHolderDrawable) {}
|
||||
};
|
||||
|
||||
Picasso.with(context)
|
||||
.load(body.account.avatar)
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.transform(new RoundedTransformation(7, 0))
|
||||
.into(mTarget);
|
||||
} else {
|
||||
setupPreferences(preferences, builder);
|
||||
try {
|
||||
builder.setContentTitle(String.format(context.getString(R.string.notification_title_summary), currentNotifications.length()))
|
||||
.setContentText(truncateWithEllipses(joinNames(context, currentNotifications), 40));
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
builder.setVisibility(android.app.Notification.VISIBILITY_PRIVATE);
|
||||
builder.setCategory(android.app.Notification.CATEGORY_SOCIAL);
|
||||
}
|
||||
|
||||
((NotificationManager) (context.getSystemService(Context.NOTIFICATION_SERVICE)))
|
||||
.notify(notifyId, builder.build());
|
||||
}
|
||||
|
||||
private static boolean filterNotification(SharedPreferences preferences,
|
||||
Notification notification) {
|
||||
switch (notification.type) {
|
||||
default:
|
||||
case MENTION: {
|
||||
return preferences.getBoolean("notificationFilterMentions", true);
|
||||
}
|
||||
case FOLLOW: {
|
||||
return preferences.getBoolean("notificationFilterFollows", true);
|
||||
}
|
||||
case REBLOG: {
|
||||
return preferences.getBoolean("notificationFilterReblogs", true);
|
||||
}
|
||||
case FAVOURITE: {
|
||||
return preferences.getBoolean("notificationFilterFavourites", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String truncateWithEllipses(String string, int limit) {
|
||||
if (string.length() < limit) {
|
||||
return string;
|
||||
} else {
|
||||
return string.substring(0, limit - 3) + "...";
|
||||
}
|
||||
}
|
||||
|
||||
private static void setupPreferences(SharedPreferences preferences,
|
||||
NotificationCompat.Builder builder) {
|
||||
if (preferences.getBoolean("notificationAlertSound", true)) {
|
||||
builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI);
|
||||
}
|
||||
|
||||
if (preferences.getBoolean("notificationAlertVibrate", false)) {
|
||||
builder.setVibrate(new long[] { 500, 500 });
|
||||
}
|
||||
|
||||
if (preferences.getBoolean("notificationAlertLight", false)) {
|
||||
builder.setLights(0xFF00FF8F, 300, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String joinNames(Context context, JSONArray array) throws JSONException {
|
||||
if (array.length() > 3) {
|
||||
return String.format(context.getString(R.string.notification_summary_large), array.get(0), array.get(1), array.get(2), array.length() - 3);
|
||||
} else if (array.length() == 3) {
|
||||
return String.format(context.getString(R.string.notification_summary_medium), array.get(0), array.get(1), array.get(2));
|
||||
} else if (array.length() == 2) {
|
||||
return String.format(context.getString(R.string.notification_summary_small), array.get(0), array.get(1));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String titleForType(Context context, Notification notification) {
|
||||
switch (notification.type) {
|
||||
case MENTION:
|
||||
return String.format(context.getString(R.string.notification_mention_format), notification.account.getDisplayName());
|
||||
case FOLLOW:
|
||||
return String.format(context.getString(R.string.notification_follow_format), notification.account.getDisplayName());
|
||||
case FAVOURITE:
|
||||
return String.format(context.getString(R.string.notification_favourite_format), notification.account.getDisplayName());
|
||||
case REBLOG:
|
||||
return String.format(context.getString(R.string.notification_reblog_format), notification.account.getDisplayName());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String bodyForType(Notification notification) {
|
||||
switch (notification.type) {
|
||||
case FOLLOW:
|
||||
return notification.account.username;
|
||||
case MENTION:
|
||||
case FAVOURITE:
|
||||
case REBLOG:
|
||||
return notification.status.content.toString();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -42,7 +42,7 @@ import okhttp3.OkHttpClient;
|
|||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
class OkHttpUtils {
|
||||
public class OkHttpUtils {
|
||||
static final String TAG = "OkHttpUtils"; // logging tag
|
||||
|
||||
/**
|
||||
|
@ -58,8 +58,7 @@ class OkHttpUtils {
|
|||
* TLS 1.1 and 1.2 have to be manually enabled on API levels 16-20.
|
||||
*/
|
||||
@NonNull
|
||||
static OkHttpClient.Builder getCompatibleClientBuilder() {
|
||||
|
||||
public static OkHttpClient.Builder getCompatibleClientBuilder() {
|
||||
ConnectionSpec fallback = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
|
||||
.allEnabledCipherSuites()
|
||||
.supportsTlsExtensions(true)
|
||||
|
@ -79,7 +78,7 @@ class OkHttpUtils {
|
|||
}
|
||||
|
||||
@NonNull
|
||||
static OkHttpClient getCompatibleClient() {
|
||||
public static OkHttpClient getCompatibleClient() {
|
||||
return getCompatibleClientBuilder().build();
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ import com.google.gson.JsonParseException;
|
|||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
class SpannedTypeAdapter implements JsonDeserializer<Spanned> {
|
||||
public class SpannedTypeAdapter implements JsonDeserializer<Spanned> {
|
||||
@Override
|
||||
public Spanned deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
return HtmlUtils.fromHtml(Emojione.shortnameToUnicode(json.getAsString(), false));
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
/* 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;
|
||||
|
||||
interface StatusRemoveListener {
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
/* 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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
/* 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;
|
||||
|
||||
import com.emojione.Emojione;
|
||||
|
|
Loading…
Reference in a new issue