diff --git a/app/build.gradle b/app/build.gradle
index 7624ea62c..347ec31cf 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -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'
}
diff --git a/app/src/fdroid/AndroidManifest.xml b/app/src/fdroid/AndroidManifest.xml
new file mode 100644
index 000000000..b7060e6e2
--- /dev/null
+++ b/app/src/fdroid/AndroidManifest.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/fdroid/java/com/keylesspalace/tusky/MessagingService.java b/app/src/fdroid/java/com/keylesspalace/tusky/MessagingService.java
new file mode 100644
index 000000000..f7c24c425
--- /dev/null
+++ b/app/src/fdroid/java/com/keylesspalace/tusky/MessagingService.java
@@ -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 . */
+
+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>() {
+ @Override
+ public void onResponse(Call> call,
+ Response> response) {
+ if (response.isSuccessful()) {
+ List notificationList = response.body();
+ for (Notification notification : notificationList) {
+ NotificationMaker.make(MessagingService.this, NOTIFY_ID, notification);
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(Call> 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);
+ }
+}
diff --git a/app/src/google/AndroidManifest.xml b/app/src/google/AndroidManifest.xml
new file mode 100644
index 000000000..20ecbe94c
--- /dev/null
+++ b/app/src/google/AndroidManifest.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/google/java/com/keylesspalace/tusky/MessagingService.java b/app/src/google/java/com/keylesspalace/tusky/MessagingService.java
new file mode 100644
index 000000000..cf4e52733
--- /dev/null
+++ b/app/src/google/java/com/keylesspalace/tusky/MessagingService.java
@@ -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 .
+ *
+ * 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() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if (response.isSuccessful()) {
+ NotificationMaker.make(MessagingService.this, NOTIFY_ID, response.body());
+ }
+ }
+
+ @Override
+ public void onFailure(Call 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);
+ }
+}
diff --git a/app/src/main/java/com/keylesspalace/tusky/MyFirebaseInstanceIdService.java b/app/src/google/java/com/keylesspalace/tusky/MyFirebaseInstanceIdService.java
similarity index 97%
rename from app/src/main/java/com/keylesspalace/tusky/MyFirebaseInstanceIdService.java
rename to app/src/google/java/com/keylesspalace/tusky/MyFirebaseInstanceIdService.java
index 9e139187f..adb478795 100644
--- a/app/src/main/java/com/keylesspalace/tusky/MyFirebaseInstanceIdService.java
+++ b/app/src/google/java/com/keylesspalace/tusky/MyFirebaseInstanceIdService.java
@@ -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;
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ee77b8949..fcf90d73a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -15,7 +15,7 @@
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:name=".TuskyApplication">
-
+
@@ -72,18 +72,6 @@
android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
android:theme="@style/Base.Theme.AppCompat" />
-
-
-
-
-
-
-
-
-
-
-
-
() {
- @Override
- public void onResponse(Call call, retrofit2.Response 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() {
+ @Override
+ public void onResponse(Call call, retrofit2.Response response) {
+ Log.d(TAG, "Enable push notifications response: " + response.message());
+ }
- @Override
- public void onFailure(Call call, Throwable t) {
- Log.d(TAG, "Enable push notifications failed: " + t.getMessage());
- }
- });
+ @Override
+ public void onFailure(Call 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() {
- @Override
- public void onResponse(Call call, retrofit2.Response response) {
- Log.d(TAG, "Disable push notifications response: " + response.message());
- }
+ if (BuildConfig.USES_PUSH_NOTIFICATIONS) {
+ tuskyAPI.unregister(getBaseUrl(), getAccessToken()).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, retrofit2.Response response) {
+ Log.d(TAG, "Disable push notifications response: " + response.message());
+ }
- @Override
- public void onFailure(Call call, Throwable t) {
- Log.d(TAG, "Disable push notifications failed: " + t.getMessage());
- }
- });
+ @Override
+ public void onFailure(Call 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);
+ }
}
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/BlocksAdapter.java b/app/src/main/java/com/keylesspalace/tusky/BlocksAdapter.java
index 8b00c9397..d052db34b 100644
--- a/app/src/main/java/com/keylesspalace/tusky/BlocksAdapter.java
+++ b/app/src/main/java/com/keylesspalace/tusky/BlocksAdapter.java
@@ -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;
diff --git a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.java b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.java
index 84f06334a..f0313f946 100644
--- a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.java
+++ b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.java
@@ -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 . */
+
package com.keylesspalace.tusky;
import android.Manifest;
diff --git a/app/src/main/java/com/keylesspalace/tusky/LinkHelper.java b/app/src/main/java/com/keylesspalace/tusky/LinkHelper.java
index e2b2fedcf..ba9a591fe 100644
--- a/app/src/main/java/com/keylesspalace/tusky/LinkHelper.java
+++ b/app/src/main/java/com/keylesspalace/tusky/LinkHelper.java
@@ -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 . */
+
package com.keylesspalace.tusky;
import android.preference.PreferenceManager;
diff --git a/app/src/main/java/com/keylesspalace/tusky/LinkListener.java b/app/src/main/java/com/keylesspalace/tusky/LinkListener.java
index de0242bc1..9e188ae42 100644
--- a/app/src/main/java/com/keylesspalace/tusky/LinkListener.java
+++ b/app/src/main/java/com/keylesspalace/tusky/LinkListener.java
@@ -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 . */
+
package com.keylesspalace.tusky;
interface LinkListener {
diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java
index f42dd72ca..d25729a29 100644
--- a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java
+++ b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java
@@ -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 */
diff --git a/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java b/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java
deleted file mode 100644
index 6ff4d212b..000000000
--- a/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java
+++ /dev/null
@@ -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 .
- *
- * 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() {
- @Override
- public void onResponse(Call call, Response response) {
- if (response.isSuccessful()) {
- buildNotification(response.body());
- }
- }
-
- @Override
- public void onFailure(Call 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;
- }
-}
diff --git a/app/src/main/java/com/keylesspalace/tusky/NotificationMaker.java b/app/src/main/java/com/keylesspalace/tusky/NotificationMaker.java
new file mode 100644
index 000000000..8c7bfb85d
--- /dev/null
+++ b/app/src/main/java/com/keylesspalace/tusky/NotificationMaker.java
@@ -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 . */
+
+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;
+ }
+}
diff --git a/app/src/main/java/com/keylesspalace/tusky/OkHttpUtils.java b/app/src/main/java/com/keylesspalace/tusky/OkHttpUtils.java
index 09b086816..a821df4d8 100644
--- a/app/src/main/java/com/keylesspalace/tusky/OkHttpUtils.java
+++ b/app/src/main/java/com/keylesspalace/tusky/OkHttpUtils.java
@@ -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();
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/SpannedTypeAdapter.java b/app/src/main/java/com/keylesspalace/tusky/SpannedTypeAdapter.java
index 817d6d3f3..3b47acc3b 100644
--- a/app/src/main/java/com/keylesspalace/tusky/SpannedTypeAdapter.java
+++ b/app/src/main/java/com/keylesspalace/tusky/SpannedTypeAdapter.java
@@ -25,7 +25,7 @@ import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
-class SpannedTypeAdapter implements JsonDeserializer {
+public class SpannedTypeAdapter implements JsonDeserializer {
@Override
public Spanned deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return HtmlUtils.fromHtml(Emojione.shortnameToUnicode(json.getAsString(), false));
diff --git a/app/src/main/java/com/keylesspalace/tusky/StatusRemoveListener.java b/app/src/main/java/com/keylesspalace/tusky/StatusRemoveListener.java
index b662f237d..23111c6ab 100644
--- a/app/src/main/java/com/keylesspalace/tusky/StatusRemoveListener.java
+++ b/app/src/main/java/com/keylesspalace/tusky/StatusRemoveListener.java
@@ -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 . */
+
package com.keylesspalace.tusky;
interface StatusRemoveListener {
diff --git a/app/src/main/java/com/keylesspalace/tusky/StringWithEmoji.java b/app/src/main/java/com/keylesspalace/tusky/StringWithEmoji.java
index 76d07e9c9..cad8e8c08 100644
--- a/app/src/main/java/com/keylesspalace/tusky/StringWithEmoji.java
+++ b/app/src/main/java/com/keylesspalace/tusky/StringWithEmoji.java
@@ -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 . */
+
package com.keylesspalace.tusky;
/**
diff --git a/app/src/main/java/com/keylesspalace/tusky/StringWithEmojiTypeAdapter.java b/app/src/main/java/com/keylesspalace/tusky/StringWithEmojiTypeAdapter.java
index e4923921a..ecf18bcd9 100644
--- a/app/src/main/java/com/keylesspalace/tusky/StringWithEmojiTypeAdapter.java
+++ b/app/src/main/java/com/keylesspalace/tusky/StringWithEmojiTypeAdapter.java
@@ -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 . */
+
package com.keylesspalace.tusky;
import com.emojione.Emojione;