From 17b958f8ed61b284310d05df63f42f7a38b00d4b Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 14 Mar 2017 12:45:19 +0100 Subject: [PATCH 1/5] Cancel retrofit calls in fragment onDestroy --- .../keylesspalace/tusky/AccountFragment.java | 23 ++++++++++++++++--- .../tusky/NotificationsFragment.java | 11 ++++++++- .../keylesspalace/tusky/TimelineFragment.java | 23 +++++++++++++++---- 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountFragment.java b/app/src/main/java/com/keylesspalace/tusky/AccountFragment.java index e754abcb5..f5044783a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/AccountFragment.java @@ -40,10 +40,13 @@ import retrofit2.Callback; public class AccountFragment extends Fragment implements AccountActionListener { private static final String TAG = "Account"; // logging tag + private Call> listCall; + public enum Type { FOLLOWS, FOLLOWERS, BLOCKS, + MUTES, } private Type type; @@ -141,6 +144,12 @@ public class AccountFragment extends Fragment implements AccountActionListener { return rootView; } + @Override + public void onDestroy() { + super.onDestroy(); + if (listCall != null) listCall.cancel(); + } + @Override public void onDestroyView() { if (jumpToTopAllowed()) { @@ -166,15 +175,23 @@ public class AccountFragment extends Fragment implements AccountActionListener { switch (type) { default: case FOLLOWS: { - api.accountFollowing(accountId, fromId, uptoId, null).enqueue(cb); + listCall = api.accountFollowing(accountId, fromId, uptoId, null); + listCall.enqueue(cb); break; } case FOLLOWERS: { - api.accountFollowers(accountId, fromId, uptoId, null).enqueue(cb); + listCall = api.accountFollowers(accountId, fromId, uptoId, null); + listCall.enqueue(cb); break; } case BLOCKS: { - api.blocks(fromId, uptoId, null).enqueue(cb); + listCall = api.blocks(fromId, uptoId, null); + listCall.enqueue(cb); + break; + } + case MUTES: { + listCall = api.mutes(fromId, uptoId, null); + listCall.enqueue(cb); break; } } diff --git a/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java b/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java index dacebb1e8..b0cbe28b9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java @@ -48,6 +48,7 @@ public class NotificationsFragment extends SFragment implements private EndlessOnScrollListener scrollListener; private NotificationsAdapter adapter; private TabLayout.OnTabSelectedListener onTabSelectedListener; + private Call> listCall; public static NotificationsFragment newInstance() { NotificationsFragment fragment = new NotificationsFragment(); @@ -122,6 +123,12 @@ public class NotificationsFragment extends SFragment implements sendFetchNotificationsRequest(); } + @Override + public void onDestroy() { + super.onDestroy(); + if (listCall != null) listCall.cancel(); + } + @Override public void onDestroyView() { TabLayout tabLayout = (TabLayout) getActivity().findViewById(R.id.tab_layout); @@ -137,7 +144,9 @@ public class NotificationsFragment extends SFragment implements private void sendFetchNotificationsRequest(final String fromId, String uptoId) { MastodonAPI api = ((BaseActivity) getActivity()).mastodonAPI; - api.notifications(fromId, uptoId, null).enqueue(new Callback>() { + listCall = api.notifications(fromId, uptoId, null); + + listCall.enqueue(new Callback>() { @Override public void onResponse(Call> call, retrofit2.Response> response) { if (response.isSuccessful()) { diff --git a/app/src/main/java/com/keylesspalace/tusky/TimelineFragment.java b/app/src/main/java/com/keylesspalace/tusky/TimelineFragment.java index 4037dbc46..fff484cb7 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TimelineFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/TimelineFragment.java @@ -39,6 +39,8 @@ public class TimelineFragment extends SFragment implements SwipeRefreshLayout.OnRefreshListener, StatusActionListener { private static final String TAG = "Timeline"; // logging tag + private Call> listCall; + enum Kind { HOME, PUBLIC, @@ -144,6 +146,12 @@ public class TimelineFragment extends SFragment implements sendFetchTimelineRequest(); } + @Override + public void onDestroy() { + super.onDestroy(); + if (listCall != null) listCall.cancel(); + } + @Override public void onDestroyView() { if (jumpToTopAllowed()) { @@ -184,23 +192,28 @@ public class TimelineFragment extends SFragment implements switch (kind) { default: case HOME: { - api.homeTimeline(fromId, uptoId, null).enqueue(cb); + listCall = api.homeTimeline(fromId, uptoId, null); + listCall.enqueue(cb); break; } case PUBLIC: { - api.publicTimeline(null, fromId, uptoId, null).enqueue(cb); + listCall = api.publicTimeline(null, fromId, uptoId, null); + listCall.enqueue(cb); break; } case TAG: { - api.hashtagTimeline(hashtagOrId, null, fromId, uptoId, null).enqueue(cb); + listCall = api.hashtagTimeline(hashtagOrId, null, fromId, uptoId, null); + listCall.enqueue(cb); break; } case USER: { - api.accountStatuses(hashtagOrId, fromId, uptoId, null).enqueue(cb); + listCall = api.accountStatuses(hashtagOrId, fromId, uptoId, null); + listCall.enqueue(cb); break; } case FAVOURITES: { - api.favourites(fromId, uptoId, null).enqueue(cb); + listCall = api.favourites(fromId, uptoId, null); + listCall.enqueue(cb); break; } } From 4db1d54d79f1dd74eafd0eaa29c8be4d3b58220b Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 14 Mar 2017 12:59:52 +0100 Subject: [PATCH 2/5] Fix exception when bad domain on login screen --- .../keylesspalace/tusky/LoginActivity.java | 45 +++++++++++-------- app/src/main/res/values/strings.xml | 2 + 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/LoginActivity.java b/app/src/main/java/com/keylesspalace/tusky/LoginActivity.java index 2b473783b..91b05a321 100644 --- a/app/src/main/java/com/keylesspalace/tusky/LoginActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/LoginActivity.java @@ -22,7 +22,6 @@ import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; -import android.support.v7.widget.Toolbar; import android.view.View; import android.widget.Button; import android.widget.EditText; @@ -34,6 +33,8 @@ import com.keylesspalace.tusky.entity.AppCredentials; import java.util.HashMap; import java.util.Map; +import butterknife.BindView; +import butterknife.ButterKnife; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; @@ -45,10 +46,14 @@ public class LoginActivity extends BaseActivity { private static String OAUTH_SCOPES = "read write follow"; private SharedPreferences preferences; + private String domain; private String clientId; private String clientSecret; - private EditText editText; + + @BindView(R.id.edit_text_domain) EditText editText; + @BindView(R.id.button_login) Button button; + @BindView(R.id.no_account) TextView noAccount; /** * Chain together the key-value pairs into a query string, for either appending to a URL or @@ -117,6 +122,7 @@ public class LoginActivity extends BaseActivity { * time. */ String prefClientId = preferences.getString(domain + "/client_id", null); String prefClientSecret = preferences.getString(domain + "/client_secret", null); + if (prefClientId != null && prefClientSecret != null) { clientId = prefClientId; clientSecret = prefClientSecret; @@ -126,9 +132,7 @@ public class LoginActivity extends BaseActivity { @Override public void onResponse(Call call, Response response) { if (!response.isSuccessful()) { - editText.setError( - "This app could not obtain authentication from that server " + - "instance."); + editText.setError(getString(R.string.error_failed_app_registration)); Log.e(TAG, "App authentication failed. " + response.message()); return; } @@ -144,15 +148,17 @@ public class LoginActivity extends BaseActivity { @Override public void onFailure(Call call, Throwable t) { - editText.setError( - "This app could not obtain authentication from that server " + - "instance."); + editText.setError(getString(R.string.error_failed_app_registration)); t.printStackTrace(); } }; - getApiFor(domain).authenticateApp(getString(R.string.app_name), getOauthRedirectUri(), OAUTH_SCOPES, - getString(R.string.app_website)).enqueue(callback); + try { + getApiFor(domain).authenticateApp(getString(R.string.app_name), getOauthRedirectUri(), OAUTH_SCOPES, + getString(R.string.app_website)).enqueue(callback); + } catch (IllegalArgumentException e) { + editText.setError(getString(R.string.error_invalid_domain)); + } } } @@ -161,25 +167,26 @@ public class LoginActivity extends BaseActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); + ButterKnife.bind(this); + if (savedInstanceState != null) { domain = savedInstanceState.getString("domain"); clientId = savedInstanceState.getString("clientId"); clientSecret = savedInstanceState.getString("clientSecret"); } - Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); - setSupportActionBar(toolbar); + preferences = getSharedPreferences( getString(R.string.preferences_file_key), Context.MODE_PRIVATE); - Button button = (Button) findViewById(R.id.button_login); - editText = (EditText) findViewById(R.id.edit_text_domain); + button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onButtonClick(editText); } }); - TextView noAccount = (TextView) findViewById(R.id.no_account); + final Context context = this; + noAccount.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -233,11 +240,12 @@ public class LoginActivity extends BaseActivity { * redirect that was given to the server. If so, its response is here! */ Uri uri = getIntent().getData(); String redirectUri = getOauthRedirectUri(); + if (uri != null && uri.toString().startsWith(redirectUri)) { // This should either have returned an authorization code or an error. String code = uri.getQueryParameter("code"); String error = uri.getQueryParameter("error"); - final TextView errorText = (TextView) findViewById(R.id.text_error); + if (code != null) { /* During the redirect roundtrip this Activity usually dies, which wipes out the * instance variables, so they have to be recovered from where they were saved in @@ -264,15 +272,16 @@ public class LoginActivity extends BaseActivity { editText.setError(t.getMessage()); } }; + getApiFor(domain).fetchOAuthToken(clientId, clientSecret, redirectUri, code, "authorization_code").enqueue(callback); } else if (error != null) { /* Authorization failed. Put the error response where the user can read it and they * can try again. */ - errorText.setText(error); + editText.setError(error); } else { // This case means a junk response was received somehow. - errorText.setText(getString(R.string.error_authorization_unknown)); + editText.setError(getString(R.string.error_authorization_unknown)); } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 574ac06a6..1a3957b24 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -131,5 +131,7 @@ Mention http://tuskynotify.keylesspalace.com %s mentioned you + Invalid domain entered + This app could not obtain authentication from that server instance. From 2e50c547ced85748cfc8c87e8ef14cde6d37513b Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 14 Mar 2017 14:31:52 +0100 Subject: [PATCH 3/5] Group push notifications Clear notifications when opening MainActivity Use lowest privacy setting when replying --- .../keylesspalace/tusky/ComposeActivity.java | 13 +- .../com/keylesspalace/tusky/MainActivity.java | 15 +- .../tusky/MyFirebaseMessagingService.java | 158 ++++++++++++------ app/src/main/res/values/strings.xml | 11 +- 4 files changed, 139 insertions(+), 58 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java index 3fd55a93a..3b045c182 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java @@ -399,11 +399,18 @@ public class ComposeActivity extends BaseActivity { if (intent != null) { inReplyToId = intent.getStringExtra("in_reply_to_id"); String replyVisibility = intent.getStringExtra("reply_visibility"); + if (replyVisibility != null) { - /* Override any remembered visibilty and instead adopt the visibility of the status - * to which this replies. */ - statusVisibility = replyVisibility; + // Lowest possible visibility setting in response + if (statusVisibility.equals("private") || replyVisibility.equals("private")) { + statusVisibility = "private"; + } else if (statusVisibility.equals("unlisted") || replyVisibility.equals("unlisted")) { + statusVisibility = "unlisted"; + } else { + statusVisibility = replyVisibility; + } } + mentionedUsernames = intent.getStringArrayExtra("mentioned_usernames"); } diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java index bd54f6ad3..a752ce4ba 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java @@ -15,6 +15,7 @@ package com.keylesspalace.tusky; +import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -22,12 +23,12 @@ import android.graphics.PorterDuff; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Bundle; import android.os.PersistableBundle; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.TabLayout; import android.support.v4.content.ContextCompat; import android.support.v4.view.ViewPager; -import android.os.Bundle; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; @@ -187,6 +188,18 @@ public class MainActivity extends BaseActivity { }); } + @Override + protected void onResume() { + super.onResume(); + + SharedPreferences notificationPreferences = getApplicationContext().getSharedPreferences("Notifications", MODE_PRIVATE); + SharedPreferences.Editor editor = notificationPreferences.edit(); + editor.putString("current", "[]"); + editor.apply(); + + ((NotificationManager) (getSystemService(NOTIFICATION_SERVICE))).cancel(MyFirebaseMessagingService.NOTIFY_ID); + } + @Override public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) { ArrayList pageHistoryList = new ArrayList<>(); diff --git a/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java b/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java index 5d1f1e92a..3449f3f9e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java +++ b/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java @@ -22,6 +22,9 @@ 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; @@ -36,6 +39,7 @@ 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) { @@ -112,6 +116,34 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService { private void buildNotification(Notification body) { final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); + final SharedPreferences notificationPreferences = getApplicationContext().getSharedPreferences("Notifications", MODE_PRIVATE); + + 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); @@ -122,73 +154,103 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService { final NotificationCompat.Builder builder = new NotificationCompat.Builder(this) .setSmallIcon(R.drawable.ic_notify) - .setAutoCancel(true) .setContentIntent(resultPendingIntent) .setDefaults(0); // So it doesn't ring twice, notify only in Target callback - final Integer mId = (int)(System.currentTimeMillis() / 1000); + 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); + Target mTarget = new Target() { + @Override + public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { + builder.setLargeIcon(bitmap); - if (preferences.getBoolean("notificationAlertSound", true)) { - builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI); + 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); + } + + ((NotificationManager) (getSystemService(NOTIFICATION_SERVICE))).notify(NOTIFY_ID, builder.build()); } - if (preferences.getBoolean("notificationStyleVibrate", false)) { - builder.setVibrate(new long[] { 500, 500 }); + @Override + public void onBitmapFailed(Drawable errorDrawable) { + } - if (preferences.getBoolean("notificationStyleLight", false)) { - builder.setLights(0xFF00FF8F, 300, 1000); + @Override + public void onPrepareLoad(Drawable placeHolderDrawable) { + } + }; - ((NotificationManager) (getSystemService(NOTIFICATION_SERVICE))).notify(mId, builder.build()); + Picasso.with(this) + .load(body.account.avatar) + .placeholder(R.drawable.avatar_default) + .transform(new RoundedTransformation(7, 0)) + .into(mTarget); + } else { + try { + builder.setContentTitle(String.format(getString(R.string.notification_title_summary), currentNotifications.length())) + .setContentText(truncateWithEllipses(joinNames(currentNotifications), 40)); + } catch (JSONException e) { + e.printStackTrace(); } - - @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); + } 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(NOTIFY_ID, builder.build()); + } + + 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)); } - ((NotificationManager) (getSystemService(NOTIFICATION_SERVICE))).notify(mId, builder.build()); + 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/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1a3957b24..b2fac3348 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -102,12 +102,8 @@ Everyone can see, but not on public timelines Only followers and mentions can see - Allows Tusky to check for Mastodon notifications. - %d new mentions - Mention from %s - Notifications - Enable pull notifcations + Enable pull notifications Check for notifications periodically Check interval How often to pull @@ -133,5 +129,8 @@ %s mentioned you Invalid domain entered This app could not obtain authentication from that server instance. - + %1$s, %2$s, %3$s and %4$d others + %1$s, %2$s, and %3$s + %1$s and %2$s + %d new interactions From bc46afd801499c02a74db6180ff48b5169a9ff88 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 14 Mar 2017 15:27:22 +0100 Subject: [PATCH 4/5] Display lock icon on locked profiles --- .../keylesspalace/tusky/AccountActivity.java | 11 +++++++ app/src/main/res/layout/activity_account.xml | 30 ++++++++++++++----- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.java b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.java index a5ce6ab4e..22b417d11 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.java @@ -47,6 +47,8 @@ import com.squareup.picasso.Picasso; import java.util.ArrayList; import java.util.List; +import butterknife.BindView; +import butterknife.ButterKnife; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; @@ -63,10 +65,13 @@ public class AccountActivity extends BaseActivity { private TabLayout tabLayout; private Account loadedAccount; + @BindView(R.id.account_locked) ImageView accountLockedView; + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_account); + ButterKnife.bind(this); Intent intent = getIntent(); accountId = intent.getStringExtra("id"); @@ -193,6 +198,12 @@ public class AccountActivity extends BaseActivity { note.setLinksClickable(true); note.setMovementMethod(LinkMovementMethod.getInstance()); + if (account.locked) { + accountLockedView.setVisibility(View.VISIBLE); + } else { + accountLockedView.setVisibility(View.GONE); + } + Picasso.with(this) .load(account.avatar) .placeholder(R.drawable.avatar_default) diff --git a/app/src/main/res/layout/activity_account.xml b/app/src/main/res/layout/activity_account.xml index 32399cb1b..af884a60c 100644 --- a/app/src/main/res/layout/activity_account.xml +++ b/app/src/main/res/layout/activity_account.xml @@ -78,14 +78,30 @@ android:textColor="?android:textColorPrimary" android:textSize="18sp" /> - + + + + From ce82a81f0ef005be8a5990145c49d3b153d76172 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 14 Mar 2017 19:24:38 +0100 Subject: [PATCH 5/5] Fixed view media fragment behaviour, reblogs/favs of reblogs --- .../tusky/MyFirebaseMessagingService.java | 28 ++++++++----- .../com/keylesspalace/tusky/SFragment.java | 41 +++++++++++-------- .../tusky/ViewMediaFragment.java | 31 ++++++++++---- .../main/res/layout/fragment_view_media.xml | 7 ++-- app/src/main/res/values/styles.xml | 5 +++ 5 files changed, 75 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java b/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java index 3449f3f9e..9455a805f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java +++ b/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java @@ -166,17 +166,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService { public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { builder.setLargeIcon(bitmap); - 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); - } + setupPreferences(preferences, builder); ((NotificationManager) (getSystemService(NOTIFICATION_SERVICE))).notify(NOTIFY_ID, builder.build()); } @@ -198,6 +188,8 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService { .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)); @@ -214,6 +206,20 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService { ((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("notificationStyleVibrate", false)) { + builder.setVibrate(new long[] { 500, 500 }); + } + + if (preferences.getBoolean("notificationStyleLight", 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); diff --git a/app/src/main/java/com/keylesspalace/tusky/SFragment.java b/app/src/main/java/com/keylesspalace/tusky/SFragment.java index 10e5864d4..976797c8f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/SFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/SFragment.java @@ -20,8 +20,9 @@ import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; import android.support.v7.widget.PopupMenu; import android.support.v7.widget.RecyclerView; import android.text.Spanned; @@ -47,7 +48,6 @@ import retrofit2.Callback; public class SFragment extends Fragment { protected String loggedInAccountId; protected String loggedInUsername; - private MastodonAPI api; @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -57,7 +57,10 @@ public class SFragment extends Fragment { getString(R.string.preferences_file_key), Context.MODE_PRIVATE); loggedInAccountId = preferences.getString("loggedInAccountId", null); loggedInUsername = preferences.getString("loggedInAccountUsername", null); - api = ((BaseActivity) getActivity()).mastodonAPI; + } + + public MastodonAPI getApi() { + return ((BaseActivity) getActivity()).mastodonAPI; } protected void reply(Status status) { @@ -85,6 +88,11 @@ public class SFragment extends Fragment { public void onResponse(Call call, retrofit2.Response response) { if (response.isSuccessful()) { status.reblogged = reblog; + + if (status.reblog != null) { + status.reblog.reblogged = reblog; + } + adapter.notifyItemChanged(position); } } @@ -96,9 +104,9 @@ public class SFragment extends Fragment { }; if (reblog) { - api.reblogStatus(id).enqueue(cb); + getApi().reblogStatus(id).enqueue(cb); } else { - api.unreblogStatus(id).enqueue(cb); + getApi().unreblogStatus(id).enqueue(cb); } } @@ -111,6 +119,11 @@ public class SFragment extends Fragment { public void onResponse(Call call, retrofit2.Response response) { if (response.isSuccessful()) { status.favourited = favourite; + + if (status.reblog != null) { + status.reblog.favourited = favourite; + } + adapter.notifyItemChanged(position); } } @@ -122,14 +135,14 @@ public class SFragment extends Fragment { }; if (favourite) { - api.favouriteStatus(id).enqueue(cb); + getApi().favouriteStatus(id).enqueue(cb); } else { - api.unfavouriteStatus(id).enqueue(cb); + getApi().unfavouriteStatus(id).enqueue(cb); } } private void block(String id) { - api.blockAccount(id).enqueue(new Callback() { + getApi().blockAccount(id).enqueue(new Callback() { @Override public void onResponse(Call call, retrofit2.Response response) { @@ -143,7 +156,7 @@ public class SFragment extends Fragment { } private void delete(String id) { - api.deleteStatus(id).enqueue(new Callback() { + getApi().deleteStatus(id).enqueue(new Callback() { @Override public void onResponse(Call call, retrofit2.Response response) { @@ -206,13 +219,9 @@ public class SFragment extends Fragment { protected void viewMedia(String url, Status.MediaAttachment.Type type) { switch (type) { case IMAGE: { - Fragment newFragment = ViewMediaFragment.newInstance(url); - - FragmentManager manager = getFragmentManager(); - manager.beginTransaction() - .add(R.id.overlay_fragment_container, newFragment) - .addToBackStack(null) - .commit(); + DialogFragment newFragment = ViewMediaFragment.newInstance(url); + FragmentTransaction ft = getFragmentManager().beginTransaction(); + newFragment.show(ft, "view_media"); break; } case GIFV: diff --git a/app/src/main/java/com/keylesspalace/tusky/ViewMediaFragment.java b/app/src/main/java/com/keylesspalace/tusky/ViewMediaFragment.java index dd17c1310..433bdf272 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ViewMediaFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/ViewMediaFragment.java @@ -16,21 +16,27 @@ package com.keylesspalace.tusky; import android.os.Bundle; -import android.support.v4.app.Fragment; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.WindowManager; import com.squareup.picasso.Callback; import com.squareup.picasso.Picasso; +import butterknife.BindView; +import butterknife.ButterKnife; import uk.co.senab.photoview.PhotoView; import uk.co.senab.photoview.PhotoViewAttacher; -public class ViewMediaFragment extends Fragment { +public class ViewMediaFragment extends DialogFragment { private PhotoViewAttacher attacher; + @BindView(R.id.view_media_image) PhotoView photoView; + public static ViewMediaFragment newInstance(String url) { Bundle arguments = new Bundle(); ViewMediaFragment fragment = new ViewMediaFragment(); @@ -39,14 +45,29 @@ public class ViewMediaFragment extends Fragment { return fragment; } + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setStyle(DialogFragment.STYLE_NORMAL, R.style.Dialog_FullScreen); + } + + @Override + public void onResume() { + ViewGroup.LayoutParams params = getDialog().getWindow().getAttributes(); + params.width = WindowManager.LayoutParams.MATCH_PARENT; + params.height = WindowManager.LayoutParams.MATCH_PARENT; + getDialog().getWindow().setAttributes((android.view.WindowManager.LayoutParams) params); + super.onResume(); + } + @Override public View onCreateView(LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_view_media, container, false); + ButterKnife.bind(this, rootView); Bundle arguments = getArguments(); String url = arguments.getString("url"); - PhotoView photoView = (PhotoView) rootView.findViewById(R.id.view_media_image); attacher = new PhotoViewAttacher(photoView); @@ -84,8 +105,4 @@ public class ViewMediaFragment extends Fragment { attacher.cleanup(); super.onDestroyView(); } - - private void dismiss() { - getFragmentManager().popBackStack(); - } } diff --git a/app/src/main/res/layout/fragment_view_media.xml b/app/src/main/res/layout/fragment_view_media.xml index d3b84def2..ea1918fca 100644 --- a/app/src/main/res/layout/fragment_view_media.xml +++ b/app/src/main/res/layout/fragment_view_media.xml @@ -1,11 +1,12 @@ - + android:layout_gravity="center" + android:background="@android:color/black"> - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 4e651e201..e38552849 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -92,6 +92,11 @@ true + +