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.main
parent
3eb69b384d
commit
f67de9037d
@ -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); |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
Loading…
Reference in new issue