Change locked accounts to default visibility to "followers-only", and reorganizes the composer because it was getting cluttered.

This commit is contained in:
Vavassor 2017-05-02 18:17:54 -04:00
parent b6cf99602b
commit ab76121692
6 changed files with 290 additions and 257 deletions

View File

@ -40,11 +40,11 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.support.annotation.AttrRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
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.InputContentInfoCompat;
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.widget.Toolbar;
import android.text.Editable;
import android.text.InputType;
import android.text.Spannable;
import android.text.Spanned;
import android.text.TextWatcher;
import android.text.style.ForegroundColorSpan;
import android.view.Gravity;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.webkit.MimeTypeMap;
import android.widget.Button;
import android.widget.EditText;
@ -72,7 +64,6 @@ import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.keylesspalace.tusky.entity.Media;
@ -92,6 +83,8 @@ import java.util.List;
import java.util.Locale;
import java.util.Random;
import butterknife.BindView;
import butterknife.ButterKnife;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
@ -110,28 +103,29 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
private static final int THUMBNAIL_SIZE = 128; // pixels
private String inReplyToId;
private EditText textEditor;
private LinearLayout mediaPreviewBar;
private ArrayList<QueuedMedia> mediaQueued;
private CountUpDownLatch waitForMediaLatch;
private boolean showMarkSensitive;
private String statusVisibility; // The current values of the options that will be applied
private boolean statusMarkSensitive; // to the status being composed.
private boolean statusHideText; //
private View contentWarningBar;
private boolean statusAlreadyInFlight; // to prevent duplicate sends by mashing the send button
private InputContentInfoCompat currentInputContentInfo;
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;
// 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 {
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
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_compose);
ButterKnife.bind(this);
// Setup the toolbar.
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(null);
actionBar.setDisplayHomeAsUpEnabled(true);
@ -345,18 +220,11 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
actionBar.setHomeAsUpIndicator(closeIcon);
}
SharedPreferences preferences = getPrivatePreferences();
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);
// Setup the interface buttons.
floatingBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sendStatus();
prepareStatus();
}
});
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;
boolean startingHideText;
@ -411,9 +281,9 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
startingHideText = false;
}
postProgress = (ProgressBar) findViewById(R.id.postProgress);
postProgress.setVisibility(View.INVISIBLE);
updateNsfwButtonColor();
/* If the composer is started up as a reply to another post, override the "starting" state
* based on what the intent from the reply request passes. */
Intent intent = getIntent();
String[] mentionedUsernames = null;
inReplyToId = null;
@ -434,30 +304,29 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
mentionedUsernames = intent.getStringArrayExtra("mentioned_usernames");
if(inReplyToId != null) {
if (inReplyToId != null) {
startingHideText = !intent.getStringExtra("content_warning").equals("");
if(startingHideText){
if (startingHideText) {
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" }
final int mentionColour = ThemeUtils.getColor(this, R.attr.compose_mention_color);
if (savedInstanceState != null) {
restoreTextEditorState(savedInstanceState.getParcelable("textEditorState"));
highlightSpans(textEditor.getText(), mentionColour);
/* If the currently logged in account is locked, its posts should default to private. This
* should override even the reply settings, so this must be done after those are set up. */
if (preferences.getBoolean("loggedInAccountLocked", false)) {
startingVisibility = "private";
}
RelativeLayout editArea = (RelativeLayout) findViewById(R.id.compose_edit_area);
/* Adding this at index zero because it implicitly gives it the lowest input priority. So,
* when media previews are added in front of the editor, they can receive click events
* without the text editor stealing the events from behind them. */
editArea.addView(textEditor, 0);
contentWarningEditor = (EditText) findViewById(R.id.field_content_warning);
charactersLeft = (TextView) findViewById(R.id.characters_left);
// After the starting state is finalised, the interface can be set to reflect this state.
setStatusVisibility(startingVisibility);
postProgress.setVisibility(View.INVISIBLE);
updateNsfwButtonColor();
// 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() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
@ -469,10 +338,11 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
@Override
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) {
StringBuilder builder = new StringBuilder();
for (String name : mentionedUsernames) {
@ -484,11 +354,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
textEditor.setSelection(textEditor.length());
}
mediaPreviewBar = (LinearLayout) findViewById(R.id.compose_media_preview_bar);
mediaQueued = new ArrayList<>();
waitForMediaLatch = new CountUpDownLatch();
contentWarningBar = findViewById(R.id.compose_content_warning_bar);
// Initialise the content warning editor.
contentWarningEditor.addTextChangedListener(new TextWatcher() {
@Override
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) {}
});
showContentWarning(startingHideText);
if(startingContentWarning != null){
contentWarningEditor.setText(startingContentWarning);
}
// Initialise the empty media queue state.
mediaQueued = new ArrayList<>();
waitForMediaLatch = new CountUpDownLatch();
statusAlreadyInFlight = false;
// 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() {
statusMarkSensitive = !statusMarkSensitive;
updateNsfwButtonColor();
}
private void updateNsfwButtonColor() {
@AttrRes int attribute;
if (statusMarkSensitive) {
nsfwBtn.setTextColor(ThemeUtils.getColor(this, R.attr.compose_nsfw_button_selected_color));
attribute = R.attr.compose_nsfw_button_selected_color;
} 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() {
@ -680,7 +584,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
enableButtons();
}
private void sendStatus() {
private void prepareStatus() {
if (statusAlreadyInFlight) {
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
protected void onStop() {
super.onStop();
@ -758,39 +617,21 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag
.apply();
}
private EditText createEditText(String[] contentMimeTypes) {
private void setEditTextMimeTypes(String[] contentMimeTypes) {
final String[] mimeTypes;
if (contentMimeTypes == null || contentMimeTypes.length == 0) {
mimeTypes = new String[0];
} else {
mimeTypes = Arrays.copyOf(contentMimeTypes, contentMimeTypes.length);
}
EditText editText = new android.support.v7.widget.AppCompatEditText(this) {
textEditor.setMimeTypes(mimeTypes, new InputConnectionCompat.OnCommitContentListener() {
@Override
public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
final InputConnection ic = super.onCreateInputConnection(editorInfo);
EditorInfoCompat.setContentMimeTypes(editorInfo, mimeTypes);
final InputConnectionCompat.OnCommitContentListener callback =
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);
public boolean onCommitContent(InputContentInfoCompat inputContentInfo,
int flags, Bundle opts) {
return ComposeActivity.this.onCommitContent(inputContentInfo, flags,
mimeTypes);
}
};
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,

View 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;
}
}
}

View File

@ -502,6 +502,7 @@ public class MainActivity extends BaseActivity implements SFragment.OnUserRemove
getPrivatePreferences().edit()
.putString("loggedInAccountId", loggedInAccountId)
.putString("loggedInAccountUsername", loggedInAccountUsername)
.putBoolean("loggedInAccountLocked", me.locked)
.apply();
}

View 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);
}
}
}

View File

@ -47,7 +47,6 @@
</LinearLayout>
<RelativeLayout
android:id="@+id/compose_edit_area"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
@ -55,8 +54,15 @@
android:paddingLeft="16dp"
android:paddingRight="16dp">
<!--An special EditText is created at runtime here, because it has to be a modified
* anonymous class to support image/GIF picking from the soft keyboard.-->
<com.keylesspalace.tusky.EditTextTyped
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
android:layout_width="match_parent"

View File

@ -137,7 +137,7 @@
<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_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="pref_title_notification_settings">Notifications</string>