diff --git a/app/build.gradle b/app/build.gradle
index 553f1fee..9e976658 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -25,6 +25,9 @@ dependencies {
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
+ compile('com.mikepenz:materialdrawer:5.8.2@aar') {
+ transitive = true
+ }
compile 'com.android.support:appcompat-v7:25.2.0'
compile 'com.android.support:recyclerview-v7:25.2.0'
compile 'com.android.support:support-v13:25.2.0'
@@ -32,16 +35,17 @@ dependencies {
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.pkmmte.view:circularimageview:1.1'
compile 'com.github.peter9870:sparkbutton:master'
- testCompile 'junit:junit:4.12'
compile 'com.mikhaellopez:circularfillableloaders:1.2.0'
compile 'com.squareup.retrofit2:retrofit:2.2.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
- compile('com.mikepenz:materialdrawer:5.8.2@aar') {
- transitive = true
- }
compile 'com.github.chrisbanes:PhotoView:1.3.1'
compile 'com.mikepenz:google-material-typeface:3.0.1.0.original@aar'
compile 'com.github.arimorty:floatingsearchview:2.0.3'
compile 'com.jakewharton:butterknife:8.4.0'
+ compile 'com.google.firebase:firebase-messaging:10.0.1'
+ testCompile 'junit:junit:4.12'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
}
+
+
+apply plugin: 'com.google.gms.google-services'
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4bdc081e..fae43e26 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,7 +4,7 @@
-
+
+
+
-
+
+
@@ -42,10 +47,17 @@
-
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java
index 14ec4af3..689df0dc 100644
--- a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java
+++ b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java
@@ -47,12 +47,14 @@ import retrofit2.converter.gson.GsonConverterFactory;
* activity extend from it. */
public class BaseActivity extends AppCompatActivity {
protected MastodonAPI mastodonAPI;
+ protected TuskyAPI tuskyAPI;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
createMastodonAPI();
+ createTuskyAPI();
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("lightTheme", false)) {
setTheme(R.style.AppTheme_Light);
@@ -121,6 +123,14 @@ public class BaseActivity extends AppCompatActivity {
mastodonAPI = retrofit.create(MastodonAPI.class);
}
+ protected void createTuskyAPI() {
+ Retrofit retrofit = new Retrofit.Builder()
+ .baseUrl(getString(R.string.tusky_api_url))
+ .build();
+
+ tuskyAPI = retrofit.create(TuskyAPI.class);
+ }
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
TypedValue value = new TypedValue();
diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java
index 22626433..ff984ee3 100644
--- a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java
+++ b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java
@@ -41,6 +41,7 @@ import android.widget.TextView;
import com.arlib.floatingsearchview.FloatingSearchView;
import com.arlib.floatingsearchview.suggestions.SearchSuggestionsAdapter;
import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion;
+import com.google.firebase.iid.FirebaseInstanceId;
import com.keylesspalace.tusky.entity.Account;
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
import com.mikepenz.materialdrawer.AccountHeader;
@@ -60,6 +61,9 @@ import com.squareup.picasso.Picasso;
import java.util.List;
import java.util.Stack;
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@@ -67,28 +71,27 @@ import retrofit2.Response;
public class MainActivity extends BaseActivity {
private static final String TAG = "MainActivity"; // logging tag and Volley request tag
- private AlarmManager alarmManager;
- private PendingIntent serviceAlarmIntent;
- private boolean notificationServiceEnabled;
private String loggedInAccountId;
private String loggedInAccountUsername;
Stack pageHistory = new Stack();
- private ViewPager viewPager;
private AccountHeader headerResult;
private Drawer drawer;
+ @BindView(R.id.floating_search_view) FloatingSearchView searchView;
+ @BindView(R.id.floating_btn) FloatingActionButton floatingBtn;
+ @BindView(R.id.tab_layout) TabLayout tabLayout;
+ @BindView(R.id.pager) ViewPager viewPager;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
+ ButterKnife.bind(this);
+
// Fetch user info while we're doing other things.
fetchUserInfo();
- //Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- //setSupportActionBar(toolbar);
-
- FloatingActionButton floatingBtn = (FloatingActionButton) findViewById(R.id.floating_btn);
floatingBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -97,8 +100,79 @@ public class MainActivity extends BaseActivity {
}
});
- final FloatingSearchView searchView = (FloatingSearchView) findViewById(R.id.floating_search_view);
+ setupDrawer();
+ setupSearchView();
+ // Setup the tabs and timeline pager.
+ TimelinePagerAdapter adapter = new TimelinePagerAdapter(getSupportFragmentManager());
+ String[] pageTitles = {
+ getString(R.string.title_home),
+ getString(R.string.title_notifications),
+ getString(R.string.title_public)
+ };
+ adapter.setPageTitles(pageTitles);
+
+ int pageMargin = getResources().getDimensionPixelSize(R.dimen.tab_page_margin);
+ viewPager.setPageMargin(pageMargin);
+ Drawable pageMarginDrawable = ThemeUtils.getDrawable(this, R.attr.tab_page_margin_drawable,
+ R.drawable.tab_page_margin_dark);
+ viewPager.setPageMarginDrawable(pageMarginDrawable);
+ viewPager.setAdapter(adapter);
+
+ tabLayout.setupWithViewPager(viewPager);
+
+ tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
+ @Override
+ public void onTabSelected(TabLayout.Tab tab) {
+ viewPager.setCurrentItem(tab.getPosition());
+
+ if (pageHistory.empty()) {
+ pageHistory.push(0);
+ }
+
+ if (pageHistory.contains(tab.getPosition())) {
+ pageHistory.remove(pageHistory.indexOf(tab.getPosition()));
+ }
+
+ pageHistory.push(tab.getPosition());
+ }
+
+ @Override
+ public void onTabUnselected(TabLayout.Tab tab) {
+
+ }
+
+ @Override
+ public void onTabReselected(TabLayout.Tab tab) {
+
+ }
+ });
+
+ Intent intent = getIntent();
+
+ if (intent != null) {
+ int tabPosition = intent.getIntExtra("tab_position", 0);
+
+ if (tabPosition != 0) {
+ tabLayout.getTabAt(tabPosition).select();
+ }
+ }
+
+ // Setup push notifications
+ tuskyAPI.register(getBaseUrl(), getAccessToken(), FirebaseInstanceId.getInstance().getToken()).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+
+ }
+ });
+ }
+
+ private void setupDrawer() {
headerResult = new AccountHeaderBuilder()
.withActivity(this)
.withSelectionListEnabledForSingleProfile(false)
@@ -152,18 +226,7 @@ public class MainActivity extends BaseActivity {
Intent intent = new Intent(MainActivity.this, PreferencesActivity.class);
startActivity(intent);
} else if (drawerItemIdentifier == 4) {
- if (notificationServiceEnabled) {
- alarmManager.cancel(serviceAlarmIntent);
- }
- SharedPreferences preferences = getSharedPreferences(
- getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = preferences.edit();
- editor.remove("domain");
- editor.remove("accessToken");
- editor.apply();
- Intent intent = new Intent(MainActivity.this, SplashActivity.class);
- startActivity(intent);
- finish();
+ logout();
}
}
@@ -171,7 +234,33 @@ public class MainActivity extends BaseActivity {
}
})
.build();
+ }
+ private void logout() {
+ tuskyAPI.unregister(getBaseUrl(), getAccessToken()).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+
+ }
+ });
+
+ SharedPreferences preferences = getSharedPreferences(getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = preferences.edit();
+ editor.remove("domain");
+ editor.remove("accessToken");
+ editor.apply();
+
+ Intent intent = new Intent(MainActivity.this, LoginActivity.class);
+ startActivity(intent);
+ finish();
+ }
+
+ private void setupSearchView() {
searchView.attachNavigationDrawerToMenuButton(drawer.getDrawerLayout());
searchView.setOnQueryChangeListener(new FloatingSearchView.OnQueryChangeListener() {
@@ -237,70 +326,6 @@ public class MainActivity extends BaseActivity {
textView.setEllipsize(TextUtils.TruncateAt.END);
}
});
-
- // Setup the tabs and timeline pager.
- TimelinePagerAdapter adapter = new TimelinePagerAdapter(getSupportFragmentManager());
- String[] pageTitles = {
- getString(R.string.title_home),
- getString(R.string.title_notifications),
- getString(R.string.title_public)
- };
- adapter.setPageTitles(pageTitles);
- viewPager = (ViewPager) findViewById(R.id.pager);
- int pageMargin = getResources().getDimensionPixelSize(R.dimen.tab_page_margin);
- viewPager.setPageMargin(pageMargin);
- Drawable pageMarginDrawable = ThemeUtils.getDrawable(this, R.attr.tab_page_margin_drawable,
- R.drawable.tab_page_margin_dark);
- viewPager.setPageMarginDrawable(pageMarginDrawable);
- viewPager.setAdapter(adapter);
-
- TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
- tabLayout.setupWithViewPager(viewPager);
-
- tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
- @Override
- public void onTabSelected(TabLayout.Tab tab) {
- viewPager.setCurrentItem(tab.getPosition());
-
- if (pageHistory.empty()) {
- pageHistory.push(0);
- }
-
- if (pageHistory.contains(tab.getPosition())) {
- pageHistory.remove(pageHistory.indexOf(tab.getPosition()));
- }
-
- pageHistory.push(tab.getPosition());
- }
-
- @Override
- public void onTabUnselected(TabLayout.Tab tab) {
-
- }
-
- @Override
- public void onTabReselected(TabLayout.Tab tab) {
-
- }
- });
-
- // Retrieve notification update preference.
- SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
- notificationServiceEnabled = preferences.getBoolean("pullNotifications", true);
- String minutesString = preferences.getString("pullNotificationCheckInterval", "15");
- long notificationCheckInterval = 60 * 1000 * Integer.valueOf(minutesString);
- // Start up the PullNotificationsService.
- alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
- Intent intent = new Intent(this, PullNotificationService.class);
- final int SERVICE_REQUEST_CODE = 8574603; // This number is arbitrary.
- serviceAlarmIntent = PendingIntent.getService(this, SERVICE_REQUEST_CODE, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- if (notificationServiceEnabled) {
- alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime(), notificationCheckInterval, serviceAlarmIntent);
- } else {
- alarmManager.cancel(serviceAlarmIntent);
- }
}
private void fetchUserInfo() {
diff --git a/app/src/main/java/com/keylesspalace/tusky/MyFirebaseInstanceIdService.java b/app/src/main/java/com/keylesspalace/tusky/MyFirebaseInstanceIdService.java
new file mode 100644
index 00000000..7c173369
--- /dev/null
+++ b/app/src/main/java/com/keylesspalace/tusky/MyFirebaseInstanceIdService.java
@@ -0,0 +1,61 @@
+package com.keylesspalace.tusky;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import com.google.firebase.iid.FirebaseInstanceId;
+import com.google.firebase.iid.FirebaseInstanceIdService;
+
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+import retrofit2.Retrofit;
+
+public class MyFirebaseInstanceIdService extends FirebaseInstanceIdService {
+
+ private TuskyAPI tuskyAPI;
+
+ protected void createTuskyAPI() {
+ Retrofit retrofit = new Retrofit.Builder()
+ .baseUrl(getString(R.string.tusky_api_url))
+ .build();
+
+ tuskyAPI = retrofit.create(TuskyAPI.class);
+ }
+
+ @Override
+ public void onTokenRefresh() {
+ createTuskyAPI();
+
+ String refreshedToken = FirebaseInstanceId.getInstance().getToken();
+ SharedPreferences preferences = getSharedPreferences(getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
+ String accessToken = preferences.getString("accessToken", null);
+ String domain = preferences.getString("domain", null);
+
+ if (accessToken != null && domain != null) {
+ tuskyAPI.unregister("https://" + domain, accessToken).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+
+ }
+ });
+ tuskyAPI.register("https://" + domain, accessToken, refreshedToken).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+
+ }
+ });
+ }
+ }
+}
diff --git a/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java b/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java
new file mode 100644
index 00000000..20b5cd58
--- /dev/null
+++ b/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java
@@ -0,0 +1,189 @@
+package com.keylesspalace.tusky;
+
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.TaskStackBuilder;
+import android.text.Spanned;
+
+import com.google.firebase.messaging.FirebaseMessagingService;
+import com.google.firebase.messaging.RemoteMessage;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.keylesspalace.tusky.entity.Notification;
+import com.squareup.picasso.Picasso;
+import com.squareup.picasso.Target;
+
+import java.io.IOException;
+
+import okhttp3.Interceptor;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+import retrofit2.Retrofit;
+import retrofit2.converter.gson.GsonConverterFactory;
+
+public class MyFirebaseMessagingService extends FirebaseMessagingService {
+ private MastodonAPI mastodonAPI;
+ private static final String TAG = "MyFirebaseMessagingService";
+
+ @Override
+ public void onMessageReceived(RemoteMessage remoteMessage) {
+ Log.d(TAG, remoteMessage.getFrom());
+ Log.d(TAG, remoteMessage.toString());
+
+ String notificationId = remoteMessage.getData().get("notification_id");
+
+ if (notificationId == null) {
+ Log.e(TAG, "No notification ID in payload!!");
+ return;
+ }
+
+ Log.d(TAG, notificationId);
+
+ createMastodonAPI();
+
+ mastodonAPI.notification(notificationId).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ 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 = new OkHttpClient.Builder()
+ .addInterceptor(new Interceptor() {
+ @Override
+ public okhttp3.Response intercept(Chain chain) throws IOException {
+ Request originalRequest = chain.request();
+
+ Request.Builder builder = originalRequest.newBuilder()
+ .header("Authorization", String.format("Bearer %s", accessToken));
+
+ Request newRequest = builder.build();
+
+ return chain.proceed(newRequest);
+ }
+ })
+ .build();
+
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Spanned.class, new SpannedTypeAdapter())
+ .create();
+
+ Retrofit retrofit = new Retrofit.Builder()
+ .baseUrl("https://" + domain)
+ .client(okHttpClient)
+ .addConverterFactory(GsonConverterFactory.create(gson))
+ .build();
+
+ mastodonAPI = retrofit.create(MastodonAPI.class);
+ }
+
+ private String truncateWithEllipses(String string, int limit) {
+ if (string.length() < limit) {
+ return string;
+ } else {
+ return string.substring(0, limit - 3) + "...";
+ }
+ }
+
+ private void buildNotification(Notification body) {
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ Intent resultIntent = new Intent(this, MainActivity.class);
+ resultIntent.putExtra("tab_position", 1);
+ TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
+ stackBuilder.addParentStack(MainActivity.class);
+ stackBuilder.addNextIntent(resultIntent);
+ PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
+
+ final NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
+ .setSmallIcon(R.drawable.ic_notify)
+ .setAutoCancel(true)
+ .setContentIntent(resultPendingIntent);
+
+ final Integer mId = (int)(System.currentTimeMillis() / 1000);
+
+ Target mTarget = new Target() {
+ @Override
+ public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
+ builder.setLargeIcon(bitmap);
+ ((NotificationManager) (getSystemService(NOTIFICATION_SERVICE))).notify(mId, builder.build());
+ }
+
+ @Override
+ public void onBitmapFailed(Drawable errorDrawable) {
+
+ }
+
+ @Override
+ public void onPrepareLoad(Drawable placeHolderDrawable) {
+
+ }
+ };
+
+ Picasso.with(this)
+ .load(body.account.avatar)
+ .placeholder(R.drawable.avatar_default)
+ .into(mTarget);
+
+ if (preferences.getBoolean("notificationAlertSound", true)) {
+ builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI);
+ }
+
+ if (preferences.getBoolean("notificationStyleVibrate", false)) {
+ builder.setVibrate(new long[] { 500, 500 });
+ }
+
+ if (preferences.getBoolean("notificationStyleLight", false)) {
+ builder.setLights(0xFF00FF8F, 300, 1000);
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ builder.setVisibility(android.app.Notification.VISIBILITY_PRIVATE);
+ builder.setCategory(android.app.Notification.CATEGORY_SOCIAL);
+ }
+
+ switch (body.type) {
+ case MENTION:
+ builder.setContentTitle(String.format(getString(R.string.notification_mention_format), body.account.getDisplayName()))
+ .setContentText(truncateWithEllipses(body.status.content.toString(), 40));
+ break;
+ case FOLLOW:
+ builder.setContentTitle(String.format(getString(R.string.notification_follow_format), body.account.getDisplayName()))
+ .setContentText(truncateWithEllipses(body.account.username, 40));
+ break;
+ case FAVOURITE:
+ builder.setContentTitle(String.format(getString(R.string.notification_favourite_format), body.account.getDisplayName()))
+ .setContentText(truncateWithEllipses(body.status.content.toString(), 40));
+ break;
+ case REBLOG:
+ builder.setContentTitle(String.format(getString(R.string.notification_reblog_format), body.account.getDisplayName()))
+ .setContentText(truncateWithEllipses(body.status.content.toString(), 40));
+ break;
+ }
+
+ ((NotificationManager) (getSystemService(NOTIFICATION_SERVICE))).notify(mId, builder.build());
+ }
+}
diff --git a/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java b/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java
index 08460a08..a06441d6 100644
--- a/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java
@@ -117,15 +117,6 @@ public class NotificationsFragment extends SFragment implements
return rootView;
}
- @Override
- public void onResume() {
- super.onResume();
-
- // When we view this fragment, dismiss the notifications
- NotificationManager notificationManager = (NotificationManager) getActivity().getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.cancel(PullNotificationService.NOTIFY_ID);
- }
-
@Override
public void onDestroyView() {
TabLayout tabLayout = (TabLayout) getActivity().findViewById(R.id.tab_layout);
diff --git a/app/src/main/java/com/keylesspalace/tusky/PullNotificationService.java b/app/src/main/java/com/keylesspalace/tusky/PullNotificationService.java
deleted file mode 100644
index 745e0371..00000000
--- a/app/src/main/java/com/keylesspalace/tusky/PullNotificationService.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/* Copyright 2017 Andrew Dawson
- *
- * This file is part of Tusky.
- *
- * Tusky is free software: you can redistribute it and/or modify it under the terms of the GNU
- * General Public License as published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
- * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
- * Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with Tusky. If not, see
- * . */
-
-package com.keylesspalace.tusky;
-
-import android.app.*;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.graphics.Bitmap;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.preference.PreferenceManager;
-import android.provider.Settings;
-import android.support.annotation.Nullable;
-import android.support.v4.app.NotificationCompat;
-import android.support.v4.app.TaskStackBuilder;
-import android.text.Spanned;
-
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.keylesspalace.tusky.entity.*;
-import com.keylesspalace.tusky.entity.Notification;
-import com.squareup.picasso.Picasso;
-import com.squareup.picasso.Target;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import okhttp3.Interceptor;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import retrofit2.Call;
-import retrofit2.Callback;
-import retrofit2.Retrofit;
-import retrofit2.converter.gson.GsonConverterFactory;
-
-public class PullNotificationService extends IntentService {
- static final int NOTIFY_ID = 6; // This is an arbitrary number.
- private static final String TAG = "PullNotifications"; // logging tag
-
- public PullNotificationService() {
- super("Tusky Pull Notification Service");
- }
-
- @Override
- protected void onHandleIntent(Intent intent) {
- SharedPreferences preferences = getSharedPreferences(
- getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
- String domain = preferences.getString("domain", null);
- String accessToken = preferences.getString("accessToken", null);
- String lastUpdateId = preferences.getString("lastUpdateId", null);
- checkNotifications(domain, accessToken, lastUpdateId);
- }
-
- private void checkNotifications(final String domain, final String accessToken,
- final String lastUpdateId) {
- OkHttpClient okHttpClient = new OkHttpClient.Builder()
- .addInterceptor(new Interceptor() {
- @Override
- public okhttp3.Response intercept(Chain chain) throws IOException {
- Request originalRequest = chain.request();
-
- Request.Builder builder = originalRequest.newBuilder()
- .header("Authorization", String.format("Bearer %s", accessToken));
-
- Request newRequest = builder.build();
-
- return chain.proceed(newRequest);
- }
- })
- .build();
-
- Gson gson = new GsonBuilder()
- .registerTypeAdapter(Spanned.class, new SpannedTypeAdapter())
- .create();
-
- Retrofit retrofit = new Retrofit.Builder()
- .baseUrl("https://" + domain)
- .client(okHttpClient)
- .addConverterFactory(GsonConverterFactory.create(gson))
- .build();
-
- MastodonAPI api = retrofit.create(MastodonAPI.class);
-
- api.notifications(null, lastUpdateId, null).enqueue(new Callback>() {
- @Override
- public void onResponse(Call> call, retrofit2.Response> response) {
- onCheckNotificationsSuccess(response.body(), lastUpdateId);
- }
-
- @Override
- public void onFailure(Call> call, Throwable t) {
- onCheckNotificationsFailure((Exception) t);
- }
- });
- }
-
- private void onCheckNotificationsSuccess(List notifications, String lastUpdateId) {
- List mentions = new ArrayList<>();
-
- for (com.keylesspalace.tusky.entity.Notification notification : notifications) {
- if (notification.type == com.keylesspalace.tusky.entity.Notification.Type.MENTION) {
- Status status = notification.status;
-
- if (status != null) {
- MentionResult mention = new MentionResult();
- mention.content = status.content.toString();
- mention.displayName = notification.account.getDisplayName();
- mention.avatarUrl = status.account.avatar;
- mentions.add(mention);
- }
- }
- }
-
- if (notifications.size() > 0) {
- SharedPreferences preferences = getSharedPreferences(
- getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = preferences.edit();
- editor.putString("lastUpdateId", notifications.get(0).id);
- editor.apply();
- }
-
- if (mentions.size() > 0) {
- loadAvatar(mentions, mentions.get(0).avatarUrl);
- }
- }
-
- private void onCheckNotificationsFailure(Exception exception) {
- Log.e(TAG, "Failed to check notifications. " + exception.getMessage());
- }
-
- private static class MentionResult {
- String displayName;
- String content;
- String avatarUrl;
- }
-
- private String truncateWithEllipses(String string, int limit) {
- if (string.length() < limit) {
- return string;
- } else {
- return string.substring(0, limit - 3) + "...";
- }
- }
-
- private void loadAvatar(final List mentions, String url) {
- if (url != null) {
- Target target = new Target() {
- @Override
- public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
- updateNotification(mentions, bitmap);
- }
-
- @Override
- public void onBitmapFailed(Drawable errorDrawable) {
- updateNotification(mentions, null);
- }
-
- @Override
- public void onPrepareLoad(Drawable placeHolderDrawable) {}
- };
- Picasso.with(this)
- .load(url)
- .into(target);
- } else {
- updateNotification(mentions, null);
- }
- }
-
- private void updateNotification(List mentions, @Nullable Bitmap icon) {
- final int NOTIFICATION_CONTENT_LIMIT = 40;
- SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
- String title;
- if (mentions.size() > 1) {
- title = String.format(
- getString(R.string.notification_service_several_mentions),
- mentions.size());
- } else {
- title = String.format(
- getString(R.string.notification_service_one_mention),
- mentions.get(0).displayName);
- }
- NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
- .setSmallIcon(R.drawable.ic_notify)
- .setContentTitle(title);
- if (icon != null) {
- builder.setLargeIcon(icon);
- }
- if (preferences.getBoolean("notificationAlertSound", true)) {
- builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI);
- }
- if (preferences.getBoolean("notificationStyleVibrate", false)) {
- builder.setVibrate(new long[] { 500, 500 });
- }
- if (preferences.getBoolean("notificationStyleLight", false)) {
- builder.setLights(0xFF00FF8F, 300, 1000);
- }
- for (int i = 0; i < mentions.size(); i++) {
- MentionResult mention = mentions.get(i);
- String text = truncateWithEllipses(mention.content, NOTIFICATION_CONTENT_LIMIT);
- builder.setContentText(text)
- .setNumber(i);
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- builder.setVisibility(android.app.Notification.VISIBILITY_PRIVATE);
- builder.setCategory(android.app.Notification.CATEGORY_SOCIAL);
- }
- Intent resultIntent = new Intent(this, SplashActivity.class);
- TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
- stackBuilder.addParentStack(SplashActivity.class);
- stackBuilder.addNextIntent(resultIntent);
- PendingIntent resultPendingIntent =
- stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
- builder.setContentIntent(resultPendingIntent);
- NotificationManager notificationManager =
- (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.notify(NOTIFY_ID, builder.build());
- }
-}
diff --git a/app/src/main/java/com/keylesspalace/tusky/TuskyAPI.java b/app/src/main/java/com/keylesspalace/tusky/TuskyAPI.java
new file mode 100644
index 00000000..2df62bc1
--- /dev/null
+++ b/app/src/main/java/com/keylesspalace/tusky/TuskyAPI.java
@@ -0,0 +1,16 @@
+package com.keylesspalace.tusky;
+
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.http.Field;
+import retrofit2.http.FormUrlEncoded;
+import retrofit2.http.POST;
+
+public interface TuskyAPI {
+ @FormUrlEncoded
+ @POST("/register")
+ Call register(@Field("instance_url") String instanceUrl, @Field("access_token") String accessToken, @Field("device_token") String deviceToken);
+ @FormUrlEncoded
+ @POST("/unregister")
+ Call unregister(@Field("instance_url") String instanceUrl, @Field("access_token") String accessToken);
+}
diff --git a/app/src/main/res/layout/fragment_view_media.xml b/app/src/main/res/layout/fragment_view_media.xml
index 1126dbe0..d3b84def 100644
--- a/app/src/main/res/layout/fragment_view_media.xml
+++ b/app/src/main/res/layout/fragment_view_media.xml
@@ -2,6 +2,7 @@
end of the notifications
end of the accounts
- %s boosted your status
- %s favourited your status
+ %s boosted your toot
+ %s favourited your toot
%s followed you
Report @%s
@@ -129,5 +129,7 @@
Search accounts…
NSFW
Mention
+ http://tusky.zeonfederated.com
+ %s mentioned you
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 9cedad58..9ace1a1e 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -3,39 +3,20 @@
android:key="@string/preferences_file_key">
-
-
-
-
-
+ android:defaultValue="true" />
+ android:defaultValue="true" />
diff --git a/build.gradle b/build.gradle
index 83a6ca5a..22983d58 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,6 +9,7 @@ buildscript {
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
+ classpath 'com.google.gms:google-services:3.0.0'
}
}