Change locked accounts to default visibility to "followers-only", and reorganizes the composer because it was getting cluttered.
This commit is contained in:
parent
b6cf99602b
commit
ab76121692
@ -40,11 +40,11 @@ import android.os.Parcel;
|
|||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
import android.provider.OpenableColumns;
|
import android.provider.OpenableColumns;
|
||||||
|
import android.support.annotation.AttrRes;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.StringRes;
|
import android.support.annotation.StringRes;
|
||||||
import android.support.design.widget.Snackbar;
|
import android.support.design.widget.Snackbar;
|
||||||
import android.support.v13.view.inputmethod.EditorInfoCompat;
|
|
||||||
import android.support.v13.view.inputmethod.InputConnectionCompat;
|
import android.support.v13.view.inputmethod.InputConnectionCompat;
|
||||||
import android.support.v13.view.inputmethod.InputContentInfoCompat;
|
import android.support.v13.view.inputmethod.InputContentInfoCompat;
|
||||||
import android.support.v4.app.ActivityCompat;
|
import android.support.v4.app.ActivityCompat;
|
||||||
@ -54,17 +54,9 @@ import android.support.v7.app.ActionBar;
|
|||||||
import android.support.v7.content.res.AppCompatResources;
|
import android.support.v7.content.res.AppCompatResources;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.InputType;
|
|
||||||
import android.text.Spannable;
|
|
||||||
import android.text.Spanned;
|
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.text.style.ForegroundColorSpan;
|
|
||||||
import android.view.Gravity;
|
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.inputmethod.EditorInfo;
|
|
||||||
import android.view.inputmethod.InputConnection;
|
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
@ -72,7 +64,6 @@ import android.widget.ImageButton;
|
|||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.RelativeLayout;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.keylesspalace.tusky.entity.Media;
|
import com.keylesspalace.tusky.entity.Media;
|
||||||
@ -92,6 +83,8 @@ import java.util.List;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
import okhttp3.MediaType;
|
import okhttp3.MediaType;
|
||||||
import okhttp3.MultipartBody;
|
import okhttp3.MultipartBody;
|
||||||
import okhttp3.RequestBody;
|
import okhttp3.RequestBody;
|
||||||
@ -110,28 +103,29 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
|
|||||||
private static final int THUMBNAIL_SIZE = 128; // pixels
|
private static final int THUMBNAIL_SIZE = 128; // pixels
|
||||||
|
|
||||||
private String inReplyToId;
|
private String inReplyToId;
|
||||||
private EditText textEditor;
|
|
||||||
private LinearLayout mediaPreviewBar;
|
|
||||||
private ArrayList<QueuedMedia> mediaQueued;
|
private ArrayList<QueuedMedia> mediaQueued;
|
||||||
private CountUpDownLatch waitForMediaLatch;
|
private CountUpDownLatch waitForMediaLatch;
|
||||||
private boolean showMarkSensitive;
|
private boolean showMarkSensitive;
|
||||||
private String statusVisibility; // The current values of the options that will be applied
|
private String statusVisibility; // The current values of the options that will be applied
|
||||||
private boolean statusMarkSensitive; // to the status being composed.
|
private boolean statusMarkSensitive; // to the status being composed.
|
||||||
private boolean statusHideText; //
|
private boolean statusHideText; //
|
||||||
private View contentWarningBar;
|
|
||||||
private boolean statusAlreadyInFlight; // to prevent duplicate sends by mashing the send button
|
private boolean statusAlreadyInFlight; // to prevent duplicate sends by mashing the send button
|
||||||
private InputContentInfoCompat currentInputContentInfo;
|
private InputContentInfoCompat currentInputContentInfo;
|
||||||
private int currentFlags;
|
private int currentFlags;
|
||||||
private ProgressDialog finishingUploadDialog;
|
|
||||||
private EditText contentWarningEditor;
|
|
||||||
private TextView charactersLeft;
|
|
||||||
private Button floatingBtn;
|
|
||||||
private ImageButton pickBtn;
|
|
||||||
private ImageButton takeBtn;
|
|
||||||
private Button nsfwBtn;
|
|
||||||
private ProgressBar postProgress;
|
|
||||||
private ImageButton visibilityBtn;
|
|
||||||
private Uri photoUploadUri;
|
private Uri photoUploadUri;
|
||||||
|
// this only exists when a status is trying to be sent, but uploads are still occurring
|
||||||
|
private ProgressDialog finishingUploadDialog;
|
||||||
|
@BindView(R.id.compose_edit_field) EditTextTyped textEditor;
|
||||||
|
@BindView(R.id.compose_media_preview_bar) LinearLayout mediaPreviewBar;
|
||||||
|
@BindView(R.id.compose_content_warning_bar) View contentWarningBar;
|
||||||
|
@BindView(R.id.field_content_warning) EditText contentWarningEditor;
|
||||||
|
@BindView(R.id.characters_left) TextView charactersLeft;
|
||||||
|
@BindView(R.id.floating_btn) Button floatingBtn;
|
||||||
|
@BindView(R.id.compose_photo_pick) ImageButton pickBtn;
|
||||||
|
@BindView(R.id.compose_photo_take) ImageButton takeBtn;
|
||||||
|
@BindView(R.id.action_toggle_nsfw) Button nsfwBtn;
|
||||||
|
@BindView(R.id.postProgress) ProgressBar postProgress;
|
||||||
|
@BindView(R.id.action_toggle_visibility) ImageButton visibilityBtn;
|
||||||
|
|
||||||
private static class QueuedMedia {
|
private static class QueuedMedia {
|
||||||
enum Type {
|
enum Type {
|
||||||
@ -207,135 +201,16 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doErrorDialog(@StringRes int descriptionId, @StringRes int actionId,
|
|
||||||
View.OnClickListener listener) {
|
|
||||||
Snackbar bar = Snackbar.make(findViewById(R.id.activity_compose), getString(descriptionId),
|
|
||||||
Snackbar.LENGTH_SHORT);
|
|
||||||
bar.setAction(actionId, listener);
|
|
||||||
bar.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void displayTransientError(@StringRes int stringId) {
|
|
||||||
Snackbar.make(findViewById(R.id.activity_compose), stringId, Snackbar.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class FindCharsResult {
|
|
||||||
int charIndex;
|
|
||||||
int stringIndex;
|
|
||||||
|
|
||||||
FindCharsResult() {
|
|
||||||
charIndex = -1;
|
|
||||||
stringIndex = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static FindCharsResult findChars(String string, int fromIndex, char[] chars) {
|
|
||||||
FindCharsResult result = new FindCharsResult();
|
|
||||||
final int length = string.length();
|
|
||||||
for (int i = fromIndex; i < length; i++) {
|
|
||||||
char c = string.charAt(i);
|
|
||||||
for (int j = 0; j < chars.length; j++) {
|
|
||||||
if (chars[j] == c) {
|
|
||||||
result.charIndex = j;
|
|
||||||
result.stringIndex = i;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static FindCharsResult findStart(String string, int fromIndex, char[] chars) {
|
|
||||||
final int length = string.length();
|
|
||||||
while (fromIndex < length) {
|
|
||||||
FindCharsResult found = findChars(string, fromIndex, chars);
|
|
||||||
int i = found.stringIndex;
|
|
||||||
if (i < 0) {
|
|
||||||
break;
|
|
||||||
} else if (i == 0 || i >= 1 && Character.isWhitespace(string.codePointBefore(i))) {
|
|
||||||
return found;
|
|
||||||
} else {
|
|
||||||
fromIndex = i + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new FindCharsResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int findEndOfHashtag(String string, int fromIndex) {
|
|
||||||
final int length = string.length();
|
|
||||||
for (int i = fromIndex + 1; i < length;) {
|
|
||||||
int codepoint = string.codePointAt(i);
|
|
||||||
if (Character.isWhitespace(codepoint)) {
|
|
||||||
return i;
|
|
||||||
} else if (codepoint == '#') {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
i += Character.charCount(codepoint);
|
|
||||||
}
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int findEndOfMention(String string, int fromIndex) {
|
|
||||||
int atCount = 0;
|
|
||||||
final int length = string.length();
|
|
||||||
for (int i = fromIndex + 1; i < length;) {
|
|
||||||
int codepoint = string.codePointAt(i);
|
|
||||||
if (Character.isWhitespace(codepoint)) {
|
|
||||||
return i;
|
|
||||||
} else if (codepoint == '@') {
|
|
||||||
atCount += 1;
|
|
||||||
if (atCount >= 2) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i += Character.charCount(codepoint);
|
|
||||||
}
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void highlightSpans(Spannable text, int colour) {
|
|
||||||
// Strip all existing colour spans.
|
|
||||||
int n = text.length();
|
|
||||||
ForegroundColorSpan[] oldSpans = text.getSpans(0, n, ForegroundColorSpan.class);
|
|
||||||
for (int i = oldSpans.length - 1; i >= 0; i--) {
|
|
||||||
text.removeSpan(oldSpans[i]);
|
|
||||||
}
|
|
||||||
// Colour the mentions and hashtags.
|
|
||||||
String string = text.toString();
|
|
||||||
int start;
|
|
||||||
int end = 0;
|
|
||||||
while (end < n) {
|
|
||||||
char[] chars = { '#', '@' };
|
|
||||||
FindCharsResult found = findStart(string, end, chars);
|
|
||||||
start = found.stringIndex;
|
|
||||||
if (start < 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (found.charIndex == 0) {
|
|
||||||
end = findEndOfHashtag(string, start);
|
|
||||||
} else if (found.charIndex == 1) {
|
|
||||||
end = findEndOfMention(string, start);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (end < 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
text.setSpan(new ForegroundColorSpan(colour), start, end,
|
|
||||||
Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_compose);
|
setContentView(R.layout.activity_compose);
|
||||||
|
ButterKnife.bind(this);
|
||||||
|
|
||||||
|
// Setup the toolbar.
|
||||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
ActionBar actionBar = getSupportActionBar();
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
|
||||||
if (actionBar != null) {
|
if (actionBar != null) {
|
||||||
actionBar.setTitle(null);
|
actionBar.setTitle(null);
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
@ -345,18 +220,11 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
|
|||||||
actionBar.setHomeAsUpIndicator(closeIcon);
|
actionBar.setHomeAsUpIndicator(closeIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedPreferences preferences = getPrivatePreferences();
|
// Setup the interface buttons.
|
||||||
|
|
||||||
floatingBtn = (Button) findViewById(R.id.floating_btn);
|
|
||||||
pickBtn = (ImageButton) findViewById(R.id.compose_photo_pick);
|
|
||||||
takeBtn = (ImageButton) findViewById(R.id.compose_photo_take);
|
|
||||||
nsfwBtn = (Button) findViewById(R.id.action_toggle_nsfw);
|
|
||||||
visibilityBtn = (ImageButton) findViewById(R.id.action_toggle_visibility);
|
|
||||||
|
|
||||||
floatingBtn.setOnClickListener(new View.OnClickListener() {
|
floatingBtn.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
sendStatus();
|
prepareStatus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
pickBtn.setOnClickListener(new View.OnClickListener() {
|
pickBtn.setOnClickListener(new View.OnClickListener() {
|
||||||
@ -384,7 +252,9 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Intent intent = getIntent();
|
/* Initialise all the state, or restore it from a previous run, to determine a "starting"
|
||||||
|
* state. */
|
||||||
|
SharedPreferences preferences = getPrivatePreferences();
|
||||||
|
|
||||||
String startingVisibility;
|
String startingVisibility;
|
||||||
boolean startingHideText;
|
boolean startingHideText;
|
||||||
@ -411,9 +281,9 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
|
|||||||
startingHideText = false;
|
startingHideText = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
postProgress = (ProgressBar) findViewById(R.id.postProgress);
|
/* If the composer is started up as a reply to another post, override the "starting" state
|
||||||
postProgress.setVisibility(View.INVISIBLE);
|
* based on what the intent from the reply request passes. */
|
||||||
updateNsfwButtonColor();
|
Intent intent = getIntent();
|
||||||
|
|
||||||
String[] mentionedUsernames = null;
|
String[] mentionedUsernames = null;
|
||||||
inReplyToId = null;
|
inReplyToId = null;
|
||||||
@ -434,30 +304,29 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
|
|||||||
|
|
||||||
mentionedUsernames = intent.getStringArrayExtra("mentioned_usernames");
|
mentionedUsernames = intent.getStringArrayExtra("mentioned_usernames");
|
||||||
|
|
||||||
if(inReplyToId != null) {
|
if (inReplyToId != null) {
|
||||||
startingHideText = !intent.getStringExtra("content_warning").equals("");
|
startingHideText = !intent.getStringExtra("content_warning").equals("");
|
||||||
if(startingHideText){
|
if (startingHideText) {
|
||||||
startingContentWarning = intent.getStringExtra("content_warning");
|
startingContentWarning = intent.getStringExtra("content_warning");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Only after the starting visibility is determined and the send button is initialised can
|
|
||||||
* the status visibility be set. */
|
|
||||||
setStatusVisibility(startingVisibility);
|
|
||||||
|
|
||||||
textEditor = createEditText(null); // new String[] { "image/gif", "image/webp" }
|
/* If the currently logged in account is locked, its posts should default to private. This
|
||||||
final int mentionColour = ThemeUtils.getColor(this, R.attr.compose_mention_color);
|
* should override even the reply settings, so this must be done after those are set up. */
|
||||||
if (savedInstanceState != null) {
|
if (preferences.getBoolean("loggedInAccountLocked", false)) {
|
||||||
restoreTextEditorState(savedInstanceState.getParcelable("textEditorState"));
|
startingVisibility = "private";
|
||||||
highlightSpans(textEditor.getText(), mentionColour);
|
|
||||||
}
|
}
|
||||||
RelativeLayout editArea = (RelativeLayout) findViewById(R.id.compose_edit_area);
|
|
||||||
/* Adding this at index zero because it implicitly gives it the lowest input priority. So,
|
// After the starting state is finalised, the interface can be set to reflect this state.
|
||||||
* when media previews are added in front of the editor, they can receive click events
|
setStatusVisibility(startingVisibility);
|
||||||
* without the text editor stealing the events from behind them. */
|
postProgress.setVisibility(View.INVISIBLE);
|
||||||
editArea.addView(textEditor, 0);
|
updateNsfwButtonColor();
|
||||||
contentWarningEditor = (EditText) findViewById(R.id.field_content_warning);
|
|
||||||
charactersLeft = (TextView) findViewById(R.id.characters_left);
|
// Setup the main text field.
|
||||||
|
setEditTextMimeTypes(null); // new String[] { "image/gif", "image/webp" }
|
||||||
|
final int mentionColour = ThemeUtils.getColor(this, R.attr.compose_mention_color);
|
||||||
|
SpanUtils.highlightSpans(textEditor.getText(), mentionColour);
|
||||||
textEditor.addTextChangedListener(new TextWatcher() {
|
textEditor.addTextChangedListener(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) {
|
||||||
@ -469,10 +338,11 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterTextChanged(Editable editable) {
|
public void afterTextChanged(Editable editable) {
|
||||||
highlightSpans(editable, mentionColour);
|
SpanUtils.highlightSpans(editable, mentionColour);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add any mentions to the text field when a reply is first composed.
|
||||||
if (mentionedUsernames != null) {
|
if (mentionedUsernames != null) {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
for (String name : mentionedUsernames) {
|
for (String name : mentionedUsernames) {
|
||||||
@ -484,11 +354,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
|
|||||||
textEditor.setSelection(textEditor.length());
|
textEditor.setSelection(textEditor.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaPreviewBar = (LinearLayout) findViewById(R.id.compose_media_preview_bar);
|
// Initialise the content warning editor.
|
||||||
mediaQueued = new ArrayList<>();
|
|
||||||
waitForMediaLatch = new CountUpDownLatch();
|
|
||||||
|
|
||||||
contentWarningBar = findViewById(R.id.compose_content_warning_bar);
|
|
||||||
contentWarningEditor.addTextChangedListener(new TextWatcher() {
|
contentWarningEditor.addTextChangedListener(new TextWatcher() {
|
||||||
@Override
|
@Override
|
||||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
@ -502,11 +368,13 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
|
|||||||
public void afterTextChanged(Editable s) {}
|
public void afterTextChanged(Editable s) {}
|
||||||
});
|
});
|
||||||
showContentWarning(startingHideText);
|
showContentWarning(startingHideText);
|
||||||
|
|
||||||
if(startingContentWarning != null){
|
if(startingContentWarning != null){
|
||||||
contentWarningEditor.setText(startingContentWarning);
|
contentWarningEditor.setText(startingContentWarning);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialise the empty media queue state.
|
||||||
|
mediaQueued = new ArrayList<>();
|
||||||
|
waitForMediaLatch = new CountUpDownLatch();
|
||||||
statusAlreadyInFlight = false;
|
statusAlreadyInFlight = false;
|
||||||
|
|
||||||
// These can only be added after everything affected by the media queue is initialized.
|
// These can only be added after everything affected by the media queue is initialized.
|
||||||
@ -564,17 +432,53 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSaveInstanceState(Bundle outState) {
|
||||||
|
ArrayList<SavedQueuedMedia> savedMediaQueued = new ArrayList<>();
|
||||||
|
for (QueuedMedia item : mediaQueued) {
|
||||||
|
savedMediaQueued.add(new SavedQueuedMedia(item.type, item.uri, item.preview,
|
||||||
|
item.mediaSize));
|
||||||
|
}
|
||||||
|
outState.putParcelableArrayList("savedMediaQueued", savedMediaQueued);
|
||||||
|
outState.putBoolean("showMarkSensitive", showMarkSensitive);
|
||||||
|
outState.putString("statusVisibility", statusVisibility);
|
||||||
|
outState.putBoolean("statusMarkSensitive", statusMarkSensitive);
|
||||||
|
outState.putBoolean("statusHideText", statusHideText);
|
||||||
|
if (currentInputContentInfo != null) {
|
||||||
|
outState.putParcelable("commitContentInputContentInfo",
|
||||||
|
(Parcelable) currentInputContentInfo.unwrap());
|
||||||
|
outState.putInt("commitContentFlags", currentFlags);
|
||||||
|
}
|
||||||
|
currentInputContentInfo = null;
|
||||||
|
currentFlags = 0;
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doErrorDialog(@StringRes int descriptionId, @StringRes int actionId,
|
||||||
|
View.OnClickListener listener) {
|
||||||
|
Snackbar bar = Snackbar.make(findViewById(R.id.activity_compose), getString(descriptionId),
|
||||||
|
Snackbar.LENGTH_SHORT);
|
||||||
|
bar.setAction(actionId, listener);
|
||||||
|
bar.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void displayTransientError(@StringRes int stringId) {
|
||||||
|
Snackbar.make(findViewById(R.id.activity_compose), stringId, Snackbar.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
private void toggleNsfw() {
|
private void toggleNsfw() {
|
||||||
statusMarkSensitive = !statusMarkSensitive;
|
statusMarkSensitive = !statusMarkSensitive;
|
||||||
updateNsfwButtonColor();
|
updateNsfwButtonColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateNsfwButtonColor() {
|
private void updateNsfwButtonColor() {
|
||||||
|
@AttrRes int attribute;
|
||||||
if (statusMarkSensitive) {
|
if (statusMarkSensitive) {
|
||||||
nsfwBtn.setTextColor(ThemeUtils.getColor(this, R.attr.compose_nsfw_button_selected_color));
|
attribute = R.attr.compose_nsfw_button_selected_color;
|
||||||
} else {
|
} else {
|
||||||
nsfwBtn.setTextColor(ThemeUtils.getColor(this, R.attr.compose_nsfw_button_color));
|
attribute = R.attr.compose_nsfw_button_color;
|
||||||
}
|
}
|
||||||
|
nsfwBtn.setTextColor(ThemeUtils.getColor(this, attribute));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void disableButtons() {
|
private void disableButtons() {
|
||||||
@ -680,7 +584,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
|
|||||||
enableButtons();
|
enableButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendStatus() {
|
private void prepareStatus() {
|
||||||
if (statusAlreadyInFlight) {
|
if (statusAlreadyInFlight) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -700,51 +604,6 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onSaveInstanceState(Bundle outState) {
|
|
||||||
ArrayList<SavedQueuedMedia> savedMediaQueued = new ArrayList<>();
|
|
||||||
for (QueuedMedia item : mediaQueued) {
|
|
||||||
savedMediaQueued.add(new SavedQueuedMedia(item.type, item.uri, item.preview,
|
|
||||||
item.mediaSize));
|
|
||||||
}
|
|
||||||
outState.putParcelableArrayList("savedMediaQueued", savedMediaQueued);
|
|
||||||
outState.putBoolean("showMarkSensitive", showMarkSensitive);
|
|
||||||
outState.putString("statusVisibility", statusVisibility);
|
|
||||||
outState.putBoolean("statusMarkSensitive", statusMarkSensitive);
|
|
||||||
outState.putBoolean("statusHideText", statusHideText);
|
|
||||||
outState.putParcelable("textEditorState", saveTextEditorState());
|
|
||||||
if (currentInputContentInfo != null) {
|
|
||||||
outState.putParcelable("commitContentInputContentInfo",
|
|
||||||
(Parcelable) currentInputContentInfo.unwrap());
|
|
||||||
outState.putInt("commitContentFlags", currentFlags);
|
|
||||||
}
|
|
||||||
currentInputContentInfo = null;
|
|
||||||
currentFlags = 0;
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Parcelable saveTextEditorState() {
|
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
bundle.putString("text", textEditor.getText().toString());
|
|
||||||
bundle.putInt("selectionStart", textEditor.getSelectionStart());
|
|
||||||
bundle.putInt("selectionEnd", textEditor.getSelectionEnd());
|
|
||||||
return bundle;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void restoreTextEditorState(Parcelable state) {
|
|
||||||
Bundle bundle = (Bundle) state;
|
|
||||||
textEditor.setText(bundle.getString("text"));
|
|
||||||
int start = bundle.getInt("selectionStart");
|
|
||||||
int end = bundle.getInt("selectionEnd");
|
|
||||||
if (start != -1) {
|
|
||||||
if (end != -1) {
|
|
||||||
textEditor.setSelection(start, end);
|
|
||||||
} else {
|
|
||||||
textEditor.setSelection(start);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStop() {
|
protected void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
@ -758,39 +617,21 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
|
|||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
private EditText createEditText(String[] contentMimeTypes) {
|
private void setEditTextMimeTypes(String[] contentMimeTypes) {
|
||||||
final String[] mimeTypes;
|
final String[] mimeTypes;
|
||||||
if (contentMimeTypes == null || contentMimeTypes.length == 0) {
|
if (contentMimeTypes == null || contentMimeTypes.length == 0) {
|
||||||
mimeTypes = new String[0];
|
mimeTypes = new String[0];
|
||||||
} else {
|
} else {
|
||||||
mimeTypes = Arrays.copyOf(contentMimeTypes, contentMimeTypes.length);
|
mimeTypes = Arrays.copyOf(contentMimeTypes, contentMimeTypes.length);
|
||||||
}
|
}
|
||||||
EditText editText = new android.support.v7.widget.AppCompatEditText(this) {
|
textEditor.setMimeTypes(mimeTypes, new InputConnectionCompat.OnCommitContentListener() {
|
||||||
@Override
|
@Override
|
||||||
public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
|
public boolean onCommitContent(InputContentInfoCompat inputContentInfo,
|
||||||
final InputConnection ic = super.onCreateInputConnection(editorInfo);
|
int flags, Bundle opts) {
|
||||||
EditorInfoCompat.setContentMimeTypes(editorInfo, mimeTypes);
|
return ComposeActivity.this.onCommitContent(inputContentInfo, flags,
|
||||||
final InputConnectionCompat.OnCommitContentListener callback =
|
mimeTypes);
|
||||||
new InputConnectionCompat.OnCommitContentListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onCommitContent(InputContentInfoCompat inputContentInfo,
|
|
||||||
int flags, Bundle opts) {
|
|
||||||
return ComposeActivity.this.onCommitContent(inputContentInfo, flags,
|
|
||||||
mimeTypes);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return InputConnectionCompat.createWrapper(ic, editorInfo, callback);
|
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
|
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
|
||||||
editText.setLayoutParams(layoutParams);
|
|
||||||
editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES);
|
|
||||||
editText.setEms(10);
|
|
||||||
editText.setBackgroundColor(0);
|
|
||||||
editText.setGravity(Gravity.START | Gravity.TOP);
|
|
||||||
editText.setHint(R.string.hint_compose);
|
|
||||||
return editText;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags,
|
private boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags,
|
||||||
|
56
app/src/main/java/com/keylesspalace/tusky/EditTextTyped.java
Normal file
56
app/src/main/java/com/keylesspalace/tusky/EditTextTyped.java
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/* Copyright 2017 Andrew Dawson
|
||||||
|
*
|
||||||
|
* This file is a part of Tusky.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||||
|
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||||
|
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||||
|
* Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.v13.view.inputmethod.EditorInfoCompat;
|
||||||
|
import android.support.v13.view.inputmethod.InputConnectionCompat;
|
||||||
|
import android.support.v7.widget.AppCompatEditText;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.view.inputmethod.InputConnection;
|
||||||
|
|
||||||
|
public class EditTextTyped extends AppCompatEditText {
|
||||||
|
InputConnectionCompat.OnCommitContentListener onCommitContentListener;
|
||||||
|
String[] mimeTypes;
|
||||||
|
|
||||||
|
public EditTextTyped(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EditTextTyped(Context context, AttributeSet attributeSet) {
|
||||||
|
super(context, attributeSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMimeTypes(String[] types,
|
||||||
|
InputConnectionCompat.OnCommitContentListener listener) {
|
||||||
|
mimeTypes = types;
|
||||||
|
onCommitContentListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
|
||||||
|
InputConnection connection = super.onCreateInputConnection(editorInfo);
|
||||||
|
if (onCommitContentListener != null) {
|
||||||
|
Assert.expect(mimeTypes != null);
|
||||||
|
EditorInfoCompat.setContentMimeTypes(editorInfo, mimeTypes);
|
||||||
|
return InputConnectionCompat.createWrapper(connection, editorInfo,
|
||||||
|
onCommitContentListener);
|
||||||
|
} else {
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -502,6 +502,7 @@ public class MainActivity extends BaseActivity implements SFragment.OnUserRemove
|
|||||||
getPrivatePreferences().edit()
|
getPrivatePreferences().edit()
|
||||||
.putString("loggedInAccountId", loggedInAccountId)
|
.putString("loggedInAccountId", loggedInAccountId)
|
||||||
.putString("loggedInAccountUsername", loggedInAccountUsername)
|
.putString("loggedInAccountUsername", loggedInAccountUsername)
|
||||||
|
.putBoolean("loggedInAccountLocked", me.locked)
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
129
app/src/main/java/com/keylesspalace/tusky/SpanUtils.java
Normal file
129
app/src/main/java/com/keylesspalace/tusky/SpanUtils.java
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/* Copyright 2017 Andrew Dawson
|
||||||
|
*
|
||||||
|
* This file is a part of Tusky.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||||
|
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||||
|
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||||
|
* Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky;
|
||||||
|
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.Spanned;
|
||||||
|
import android.text.style.ForegroundColorSpan;
|
||||||
|
|
||||||
|
class SpanUtils {
|
||||||
|
private static class FindCharsResult {
|
||||||
|
int charIndex;
|
||||||
|
int stringIndex;
|
||||||
|
|
||||||
|
FindCharsResult() {
|
||||||
|
charIndex = -1;
|
||||||
|
stringIndex = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FindCharsResult findChars(String string, int fromIndex, char[] chars) {
|
||||||
|
FindCharsResult result = new FindCharsResult();
|
||||||
|
final int length = string.length();
|
||||||
|
for (int i = fromIndex; i < length; i++) {
|
||||||
|
char c = string.charAt(i);
|
||||||
|
for (int j = 0; j < chars.length; j++) {
|
||||||
|
if (chars[j] == c) {
|
||||||
|
result.charIndex = j;
|
||||||
|
result.stringIndex = i;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FindCharsResult findStart(String string, int fromIndex, char[] chars) {
|
||||||
|
final int length = string.length();
|
||||||
|
while (fromIndex < length) {
|
||||||
|
FindCharsResult found = findChars(string, fromIndex, chars);
|
||||||
|
int i = found.stringIndex;
|
||||||
|
if (i < 0) {
|
||||||
|
break;
|
||||||
|
} else if (i == 0 || i >= 1 && Character.isWhitespace(string.codePointBefore(i))) {
|
||||||
|
return found;
|
||||||
|
} else {
|
||||||
|
fromIndex = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new FindCharsResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int findEndOfHashtag(String string, int fromIndex) {
|
||||||
|
final int length = string.length();
|
||||||
|
for (int i = fromIndex + 1; i < length;) {
|
||||||
|
int codepoint = string.codePointAt(i);
|
||||||
|
if (Character.isWhitespace(codepoint)) {
|
||||||
|
return i;
|
||||||
|
} else if (codepoint == '#') {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
i += Character.charCount(codepoint);
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int findEndOfMention(String string, int fromIndex) {
|
||||||
|
int atCount = 0;
|
||||||
|
final int length = string.length();
|
||||||
|
for (int i = fromIndex + 1; i < length;) {
|
||||||
|
int codepoint = string.codePointAt(i);
|
||||||
|
if (Character.isWhitespace(codepoint)) {
|
||||||
|
return i;
|
||||||
|
} else if (codepoint == '@') {
|
||||||
|
atCount += 1;
|
||||||
|
if (atCount >= 2) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += Character.charCount(codepoint);
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void highlightSpans(Spannable text, int colour) {
|
||||||
|
// Strip all existing colour spans.
|
||||||
|
int n = text.length();
|
||||||
|
ForegroundColorSpan[] oldSpans = text.getSpans(0, n, ForegroundColorSpan.class);
|
||||||
|
for (int i = oldSpans.length - 1; i >= 0; i--) {
|
||||||
|
text.removeSpan(oldSpans[i]);
|
||||||
|
}
|
||||||
|
// Colour the mentions and hashtags.
|
||||||
|
String string = text.toString();
|
||||||
|
int start;
|
||||||
|
int end = 0;
|
||||||
|
while (end < n) {
|
||||||
|
char[] chars = { '#', '@' };
|
||||||
|
FindCharsResult found = findStart(string, end, chars);
|
||||||
|
start = found.stringIndex;
|
||||||
|
if (start < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (found.charIndex == 0) {
|
||||||
|
end = findEndOfHashtag(string, start);
|
||||||
|
} else if (found.charIndex == 1) {
|
||||||
|
end = findEndOfMention(string, start);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (end < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
text.setSpan(new ForegroundColorSpan(colour), start, end,
|
||||||
|
Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -47,7 +47,6 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/compose_edit_area"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
@ -55,8 +54,15 @@
|
|||||||
android:paddingLeft="16dp"
|
android:paddingLeft="16dp"
|
||||||
android:paddingRight="16dp">
|
android:paddingRight="16dp">
|
||||||
|
|
||||||
<!--An special EditText is created at runtime here, because it has to be a modified
|
<com.keylesspalace.tusky.EditTextTyped
|
||||||
* anonymous class to support image/GIF picking from the soft keyboard.-->
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:id="@+id/compose_edit_field"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:ems="10"
|
||||||
|
android:gravity="start|top"
|
||||||
|
android:hint="@string/hint_compose"
|
||||||
|
android:inputType="text|textMultiLine|textCapSentences" />
|
||||||
|
|
||||||
<HorizontalScrollView
|
<HorizontalScrollView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -137,7 +137,7 @@
|
|||||||
|
|
||||||
<string name="visibility_public">Public: Post to public timelines</string>
|
<string name="visibility_public">Public: Post to public timelines</string>
|
||||||
<string name="visibility_unlisted">Unlisted: Do not show in public timelines</string>
|
<string name="visibility_unlisted">Unlisted: Do not show in public timelines</string>
|
||||||
<string name="visibility_private">Private: Post to followers only</string>
|
<string name="visibility_private">Followers-Only: Post to followers only</string>
|
||||||
<string name="visibility_direct">Direct: Post to mentioned users only</string>
|
<string name="visibility_direct">Direct: Post to mentioned users only</string>
|
||||||
|
|
||||||
<string name="pref_title_notification_settings">Notifications</string>
|
<string name="pref_title_notification_settings">Notifications</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user