Fixes rare crashes when viewing accounts and favouriting. Also, fixes content warning characters not counting toward the character limit. Closes #32
This commit is contained in:
parent
263d586a51
commit
f2a400ab38
10 changed files with 88 additions and 31 deletions
|
@ -61,7 +61,6 @@ public class AccountActivity extends BaseActivity {
|
||||||
private boolean blocking = false;
|
private boolean blocking = false;
|
||||||
private boolean muting = false;
|
private boolean muting = false;
|
||||||
private boolean isSelf;
|
private boolean isSelf;
|
||||||
private String openInWebUrl;
|
|
||||||
private TabLayout tabLayout;
|
private TabLayout tabLayout;
|
||||||
private Account loadedAccount;
|
private Account loadedAccount;
|
||||||
|
|
||||||
|
@ -214,7 +213,6 @@ public class AccountActivity extends BaseActivity {
|
||||||
.placeholder(R.drawable.account_header_missing)
|
.placeholder(R.drawable.account_header_missing)
|
||||||
.into(header);
|
.into(header);
|
||||||
|
|
||||||
openInWebUrl = account.url;
|
|
||||||
java.text.NumberFormat nf = java.text.NumberFormat.getInstance();
|
java.text.NumberFormat nf = java.text.NumberFormat.getInstance();
|
||||||
|
|
||||||
// Add counts to the tabs in the TabLayout.
|
// Add counts to the tabs in the TabLayout.
|
||||||
|
@ -489,7 +487,10 @@ public class AccountActivity extends BaseActivity {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case R.id.action_open_in_web: {
|
case R.id.action_open_in_web: {
|
||||||
Uri uri = Uri.parse(openInWebUrl);
|
if (loadedAccount == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Uri uri = Uri.parse(loadedAccount.url);
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -21,7 +21,6 @@ import android.graphics.drawable.Drawable;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.design.widget.TabLayout;
|
import android.support.design.widget.TabLayout;
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v7.widget.DividerItemDecoration;
|
import android.support.v7.widget.DividerItemDecoration;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
|
|
@ -22,6 +22,7 @@ import android.graphics.Color;
|
||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
|
@ -50,6 +51,8 @@ import retrofit2.converter.gson.GsonConverterFactory;
|
||||||
* the most expedient way to accomplish this was to put it in a base class and just have every
|
* the most expedient way to accomplish this was to put it in a base class and just have every
|
||||||
* activity extend from it. */
|
* activity extend from it. */
|
||||||
public class BaseActivity extends AppCompatActivity {
|
public class BaseActivity extends AppCompatActivity {
|
||||||
|
private static final String TAG = "BaseActivity"; // logging tag
|
||||||
|
|
||||||
protected MastodonAPI mastodonAPI;
|
protected MastodonAPI mastodonAPI;
|
||||||
protected TuskyAPI tuskyAPI;
|
protected TuskyAPI tuskyAPI;
|
||||||
protected Dispatcher mastodonApiDispatcher;
|
protected Dispatcher mastodonApiDispatcher;
|
||||||
|
@ -101,7 +104,7 @@ public class BaseActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean arePushNotificationsEnabled() {
|
protected boolean arePushNotificationsEnabled() {
|
||||||
SharedPreferences preferences = getSharedPreferences(getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
return preferences.getBoolean("notificationsEnabled", true);
|
return preferences.getBoolean("notificationsEnabled", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,12 +178,12 @@ public class BaseActivity extends AppCompatActivity {
|
||||||
tuskyAPI.register(getBaseUrl(), getAccessToken(), FirebaseInstanceId.getInstance().getToken()).enqueue(new Callback<ResponseBody>() {
|
tuskyAPI.register(getBaseUrl(), getAccessToken(), FirebaseInstanceId.getInstance().getToken()).enqueue(new Callback<ResponseBody>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
|
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
|
||||||
|
Log.d(TAG, "Enable push notifications response: " + response.message());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Call<ResponseBody> call, Throwable t) {
|
public void onFailure(Call<ResponseBody> call, Throwable t) {
|
||||||
|
Log.d(TAG, "Enable push notifications failed: " + t.getMessage());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -189,12 +192,12 @@ public class BaseActivity extends AppCompatActivity {
|
||||||
tuskyAPI.unregister(getBaseUrl(), getAccessToken()).enqueue(new Callback<ResponseBody>() {
|
tuskyAPI.unregister(getBaseUrl(), getAccessToken()).enqueue(new Callback<ResponseBody>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
|
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
|
||||||
|
Log.d(TAG, "Disable push notifications response: " + response.message());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Call<ResponseBody> call, Throwable t) {
|
public void onFailure(Call<ResponseBody> call, Throwable t) {
|
||||||
|
Log.d(TAG, "Disable push notifications failed: " + t.getMessage());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -422,7 +422,7 @@ public class ComposeActivity extends BaseActivity {
|
||||||
TextWatcher textEditorWatcher = new TextWatcher() {
|
TextWatcher textEditorWatcher = new TextWatcher() {
|
||||||
@Override
|
@Override
|
||||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
int left = STATUS_CHARACTER_LIMIT - s.length();
|
int left = STATUS_CHARACTER_LIMIT - s.length() - contentWarningEditor.length();
|
||||||
charactersLeft.setText(String.format(Locale.getDefault(), "%d", left));
|
charactersLeft.setText(String.format(Locale.getDefault(), "%d", left));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,6 +453,19 @@ public class ComposeActivity extends BaseActivity {
|
||||||
|
|
||||||
contentWarningBar = findViewById(R.id.compose_content_warning_bar);
|
contentWarningBar = findViewById(R.id.compose_content_warning_bar);
|
||||||
contentWarningEditor = (EditText) findViewById(R.id.field_content_warning);
|
contentWarningEditor = (EditText) findViewById(R.id.field_content_warning);
|
||||||
|
contentWarningEditor.addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
int left = STATUS_CHARACTER_LIMIT - s.length() - textEditor.length();
|
||||||
|
charactersLeft.setText(String.format(Locale.getDefault(), "%d", left));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {}
|
||||||
|
});
|
||||||
showContentWarning(false);
|
showContentWarning(false);
|
||||||
|
|
||||||
statusAlreadyInFlight = false;
|
statusAlreadyInFlight = false;
|
||||||
|
@ -504,15 +517,14 @@ public class ComposeActivity extends BaseActivity {
|
||||||
if (statusAlreadyInFlight) {
|
if (statusAlreadyInFlight) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Editable editable = textEditor.getText();
|
String contentText = textEditor.getText().toString();
|
||||||
if (editable.length() <= STATUS_CHARACTER_LIMIT) {
|
String spoilerText = "";
|
||||||
|
if (statusHideText) {
|
||||||
|
spoilerText = contentWarningEditor.getText().toString();
|
||||||
|
}
|
||||||
|
if (contentText.length() + spoilerText.length() <= STATUS_CHARACTER_LIMIT) {
|
||||||
statusAlreadyInFlight = true;
|
statusAlreadyInFlight = true;
|
||||||
String spoilerText = "";
|
readyStatus(contentText, statusVisibility, statusMarkSensitive, spoilerText);
|
||||||
if (statusHideText) {
|
|
||||||
spoilerText = contentWarningEditor.getText().toString();
|
|
||||||
}
|
|
||||||
readyStatus(editable.toString(), statusVisibility, statusMarkSensitive,
|
|
||||||
spoilerText);
|
|
||||||
} else {
|
} else {
|
||||||
textEditor.setError(getString(R.string.error_compose_character_limit));
|
textEditor.setError(getString(R.string.error_compose_character_limit));
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ import android.os.Build;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
|
|
||||||
class HtmlUtils {
|
public class HtmlUtils {
|
||||||
private static CharSequence trimTrailingWhitespace(CharSequence s) {
|
private static CharSequence trimTrailingWhitespace(CharSequence s) {
|
||||||
int i = s.length();
|
int i = s.length();
|
||||||
do {
|
do {
|
||||||
|
@ -29,7 +29,7 @@ class HtmlUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
static Spanned fromHtml(String html) {
|
public static Spanned fromHtml(String html) {
|
||||||
Spanned result;
|
Spanned result;
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
result = Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY);
|
result = Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY);
|
||||||
|
@ -42,7 +42,7 @@ class HtmlUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
static String toHtml(Spanned text) {
|
public static String toHtml(Spanned text) {
|
||||||
String result;
|
String result;
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
result = Html.toHtml(text, Html.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE);
|
result = Html.toHtml(text, Html.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE);
|
||||||
|
|
|
@ -55,9 +55,9 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
|
||||||
|
|
||||||
Log.d(TAG, notificationId);
|
Log.d(TAG, notificationId);
|
||||||
|
|
||||||
SharedPreferences preferences = getSharedPreferences(getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(
|
||||||
|
getApplicationContext());
|
||||||
boolean enabled = preferences.getBoolean("notificationsEnabled", true);
|
boolean enabled = preferences.getBoolean("notificationsEnabled", true);
|
||||||
|
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -306,25 +306,37 @@ class StatusViewHolder extends RecyclerView.ViewHolder {
|
||||||
replyButton.setOnClickListener(new View.OnClickListener() {
|
replyButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
listener.onReply(getAdapterPosition());
|
int position = getAdapterPosition();
|
||||||
|
if (position != RecyclerView.NO_POSITION) {
|
||||||
|
listener.onReply(position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
reblogButton.setEventListener(new SparkEventListener() {
|
reblogButton.setEventListener(new SparkEventListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onEvent(ImageView button, boolean buttonState) {
|
public void onEvent(ImageView button, boolean buttonState) {
|
||||||
listener.onReblog(!reblogged, getAdapterPosition());
|
int position = getAdapterPosition();
|
||||||
|
if (position != RecyclerView.NO_POSITION) {
|
||||||
|
listener.onReblog(!reblogged, position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
favouriteButton.setEventListener(new SparkEventListener() {
|
favouriteButton.setEventListener(new SparkEventListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onEvent(ImageView button, boolean buttonState) {
|
public void onEvent(ImageView button, boolean buttonState) {
|
||||||
listener.onFavourite(!favourited, getAdapterPosition());
|
int position = getAdapterPosition();
|
||||||
|
if (position != RecyclerView.NO_POSITION) {
|
||||||
|
listener.onFavourite(!favourited, position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
moreButton.setOnClickListener(new View.OnClickListener() {
|
moreButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
listener.onMore(v, getAdapterPosition());
|
int position = getAdapterPosition();
|
||||||
|
if (position != RecyclerView.NO_POSITION) {
|
||||||
|
listener.onMore(v, position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
/* Even though the content TextView is a child of the container, it won't respond to clicks
|
/* Even though the content TextView is a child of the container, it won't respond to clicks
|
||||||
|
@ -334,7 +346,10 @@ class StatusViewHolder extends RecyclerView.ViewHolder {
|
||||||
View.OnClickListener viewThreadListener = new View.OnClickListener() {
|
View.OnClickListener viewThreadListener = new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
listener.onViewThread(getAdapterPosition());
|
int position = getAdapterPosition();
|
||||||
|
if (position != RecyclerView.NO_POSITION) {
|
||||||
|
listener.onViewThread(position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
content.setOnClickListener(viewThreadListener);
|
content.setOnClickListener(viewThreadListener);
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
package com.keylesspalace.tusky;
|
package com.keylesspalace.tusky;
|
||||||
|
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
|
@ -116,7 +116,8 @@ class TimelineAdapter extends RecyclerView.Adapter implements AdapterItemRemover
|
||||||
notifyItemRemoved(position);
|
notifyItemRemoved(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable Status getItem(int position) {
|
@Nullable
|
||||||
|
Status getItem(int position) {
|
||||||
if (position >= 0 && position < statuses.size()) {
|
if (position >= 0 && position < statuses.size()) {
|
||||||
return statuses.get(position);
|
return statuses.get(position);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import android.text.Spanned;
|
||||||
|
|
||||||
import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion;
|
import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion;
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import com.keylesspalace.tusky.HtmlUtils;
|
||||||
|
|
||||||
public class Account implements SearchSuggestion {
|
public class Account implements SearchSuggestion {
|
||||||
public String id;
|
public String id;
|
||||||
|
@ -88,7 +89,18 @@ public class Account implements SearchSuggestion {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeString(id);
|
||||||
|
dest.writeString(localUsername);
|
||||||
|
dest.writeString(username);
|
||||||
|
dest.writeString(displayName);
|
||||||
|
dest.writeString(HtmlUtils.toHtml(note));
|
||||||
|
dest.writeString(url);
|
||||||
|
dest.writeString(avatar);
|
||||||
|
dest.writeString(header);
|
||||||
|
dest.writeBooleanArray(new boolean[] { locked });
|
||||||
|
dest.writeString(followersCount);
|
||||||
|
dest.writeString(followingCount);
|
||||||
|
dest.writeString(statusesCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account() {
|
public Account() {
|
||||||
|
@ -96,7 +108,20 @@ public class Account implements SearchSuggestion {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Account(Parcel in) {
|
protected Account(Parcel in) {
|
||||||
|
id = in.readString();
|
||||||
|
localUsername = in.readString();
|
||||||
|
username = in.readString();
|
||||||
|
displayName = in.readString();
|
||||||
|
note = HtmlUtils.fromHtml(in.readString());
|
||||||
|
url = in.readString();
|
||||||
|
avatar = in.readString();
|
||||||
|
header = in.readString();
|
||||||
|
boolean[] lockedArray = new boolean[1];
|
||||||
|
in.readBooleanArray(lockedArray);
|
||||||
|
locked = lockedArray[0];
|
||||||
|
followersCount = in.readString();
|
||||||
|
followingCount = in.readString();
|
||||||
|
statusesCount = in.readString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Creator<Account> CREATOR = new Creator<Account>() {
|
public static final Creator<Account> CREATOR = new Creator<Account>() {
|
||||||
|
|
Loading…
Reference in a new issue