mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-20 22:59:54 +01:00
[android] Re-factor TextInputViews
This commit is contained in:
@@ -19,7 +19,7 @@ import org.briarproject.briar.android.controller.handler.UiExceptionHandler;
|
|||||||
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
|
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
import org.briarproject.briar.android.view.TextInputView;
|
import org.briarproject.briar.android.view.TextInputView;
|
||||||
import org.briarproject.briar.android.view.TextInputView.TextInputListener;
|
import org.briarproject.briar.android.view.TextInputView.SendListener;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -32,10 +32,11 @@ import static android.view.View.INVISIBLE;
|
|||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
|
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
|
||||||
import static org.briarproject.briar.android.blog.BasePostFragment.POST_ID;
|
import static org.briarproject.briar.android.blog.BasePostFragment.POST_ID;
|
||||||
|
import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_TEXT_LENGTH;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class ReblogFragment extends BaseFragment implements TextInputListener {
|
public class ReblogFragment extends BaseFragment implements SendListener {
|
||||||
|
|
||||||
public static final String TAG = ReblogFragment.class.getName();
|
public static final String TAG = ReblogFragment.class.getName();
|
||||||
|
|
||||||
@@ -80,7 +81,8 @@ public class ReblogFragment extends BaseFragment implements TextInputListener {
|
|||||||
View v = inflater.inflate(R.layout.fragment_reblog, container, false);
|
View v = inflater.inflate(R.layout.fragment_reblog, container, false);
|
||||||
ui = new ViewHolder(v);
|
ui = new ViewHolder(v);
|
||||||
ui.post.setTransitionName(postId);
|
ui.post.setTransitionName(postId);
|
||||||
ui.input.setSendButtonEnabled(false);
|
ui.input.setEnabled(false);
|
||||||
|
ui.input.setMaxTextLength(MAX_BLOG_POST_TEXT_LENGTH);
|
||||||
showProgressBar();
|
showProgressBar();
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
@@ -116,7 +118,7 @@ public class ReblogFragment extends BaseFragment implements TextInputListener {
|
|||||||
ui.post.hideReblogButton();
|
ui.post.hideReblogButton();
|
||||||
|
|
||||||
ui.input.setListener(this);
|
ui.input.setListener(this);
|
||||||
ui.input.setSendButtonEnabled(true);
|
ui.input.setEnabled(true);
|
||||||
ui.scrollView.post(() -> ui.scrollView.fullScroll(FOCUS_DOWN));
|
ui.scrollView.post(() -> ui.scrollView.fullScroll(FOCUS_DOWN));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import android.content.Intent;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.text.Editable;
|
|
||||||
import android.text.TextWatcher;
|
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
@@ -23,7 +21,7 @@ import org.briarproject.briar.R;
|
|||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.activity.BriarActivity;
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
import org.briarproject.briar.android.view.TextInputView;
|
import org.briarproject.briar.android.view.TextInputView;
|
||||||
import org.briarproject.briar.android.view.TextInputView.TextInputListener;
|
import org.briarproject.briar.android.view.TextInputView.SendListener;
|
||||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||||
import org.briarproject.briar.api.blog.BlogManager;
|
import org.briarproject.briar.api.blog.BlogManager;
|
||||||
import org.briarproject.briar.api.blog.BlogPost;
|
import org.briarproject.briar.api.blog.BlogPost;
|
||||||
@@ -40,13 +38,12 @@ import static android.view.View.VISIBLE;
|
|||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||||
import static org.briarproject.bramble.util.StringUtils.truncateUtf8;
|
|
||||||
import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_TEXT_LENGTH;
|
import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_TEXT_LENGTH;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class WriteBlogPostActivity extends BriarActivity
|
public class WriteBlogPostActivity extends BriarActivity
|
||||||
implements OnEditorActionListener, TextInputListener {
|
implements OnEditorActionListener, SendListener {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(WriteBlogPostActivity.class.getName());
|
Logger.getLogger(WriteBlogPostActivity.class.getName());
|
||||||
@@ -78,23 +75,7 @@ public class WriteBlogPostActivity extends BriarActivity
|
|||||||
setContentView(R.layout.activity_write_blog_post);
|
setContentView(R.layout.activity_write_blog_post);
|
||||||
|
|
||||||
input = findViewById(R.id.textInput);
|
input = findViewById(R.id.textInput);
|
||||||
input.setSendButtonEnabled(false);
|
input.setMaxTextLength(MAX_BLOG_POST_TEXT_LENGTH);
|
||||||
input.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) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterTextChanged(Editable s) {
|
|
||||||
enableOrDisablePublishButton();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
input.setListener(this);
|
input.setListener(this);
|
||||||
|
|
||||||
progressBar = findViewById(R.id.progressBar);
|
progressBar = findViewById(R.id.progressBar);
|
||||||
@@ -134,20 +115,15 @@ public class WriteBlogPostActivity extends BriarActivity
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enableOrDisablePublishButton() {
|
|
||||||
input.setSendButtonEnabled(!input.isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSendClick(@Nullable String text, List<Uri> imageUris) {
|
public void onSendClick(@Nullable String text, List<Uri> imageUris) {
|
||||||
if (isNullOrEmpty(text)) return;
|
if (isNullOrEmpty(text)) throw new AssertionError();
|
||||||
|
|
||||||
// hide publish button, show progress bar
|
// hide publish button, show progress bar
|
||||||
input.hideSoftKeyboard();
|
input.hideSoftKeyboard();
|
||||||
input.setVisibility(GONE);
|
input.setVisibility(GONE);
|
||||||
progressBar.setVisibility(VISIBLE);
|
progressBar.setVisibility(VISIBLE);
|
||||||
|
|
||||||
text = truncateUtf8(text, MAX_BLOG_POST_TEXT_LENGTH);
|
|
||||||
storePost(text);
|
storePost(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
|
|||||||
import org.briarproject.briar.android.view.BriarRecyclerView;
|
import org.briarproject.briar.android.view.BriarRecyclerView;
|
||||||
import org.briarproject.briar.android.view.TextInputView;
|
import org.briarproject.briar.android.view.TextInputView;
|
||||||
import org.briarproject.briar.android.view.TextInputView.AttachImageListener;
|
import org.briarproject.briar.android.view.TextInputView.AttachImageListener;
|
||||||
import org.briarproject.briar.android.view.TextInputView.TextInputListener;
|
import org.briarproject.briar.android.view.TextInputView.SendListener;
|
||||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||||
import org.briarproject.briar.api.blog.BlogSharingManager;
|
import org.briarproject.briar.api.blog.BlogSharingManager;
|
||||||
import org.briarproject.briar.api.client.ProtocolStateException;
|
import org.briarproject.briar.api.client.ProtocolStateException;
|
||||||
@@ -118,7 +118,6 @@ import static org.briarproject.bramble.util.LogUtils.logDuration;
|
|||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.LogUtils.now;
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||||
import static org.briarproject.bramble.util.StringUtils.truncateUtf8;
|
|
||||||
import static org.briarproject.briar.android.TestingConstants.FEATURE_FLAG_IMAGE_ATTACHMENTS;
|
import static org.briarproject.briar.android.TestingConstants.FEATURE_FLAG_IMAGE_ATTACHMENTS;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_ATTACH_IMAGE;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_ATTACH_IMAGE;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_INTRODUCTION;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_INTRODUCTION;
|
||||||
@@ -136,7 +135,7 @@ import static uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.S
|
|||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class ConversationActivity extends BriarActivity
|
public class ConversationActivity extends BriarActivity
|
||||||
implements EventListener, ConversationListener, TextInputListener,
|
implements EventListener, ConversationListener, SendListener,
|
||||||
TextCache, AttachmentCache, AttachImageListener {
|
TextCache, AttachmentCache, AttachImageListener {
|
||||||
|
|
||||||
public static final String CONTACT_ID = "briar.CONTACT_ID";
|
public static final String CONTACT_ID = "briar.CONTACT_ID";
|
||||||
@@ -256,6 +255,8 @@ public class ConversationActivity extends BriarActivity
|
|||||||
list.setEmptyText(getString(R.string.no_private_messages));
|
list.setEmptyText(getString(R.string.no_private_messages));
|
||||||
|
|
||||||
textInputView = findViewById(R.id.text_input_container);
|
textInputView = findViewById(R.id.text_input_container);
|
||||||
|
textInputView.setMaxTextLength(MAX_PRIVATE_MESSAGE_TEXT_LENGTH);
|
||||||
|
textInputView.setEnabled(false);
|
||||||
textInputView.setListener(this);
|
textInputView.setListener(this);
|
||||||
if (FEATURE_FLAG_IMAGE_ATTACHMENTS) {
|
if (FEATURE_FLAG_IMAGE_ATTACHMENTS) {
|
||||||
textInputView.setAttachImageListener(this);
|
textInputView.setAttachImageListener(this);
|
||||||
@@ -416,7 +417,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
runOnUiThreadUnlessDestroyed(() -> {
|
runOnUiThreadUnlessDestroyed(() -> {
|
||||||
if (revision == adapter.getRevision()) {
|
if (revision == adapter.getRevision()) {
|
||||||
adapter.incrementRevision();
|
adapter.incrementRevision();
|
||||||
textInputView.setSendButtonEnabled(true);
|
textInputView.setEnabled(true);
|
||||||
List<ConversationItem> items = createItems(headers);
|
List<ConversationItem> items = createItems(headers);
|
||||||
adapter.addAll(items);
|
adapter.addAll(items);
|
||||||
list.showData();
|
list.showData();
|
||||||
@@ -592,16 +593,15 @@ public class ConversationActivity extends BriarActivity
|
|||||||
public void onSendClick(@Nullable String text, List<Uri> imageUris) {
|
public void onSendClick(@Nullable String text, List<Uri> imageUris) {
|
||||||
if (!imageUris.isEmpty()) {
|
if (!imageUris.isEmpty()) {
|
||||||
Toast.makeText(this, "Not yet implemented.", LENGTH_LONG).show();
|
Toast.makeText(this, "Not yet implemented.", LENGTH_LONG).show();
|
||||||
textInputView.setText("");
|
textInputView.clearText();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isNullOrEmpty(text)) return;
|
if (isNullOrEmpty(text)) throw new AssertionError();
|
||||||
text = truncateUtf8(text, MAX_PRIVATE_MESSAGE_TEXT_LENGTH);
|
|
||||||
long timestamp = System.currentTimeMillis();
|
long timestamp = System.currentTimeMillis();
|
||||||
timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
|
timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
|
||||||
if (messagingGroupId == null) loadGroupId(text, timestamp);
|
if (messagingGroupId == null) loadGroupId(text, timestamp);
|
||||||
else createMessage(text, timestamp);
|
else createMessage(text, timestamp);
|
||||||
textInputView.setText("");
|
textInputView.clearText();
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getMinTimestampForNewMessage() {
|
private long getMinTimestampForNewMessage() {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import org.briarproject.briar.R;
|
|||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
import org.briarproject.briar.android.view.TextInputView;
|
import org.briarproject.briar.android.view.TextInputView;
|
||||||
import org.briarproject.briar.android.view.TextInputView.TextInputListener;
|
import org.briarproject.briar.android.view.TextInputView.SendListener;
|
||||||
import org.briarproject.briar.api.introduction.IntroductionManager;
|
import org.briarproject.briar.api.introduction.IntroductionManager;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -40,14 +40,13 @@ import static android.view.View.VISIBLE;
|
|||||||
import static android.widget.Toast.LENGTH_SHORT;
|
import static android.widget.Toast.LENGTH_SHORT;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.StringUtils.truncateUtf8;
|
|
||||||
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
|
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_TEXT_LENGTH;
|
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_TEXT_LENGTH;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class IntroductionMessageFragment extends BaseFragment
|
public class IntroductionMessageFragment extends BaseFragment
|
||||||
implements TextInputListener {
|
implements SendListener {
|
||||||
|
|
||||||
public static final String TAG =
|
public static final String TAG =
|
||||||
IntroductionMessageFragment.class.getName();
|
IntroductionMessageFragment.class.getName();
|
||||||
@@ -103,7 +102,8 @@ public class IntroductionMessageFragment extends BaseFragment
|
|||||||
View v = inflater.inflate(R.layout.introduction_message, container,
|
View v = inflater.inflate(R.layout.introduction_message, container,
|
||||||
false);
|
false);
|
||||||
ui = new ViewHolder(v);
|
ui = new ViewHolder(v);
|
||||||
ui.message.setSendButtonEnabled(false);
|
ui.message.setMaxTextLength(MAX_INTRODUCTION_TEXT_LENGTH);
|
||||||
|
ui.message.setEnabled(false);
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
@@ -168,7 +168,7 @@ public class IntroductionMessageFragment extends BaseFragment
|
|||||||
// show views
|
// show views
|
||||||
ui.notPossible.setVisibility(GONE);
|
ui.notPossible.setVisibility(GONE);
|
||||||
ui.message.setVisibility(VISIBLE);
|
ui.message.setVisibility(VISIBLE);
|
||||||
ui.message.setSendButtonEnabled(true);
|
ui.message.setEnabled(true);
|
||||||
ui.message.showSoftKeyboard();
|
ui.message.showSoftKeyboard();
|
||||||
} else {
|
} else {
|
||||||
ui.notPossible.setVisibility(VISIBLE);
|
ui.notPossible.setVisibility(VISIBLE);
|
||||||
@@ -192,11 +192,8 @@ public class IntroductionMessageFragment extends BaseFragment
|
|||||||
@Override
|
@Override
|
||||||
public void onSendClick(@Nullable String text, List<Uri> imageUris) {
|
public void onSendClick(@Nullable String text, List<Uri> imageUris) {
|
||||||
// disable button to prevent accidental double invitations
|
// disable button to prevent accidental double invitations
|
||||||
ui.message.setSendButtonEnabled(false);
|
ui.message.setEnabled(false);
|
||||||
|
|
||||||
if (text != null) {
|
|
||||||
text = truncateUtf8(text, MAX_INTRODUCTION_TEXT_LENGTH);
|
|
||||||
}
|
|
||||||
makeIntroduction(contact1, contact2, text);
|
makeIntroduction(contact1, contact2, text);
|
||||||
|
|
||||||
// don't wait for the introduction to be made before finishing activity
|
// don't wait for the introduction to be made before finishing activity
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ public class GroupActivity extends
|
|||||||
|
|
||||||
private void setGroupEnabled(boolean enabled) {
|
private void setGroupEnabled(boolean enabled) {
|
||||||
isDissolved = !enabled;
|
isDissolved = !enabled;
|
||||||
textInput.setSendButtonEnabled(enabled);
|
textInput.setEnabled(enabled);
|
||||||
list.getRecyclerView().setAlpha(enabled ? 1f : 0.5f);
|
list.getRecyclerView().setAlpha(enabled ? 1f : 0.5f);
|
||||||
|
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.briarproject.briar.android.privategroup.creation;
|
package org.briarproject.briar.android.privategroup.creation;
|
||||||
|
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
@@ -18,6 +20,7 @@ public interface CreateGroupController
|
|||||||
ResultExceptionHandler<GroupId, DbException> result);
|
ResultExceptionHandler<GroupId, DbException> result);
|
||||||
|
|
||||||
void sendInvitation(GroupId g, Collection<ContactId> contacts,
|
void sendInvitation(GroupId g, Collection<ContactId> contacts,
|
||||||
String text, ResultExceptionHandler<Void, DbException> result);
|
@Nullable String text,
|
||||||
|
ResultExceptionHandler<Void, DbException> result);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.briarproject.briar.android.privategroup.creation;
|
package org.briarproject.briar.android.privategroup.creation;
|
||||||
|
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
@@ -123,7 +125,8 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendInvitation(GroupId g, Collection<ContactId> contactIds,
|
public void sendInvitation(GroupId g, Collection<ContactId> contactIds,
|
||||||
String text, ResultExceptionHandler<Void, DbException> handler) {
|
@Nullable String text,
|
||||||
|
ResultExceptionHandler<Void, DbException> handler) {
|
||||||
runOnDbThread(() -> {
|
runOnDbThread(() -> {
|
||||||
try {
|
try {
|
||||||
LocalAuthor localAuthor = identityManager.getLocalAuthor();
|
LocalAuthor localAuthor = identityManager.getLocalAuthor();
|
||||||
@@ -144,7 +147,7 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void signInvitations(GroupId g, LocalAuthor localAuthor,
|
private void signInvitations(GroupId g, LocalAuthor localAuthor,
|
||||||
Collection<Contact> contacts, String text,
|
Collection<Contact> contacts, @Nullable String text,
|
||||||
ResultExceptionHandler<Void, DbException> handler) {
|
ResultExceptionHandler<Void, DbException> handler) {
|
||||||
cryptoExecutor.execute(() -> {
|
cryptoExecutor.execute(() -> {
|
||||||
long timestamp = clock.currentTimeMillis();
|
long timestamp = clock.currentTimeMillis();
|
||||||
@@ -160,15 +163,14 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void sendInvitations(GroupId g,
|
private void sendInvitations(GroupId g,
|
||||||
Collection<InvitationContext> contexts, String text,
|
Collection<InvitationContext> contexts, @Nullable String text,
|
||||||
ResultExceptionHandler<Void, DbException> handler) {
|
ResultExceptionHandler<Void, DbException> handler) {
|
||||||
runOnDbThread(() -> {
|
runOnDbThread(() -> {
|
||||||
try {
|
try {
|
||||||
String txt = text.isEmpty() ? null : text;
|
|
||||||
for (InvitationContext context : contexts) {
|
for (InvitationContext context : contexts) {
|
||||||
try {
|
try {
|
||||||
groupInvitationManager.sendInvitation(g,
|
groupInvitationManager.sendInvitation(g,
|
||||||
context.contactId, txt, context.timestamp,
|
context.contactId, text, context.timestamp,
|
||||||
context.signature);
|
context.signature);
|
||||||
} catch (NoSuchContactException e) {
|
} catch (NoSuchContactException e) {
|
||||||
// Continue
|
// Continue
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ public class GroupInviteActivity extends ContactSelectorActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onButtonClick(String text) {
|
public void onButtonClick(@Nullable String text) {
|
||||||
if (groupId == null)
|
if (groupId == null)
|
||||||
throw new IllegalStateException("GroupId was not initialized");
|
throw new IllegalStateException("GroupId was not initialized");
|
||||||
controller.sendInvitation(groupId, contacts, text,
|
controller.sendInvitation(groupId, contacts, text,
|
||||||
@@ -72,7 +72,6 @@ public class GroupInviteActivity extends ContactSelectorActivity
|
|||||||
handleDbException(exception);
|
handleDbException(exception);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import android.os.Bundle;
|
|||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.StringRes;
|
import android.support.annotation.StringRes;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.support.design.widget.Snackbar;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -18,17 +17,14 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
|||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
import org.briarproject.briar.android.view.LargeTextInputView;
|
import org.briarproject.briar.android.view.LargeTextInputView;
|
||||||
import org.briarproject.briar.android.view.TextInputView.TextInputListener;
|
import org.briarproject.briar.android.view.TextInputView.SendListener;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static android.support.design.widget.Snackbar.LENGTH_SHORT;
|
|
||||||
import static org.briarproject.bramble.util.StringUtils.utf8IsTooLong;
|
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public abstract class BaseMessageFragment extends BaseFragment
|
public abstract class BaseMessageFragment extends BaseFragment
|
||||||
implements TextInputListener {
|
implements SendListener {
|
||||||
|
|
||||||
protected LargeTextInputView message;
|
protected LargeTextInputView message;
|
||||||
private MessageFragmentListener listener;
|
private MessageFragmentListener listener;
|
||||||
@@ -48,6 +44,7 @@ public abstract class BaseMessageFragment extends BaseFragment
|
|||||||
View v = inflater.inflate(R.layout.fragment_message, container,
|
View v = inflater.inflate(R.layout.fragment_message, container,
|
||||||
false);
|
false);
|
||||||
message = v.findViewById(R.id.messageView);
|
message = v.findViewById(R.id.messageView);
|
||||||
|
message.setMaxTextLength(listener.getMaximumTextLength());
|
||||||
message.setButtonText(getString(getButtonText()));
|
message.setButtonText(getString(getButtonText()));
|
||||||
message.setHint(getHintText());
|
message.setHint(getHintText());
|
||||||
message.setListener(this);
|
message.setListener(this);
|
||||||
@@ -84,20 +81,11 @@ public abstract class BaseMessageFragment extends BaseFragment
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSendClick(@Nullable String text, List<Uri> imageUris) {
|
public void onSendClick(@Nullable String text, List<Uri> imageUris) {
|
||||||
if (text == null) return;
|
|
||||||
if (utf8IsTooLong(text, listener.getMaximumTextLength())) {
|
|
||||||
Snackbar.make(message, R.string.text_too_long, LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// disable button to prevent accidental double actions
|
// disable button to prevent accidental double actions
|
||||||
message.setSendButtonEnabled(false);
|
message.setEnabled(false);
|
||||||
message.hideSoftKeyboard();
|
message.hideSoftKeyboard();
|
||||||
|
|
||||||
if(!listener.onButtonClick(text)) {
|
listener.onButtonClick(text);
|
||||||
message.setSendButtonEnabled(true);
|
|
||||||
message.showSoftKeyboard();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@@ -108,8 +96,7 @@ public abstract class BaseMessageFragment extends BaseFragment
|
|||||||
|
|
||||||
void setTitle(@StringRes int titleRes);
|
void setTitle(@StringRes int titleRes);
|
||||||
|
|
||||||
/** Returns true when the button click has been consumed. */
|
void onButtonClick(@Nullable String text);
|
||||||
boolean onButtonClick(String text);
|
|
||||||
|
|
||||||
int getMaximumTextLength();
|
int getMaximumTextLength();
|
||||||
|
|
||||||
|
|||||||
@@ -41,13 +41,12 @@ public abstract class ShareActivity extends ContactSelectorActivity
|
|||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@Override
|
@Override
|
||||||
public boolean onButtonClick(String text) {
|
public void onButtonClick(@Nullable String text) {
|
||||||
share(contacts, text);
|
share(contacts, text);
|
||||||
setResult(RESULT_OK);
|
setResult(RESULT_OK);
|
||||||
supportFinishAfterTransition();
|
supportFinishAfterTransition();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract void share(Collection<ContactId> contacts, String text);
|
abstract void share(Collection<ContactId> contacts, @Nullable String text);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public class ShareBlogActivity extends ShareActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void share(Collection<ContactId> contacts, String text) {
|
void share(Collection<ContactId> contacts, @Nullable String text) {
|
||||||
controller.share(groupId, contacts, text,
|
controller.share(groupId, contacts, text,
|
||||||
new UiExceptionHandler<DbException>(this) {
|
new UiExceptionHandler<DbException>(this) {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ import org.briarproject.briar.android.controller.handler.ExceptionHandler;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public interface ShareBlogController
|
public interface ShareBlogController
|
||||||
extends ContactSelectorController<SelectableContactItem> {
|
extends ContactSelectorController<SelectableContactItem> {
|
||||||
|
|
||||||
void share(GroupId g, Collection<ContactId> contacts, String text,
|
void share(GroupId g, Collection<ContactId> contacts, @Nullable String text,
|
||||||
ExceptionHandler<DbException> handler);
|
ExceptionHandler<DbException> handler);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ import java.util.Collection;
|
|||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -56,17 +56,16 @@ class ShareBlogControllerImpl extends ContactSelectorControllerImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void share(GroupId g, Collection<ContactId> contacts, String text,
|
public void share(GroupId g, Collection<ContactId> contacts, @Nullable
|
||||||
ExceptionHandler<DbException> handler) {
|
String text, ExceptionHandler<DbException> handler) {
|
||||||
runOnDbThread(() -> {
|
runOnDbThread(() -> {
|
||||||
try {
|
try {
|
||||||
String txt = isNullOrEmpty(text) ? null : text;
|
|
||||||
for (ContactId c : contacts) {
|
for (ContactId c : contacts) {
|
||||||
try {
|
try {
|
||||||
long time = Math.max(clock.currentTimeMillis(),
|
long time = Math.max(clock.currentTimeMillis(),
|
||||||
conversationManager.getGroupCount(c)
|
conversationManager.getGroupCount(c)
|
||||||
.getLatestMsgTime() + 1);
|
.getLatestMsgTime() + 1);
|
||||||
blogSharingManager.sendInvitation(g, c, txt, time);
|
blogSharingManager.sendInvitation(g, c, text, time);
|
||||||
} catch (NoSuchContactException | NoSuchGroupException e) {
|
} catch (NoSuchContactException | NoSuchGroupException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public class ShareForumActivity extends ShareActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void share(Collection<ContactId> contacts, String text) {
|
void share(Collection<ContactId> contacts, @Nullable String text) {
|
||||||
controller.share(groupId, contacts, text,
|
controller.share(groupId, contacts, text,
|
||||||
new UiExceptionHandler<DbException>(this) {
|
new UiExceptionHandler<DbException>(this) {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ import org.briarproject.briar.android.controller.handler.ExceptionHandler;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public interface ShareForumController
|
public interface ShareForumController
|
||||||
extends ContactSelectorController<SelectableContactItem> {
|
extends ContactSelectorController<SelectableContactItem> {
|
||||||
|
|
||||||
void share(GroupId g, Collection<ContactId> contacts, String text,
|
void share(GroupId g, Collection<ContactId> contacts, @Nullable String text,
|
||||||
ExceptionHandler<DbException> handler);
|
ExceptionHandler<DbException> handler);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,19 +13,19 @@ import org.briarproject.bramble.api.sync.GroupId;
|
|||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.briar.android.contactselection.ContactSelectorControllerImpl;
|
import org.briarproject.briar.android.contactselection.ContactSelectorControllerImpl;
|
||||||
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
|
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
|
||||||
import org.briarproject.briar.api.forum.ForumSharingManager;
|
|
||||||
import org.briarproject.briar.api.conversation.ConversationManager;
|
import org.briarproject.briar.api.conversation.ConversationManager;
|
||||||
|
import org.briarproject.briar.api.forum.ForumSharingManager;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -57,16 +57,15 @@ class ShareForumControllerImpl extends ContactSelectorControllerImpl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void share(GroupId g, Collection<ContactId> contacts,
|
public void share(GroupId g, Collection<ContactId> contacts,
|
||||||
String text, ExceptionHandler<DbException> handler) {
|
@Nullable String text, ExceptionHandler<DbException> handler) {
|
||||||
runOnDbThread(() -> {
|
runOnDbThread(() -> {
|
||||||
try {
|
try {
|
||||||
String txt = isNullOrEmpty(text) ? null : text;
|
|
||||||
for (ContactId c : contacts) {
|
for (ContactId c : contacts) {
|
||||||
try {
|
try {
|
||||||
long time = Math.max(clock.currentTimeMillis(),
|
long time = Math.max(clock.currentTimeMillis(),
|
||||||
conversationManager.getGroupCount(c)
|
conversationManager.getGroupCount(c)
|
||||||
.getLatestMsgTime() + 1);
|
.getLatestMsgTime() + 1);
|
||||||
forumSharingManager.sendInvitation(g, c, txt, time);
|
forumSharingManager.sendInvitation(g, c, text, time);
|
||||||
} catch (NoSuchContactException | NoSuchGroupException e) {
|
} catch (NoSuchContactException | NoSuchGroupException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import org.briarproject.briar.android.threaded.ThreadListController.ThreadListDa
|
|||||||
import org.briarproject.briar.android.threaded.ThreadListController.ThreadListListener;
|
import org.briarproject.briar.android.threaded.ThreadListController.ThreadListListener;
|
||||||
import org.briarproject.briar.android.view.BriarRecyclerView;
|
import org.briarproject.briar.android.view.BriarRecyclerView;
|
||||||
import org.briarproject.briar.android.view.TextInputView;
|
import org.briarproject.briar.android.view.TextInputView;
|
||||||
import org.briarproject.briar.android.view.TextInputView.TextInputListener;
|
import org.briarproject.briar.android.view.TextInputView.SendListener;
|
||||||
import org.briarproject.briar.android.view.UnreadMessageButton;
|
import org.briarproject.briar.android.view.UnreadMessageButton;
|
||||||
import org.briarproject.briar.api.client.NamedGroup;
|
import org.briarproject.briar.api.client.NamedGroup;
|
||||||
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
|
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
|
||||||
@@ -44,14 +44,14 @@ import static android.support.design.widget.Snackbar.make;
|
|||||||
import static android.support.v7.widget.RecyclerView.NO_POSITION;
|
import static android.support.v7.widget.RecyclerView.NO_POSITION;
|
||||||
import static android.support.v7.widget.RecyclerView.SCROLL_STATE_IDLE;
|
import static android.support.v7.widget.RecyclerView.SCROLL_STATE_IDLE;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static org.briarproject.bramble.util.StringUtils.utf8IsTooLong;
|
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||||
import static org.briarproject.briar.android.threaded.ThreadItemAdapter.UnreadCount;
|
import static org.briarproject.briar.android.threaded.ThreadItemAdapter.UnreadCount;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadItem, A extends ThreadItemAdapter<I>>
|
public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadItem, A extends ThreadItemAdapter<I>>
|
||||||
extends BriarActivity
|
extends BriarActivity
|
||||||
implements ThreadListListener<I>, TextInputListener, SharingListener,
|
implements ThreadListListener<I>, SendListener, SharingListener,
|
||||||
ThreadItemListener<I>, ThreadListDataSource {
|
ThreadItemListener<I>, ThreadListDataSource {
|
||||||
|
|
||||||
protected static final String KEY_REPLY_ID = "replyId";
|
protected static final String KEY_REPLY_ID = "replyId";
|
||||||
@@ -88,6 +88,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
|
|||||||
getController().setGroupId(groupId);
|
getController().setGroupId(groupId);
|
||||||
|
|
||||||
textInput = findViewById(R.id.text_input_container);
|
textInput = findViewById(R.id.text_input_container);
|
||||||
|
textInput.setMaxTextLength(getMaxTextLength());
|
||||||
textInput.setListener(this);
|
textInput.setListener(this);
|
||||||
list = findViewById(R.id.list);
|
list = findViewById(R.id.list);
|
||||||
layoutManager = new LinearLayoutManager(this);
|
layoutManager = new LinearLayoutManager(this);
|
||||||
@@ -268,7 +269,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
|
|||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
if (adapter.getHighlightedItem() != null) {
|
if (adapter.getHighlightedItem() != null) {
|
||||||
textInput.setText("");
|
textInput.clearText();
|
||||||
replyId = null;
|
replyId = null;
|
||||||
updateTextInput();
|
updateTextInput();
|
||||||
} else {
|
} else {
|
||||||
@@ -351,12 +352,8 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSendClick(@Nullable String text, List<Uri> imageUris) {
|
public void onSendClick(@Nullable String text, List<Uri> imageUris) {
|
||||||
if (text == null || text.trim().length() == 0)
|
if (isNullOrEmpty(text)) throw new AssertionError();
|
||||||
return;
|
|
||||||
if (utf8IsTooLong(text, getMaxTextLength())) {
|
|
||||||
displaySnackbar(R.string.text_too_long);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
I replyItem = adapter.getHighlightedItem();
|
I replyItem = adapter.getHighlightedItem();
|
||||||
UiResultExceptionHandler<I, DbException> handler =
|
UiResultExceptionHandler<I, DbException> handler =
|
||||||
new UiResultExceptionHandler<I, DbException>(this) {
|
new UiResultExceptionHandler<I, DbException>(this) {
|
||||||
@@ -372,7 +369,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
|
|||||||
};
|
};
|
||||||
getController().createAndStoreMessage(text, replyItem, handler);
|
getController().createAndStoreMessage(text, replyItem, handler);
|
||||||
textInput.hideSoftKeyboard();
|
textInput.hideSoftKeyboard();
|
||||||
textInput.setText("");
|
textInput.clearText();
|
||||||
replyId = null;
|
replyId = null;
|
||||||
updateTextInput();
|
updateTextInput();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ import android.support.design.widget.FloatingActionButton;
|
|||||||
import android.support.v4.view.AbsSavedState;
|
import android.support.v4.view.AbsSavedState;
|
||||||
import android.support.v7.graphics.Palette;
|
import android.support.v7.graphics.Palette;
|
||||||
import android.support.v7.widget.AppCompatImageButton;
|
import android.support.v7.widget.AppCompatImageButton;
|
||||||
import android.text.Editable;
|
|
||||||
import android.text.TextWatcher;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
@@ -22,7 +20,6 @@ import com.bumptech.glide.load.DataSource;
|
|||||||
import com.bumptech.glide.load.engine.GlideException;
|
import com.bumptech.glide.load.engine.GlideException;
|
||||||
import com.bumptech.glide.request.RequestListener;
|
import com.bumptech.glide.request.RequestListener;
|
||||||
import com.bumptech.glide.request.target.Target;
|
import com.bumptech.glide.request.target.Target;
|
||||||
import com.vanniktech.emoji.EmojiEditText;
|
|
||||||
|
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.conversation.glide.GlideApp;
|
import org.briarproject.briar.android.conversation.glide.GlideApp;
|
||||||
@@ -48,23 +45,24 @@ import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
|
|||||||
import static com.bumptech.glide.load.resource.bitmap.DownsampleStrategy.FIT_CENTER;
|
import static com.bumptech.glide.load.resource.bitmap.DownsampleStrategy.FIT_CENTER;
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
class TextInputAttachmentController implements TextWatcher {
|
class TextAttachmentController extends TextSendController {
|
||||||
|
|
||||||
private final EmojiEditText editText;
|
|
||||||
private final View sendButton;
|
|
||||||
private final AppCompatImageButton imageButton;
|
private final AppCompatImageButton imageButton;
|
||||||
private final ViewGroup imageLayout;
|
private final ViewGroup imageLayout;
|
||||||
private final ImageView imageView;
|
private final ImageView imageView;
|
||||||
|
|
||||||
private final AttachImageListener listener;
|
@Nullable
|
||||||
|
private AttachImageListener imageListener;
|
||||||
|
|
||||||
private String textHint;
|
private CharSequence textHint;
|
||||||
private List<Uri> imageUris = emptyList();
|
private List<Uri> imageUris = emptyList();
|
||||||
|
|
||||||
public TextInputAttachmentController(View v, EmojiEditText editText,
|
TextAttachmentController(View v, View sendButton,
|
||||||
View sendButton, AttachImageListener listener) {
|
TextInputController textInput) {
|
||||||
|
super(sendButton, textInput, true);
|
||||||
|
|
||||||
imageLayout = v.findViewById(R.id.imageLayout);
|
imageLayout = v.findViewById(R.id.imageLayout);
|
||||||
imageView = v.findViewById(R.id.imageView);
|
imageView = v.findViewById(R.id.imageView);
|
||||||
@@ -72,20 +70,37 @@ class TextInputAttachmentController implements TextWatcher {
|
|||||||
v.findViewById(R.id.imageCancelButton);
|
v.findViewById(R.id.imageCancelButton);
|
||||||
imageButton = v.findViewById(R.id.imageButton);
|
imageButton = v.findViewById(R.id.imageButton);
|
||||||
|
|
||||||
this.listener = listener;
|
textHint = textInput.getHint();
|
||||||
this.sendButton = sendButton;
|
|
||||||
this.editText = editText;
|
|
||||||
this.textHint = editText.getHint().toString();
|
|
||||||
|
|
||||||
editText.addTextChangedListener(this);
|
|
||||||
imageButton.setOnClickListener(view -> onImageButtonClicked());
|
imageButton.setOnClickListener(view -> onImageButtonClicked());
|
||||||
imageCancelButton.setOnClickListener(view -> {
|
imageCancelButton.setOnClickListener(view -> {
|
||||||
editText.setText(null);
|
textInput.clearText();
|
||||||
reset();
|
reset();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttachImageListener(AttachImageListener imageListener) {
|
||||||
|
this.imageListener = imageListener;
|
||||||
showImageButton(true);
|
showImageButton(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextValidityChanged(boolean isEmpty) {
|
||||||
|
if (imageUris.isEmpty()) showImageButton(isEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void onSendButtonClicked() {
|
||||||
|
if (listener != null) {
|
||||||
|
if (textInput.isTooLong()) {
|
||||||
|
textInput.showError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
listener.onSendClick(textInput.getText(), imageUris);
|
||||||
|
}
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
private void onImageButtonClicked() {
|
private void onImageButtonClicked() {
|
||||||
Intent intent = new Intent(SDK_INT >= 19 ?
|
Intent intent = new Intent(SDK_INT >= 19 ?
|
||||||
ACTION_OPEN_DOCUMENT : ACTION_GET_CONTENT);
|
ACTION_OPEN_DOCUMENT : ACTION_GET_CONTENT);
|
||||||
@@ -93,7 +108,7 @@ class TextInputAttachmentController implements TextWatcher {
|
|||||||
intent.setType("image/*");
|
intent.setType("image/*");
|
||||||
if (SDK_INT >= 18) // TODO set true to allow attaching multiple images
|
if (SDK_INT >= 18) // TODO set true to allow attaching multiple images
|
||||||
intent.putExtra(EXTRA_ALLOW_MULTIPLE, false);
|
intent.putExtra(EXTRA_ALLOW_MULTIPLE, false);
|
||||||
listener.onAttachImage(intent);
|
requireNonNull(imageListener).onAttachImage(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onImageReceived(@Nullable Intent resultData) {
|
void onImageReceived(@Nullable Intent resultData) {
|
||||||
@@ -114,7 +129,7 @@ class TextInputAttachmentController implements TextWatcher {
|
|||||||
private void onNewUris() {
|
private void onNewUris() {
|
||||||
if (imageUris.isEmpty()) return;
|
if (imageUris.isEmpty()) return;
|
||||||
showImageButton(false);
|
showImageButton(false);
|
||||||
editText.setHint(R.string.image_caption_hint);
|
textInput.setHint(R.string.image_caption_hint);
|
||||||
imageLayout.setVisibility(VISIBLE);
|
imageLayout.setVisibility(VISIBLE);
|
||||||
GlideApp.with(imageView)
|
GlideApp.with(imageView)
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
@@ -135,7 +150,7 @@ class TextInputAttachmentController implements TextWatcher {
|
|||||||
Object model, Target<Bitmap> target,
|
Object model, Target<Bitmap> target,
|
||||||
DataSource dataSource, boolean isFirstResource) {
|
DataSource dataSource, boolean isFirstResource) {
|
||||||
Palette.from(resource).generate(
|
Palette.from(resource).generate(
|
||||||
TextInputAttachmentController.this::onPaletteGenerated);
|
TextAttachmentController.this::onPaletteGenerated);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -158,11 +173,12 @@ class TextInputAttachmentController implements TextWatcher {
|
|||||||
private void showImageButton(boolean showImageButton) {
|
private void showImageButton(boolean showImageButton) {
|
||||||
if (showImageButton) {
|
if (showImageButton) {
|
||||||
imageButton.setVisibility(VISIBLE);
|
imageButton.setVisibility(VISIBLE);
|
||||||
|
sendButton.setEnabled(false);
|
||||||
if (SDK_INT <= 15) {
|
if (SDK_INT <= 15) {
|
||||||
sendButton.setVisibility(INVISIBLE);
|
sendButton.setVisibility(INVISIBLE);
|
||||||
|
imageButton.setEnabled(true);
|
||||||
} else {
|
} else {
|
||||||
sendButton.clearAnimation();
|
sendButton.clearAnimation();
|
||||||
sendButton.setEnabled(false);
|
|
||||||
sendButton.animate().alpha(0f).withEndAction(() -> {
|
sendButton.animate().alpha(0f).withEndAction(() -> {
|
||||||
sendButton.setVisibility(INVISIBLE);
|
sendButton.setVisibility(INVISIBLE);
|
||||||
imageButton.setEnabled(true);
|
imageButton.setEnabled(true);
|
||||||
@@ -172,51 +188,25 @@ class TextInputAttachmentController implements TextWatcher {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sendButton.setVisibility(VISIBLE);
|
sendButton.setVisibility(VISIBLE);
|
||||||
|
// enable/disable buttons right away to allow fast sending
|
||||||
|
sendButton.setEnabled(true);
|
||||||
|
imageButton.setEnabled(false);
|
||||||
if (SDK_INT <= 15) {
|
if (SDK_INT <= 15) {
|
||||||
imageButton.setVisibility(INVISIBLE);
|
imageButton.setVisibility(INVISIBLE);
|
||||||
} else {
|
} else {
|
||||||
sendButton.clearAnimation();
|
sendButton.clearAnimation();
|
||||||
sendButton.animate().alpha(1f).start();
|
sendButton.animate().alpha(1f).start();
|
||||||
imageButton.clearAnimation();
|
imageButton.clearAnimation();
|
||||||
imageButton.setEnabled(false);
|
imageButton.animate().alpha(0f).withEndAction(() ->
|
||||||
imageButton.animate().alpha(0f).withEndAction(() -> {
|
imageButton.setVisibility(INVISIBLE)
|
||||||
imageButton.setVisibility(INVISIBLE);
|
).start();
|
||||||
sendButton.setEnabled(true);
|
|
||||||
}).start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void reset() {
|
||||||
public void beforeTextChanged(CharSequence s, int start, int count,
|
|
||||||
int after) {
|
|
||||||
// noop
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTextChanged(CharSequence s, int start, int before,
|
|
||||||
int count) {
|
|
||||||
if (start != 0 || !imageUris.isEmpty()) return;
|
|
||||||
if (s.length() > 0) showImageButton(false);
|
|
||||||
else showImageButton(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterTextChanged(Editable s) {
|
|
||||||
// noop
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Uri> getUris() {
|
|
||||||
return imageUris;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void saveHint(String hint) {
|
|
||||||
textHint = hint;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
// restore hint
|
// restore hint
|
||||||
editText.setHint(textHint);
|
textInput.setHint(textHint);
|
||||||
// hide image layout
|
// hide image layout
|
||||||
imageLayout.setVisibility(GONE);
|
imageLayout.setVisibility(GONE);
|
||||||
// reset image URIs
|
// reset image URIs
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
package org.briarproject.briar.android.view;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.annotation.StringRes;
|
||||||
|
import android.support.annotation.UiThread;
|
||||||
|
import android.support.design.widget.Snackbar;
|
||||||
|
import android.support.v7.widget.AppCompatImageButton;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
|
||||||
|
import com.vanniktech.emoji.EmojiEditText;
|
||||||
|
import com.vanniktech.emoji.EmojiPopup;
|
||||||
|
import com.vanniktech.emoji.RecentEmoji;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
|
||||||
|
import static android.content.Context.INPUT_METHOD_SERVICE;
|
||||||
|
import static android.support.design.widget.Snackbar.LENGTH_SHORT;
|
||||||
|
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
import static org.briarproject.briar.android.view.TextInputView.TextValidityListener;
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
@NotNullByDefault
|
||||||
|
class TextInputController implements TextWatcher {
|
||||||
|
|
||||||
|
private final Context ctx;
|
||||||
|
private final AppCompatImageButton emojiToggle;
|
||||||
|
private final EmojiPopup emojiPopup;
|
||||||
|
private final EmojiEditText editText;
|
||||||
|
|
||||||
|
private @Nullable TextValidityListener listener;
|
||||||
|
private int maxLength = Integer.MAX_VALUE;
|
||||||
|
private final boolean emptyTextAllowed;
|
||||||
|
private boolean isEmpty = true;
|
||||||
|
|
||||||
|
TextInputController(View rootView, AppCompatImageButton emojiToggle,
|
||||||
|
EmojiEditText editText, RecentEmoji recentEmoji,
|
||||||
|
boolean emptyTextAllowed) {
|
||||||
|
ctx = rootView.getContext();
|
||||||
|
this.emojiToggle = emojiToggle;
|
||||||
|
this.editText = editText;
|
||||||
|
this.editText.addTextChangedListener(this);
|
||||||
|
this.editText.setOnClickListener(v -> showSoftKeyboard());
|
||||||
|
emojiPopup = EmojiPopup.Builder
|
||||||
|
.fromRootView(rootView)
|
||||||
|
.setRecentEmoji(recentEmoji)
|
||||||
|
.setOnEmojiPopupShownListener(this::showKeyboardIcon)
|
||||||
|
.setOnEmojiPopupDismissListener(this::showEmojiIcon)
|
||||||
|
.build(this.editText);
|
||||||
|
this.emojiToggle.setOnClickListener(v -> emojiPopup.toggle());
|
||||||
|
this.emptyTextAllowed = emptyTextAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count,
|
||||||
|
int after) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before,
|
||||||
|
int count) {
|
||||||
|
if (emptyTextAllowed || listener == null) return;
|
||||||
|
if (s.toString().trim().length() == 0) {
|
||||||
|
if (!isEmpty) {
|
||||||
|
isEmpty = true;
|
||||||
|
listener.onTextValidityChanged(true);
|
||||||
|
}
|
||||||
|
} else if (isEmpty) {
|
||||||
|
isEmpty = false;
|
||||||
|
listener.onTextValidityChanged(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMaxLength(int maxLength) {
|
||||||
|
this.maxLength = maxLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isTooLong() {
|
||||||
|
return editText.getText() != null &&
|
||||||
|
editText.getText().toString().trim().length() > maxLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current text or {@code null},
|
||||||
|
* if it is empty or only consists of white-spaces.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
String getText() {
|
||||||
|
Editable editable = editText.getText();
|
||||||
|
if (editable == null || editable.toString().trim().length() == 0)
|
||||||
|
return null;
|
||||||
|
return editable.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearText() {
|
||||||
|
editText.setText(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
CharSequence getHint() {
|
||||||
|
return editText.getHint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setHint(@StringRes int res) {
|
||||||
|
setHint(ctx.getString(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setHint(CharSequence hint) {
|
||||||
|
editText.setHint(hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTextValidityListener(@Nullable TextValidityListener listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
void showError() {
|
||||||
|
Snackbar.make(editText, R.string.text_too_long, LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean requestFocus(int direction, Rect previouslyFocusedRect) {
|
||||||
|
return editText.requestFocus(direction, previouslyFocusedRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onDetachedFromWindow() {
|
||||||
|
if (emojiPopup.isShowing()) emojiPopup.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
void showSoftKeyboard() {
|
||||||
|
Object o = ctx.getSystemService(INPUT_METHOD_SERVICE);
|
||||||
|
InputMethodManager imm = (InputMethodManager) requireNonNull(o);
|
||||||
|
imm.showSoftInput(editText, SHOW_IMPLICIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hideSoftKeyboard() {
|
||||||
|
if (emojiPopup.isShowing()) emojiPopup.dismiss();
|
||||||
|
IBinder token = editText.getWindowToken();
|
||||||
|
Object o = ctx.getSystemService(INPUT_METHOD_SERVICE);
|
||||||
|
InputMethodManager imm = (InputMethodManager) requireNonNull(o);
|
||||||
|
imm.hideSoftInputFromWindow(token, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showEmojiIcon() {
|
||||||
|
emojiToggle.setImageResource(R.drawable.ic_emoji_toggle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showKeyboardIcon() {
|
||||||
|
emojiToggle.setImageResource(R.drawable.ic_keyboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
editText.setEnabled(enabled);
|
||||||
|
emojiToggle.setEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -6,22 +6,17 @@ import android.content.Intent;
|
|||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.support.annotation.CallSuper;
|
import android.support.annotation.CallSuper;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.StringRes;
|
import android.support.annotation.StringRes;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.support.v7.widget.AppCompatImageButton;
|
import android.support.v7.widget.AppCompatImageButton;
|
||||||
import android.text.Editable;
|
|
||||||
import android.text.TextWatcher;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
|
||||||
|
|
||||||
import com.vanniktech.emoji.EmojiEditText;
|
import com.vanniktech.emoji.EmojiEditText;
|
||||||
import com.vanniktech.emoji.EmojiPopup;
|
|
||||||
import com.vanniktech.emoji.RecentEmoji;
|
import com.vanniktech.emoji.RecentEmoji;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
@@ -34,11 +29,8 @@ import java.util.List;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static android.content.Context.INPUT_METHOD_SERVICE;
|
|
||||||
import static android.content.Context.LAYOUT_INFLATER_SERVICE;
|
import static android.content.Context.LAYOUT_INFLATER_SERVICE;
|
||||||
import static android.view.KeyEvent.KEYCODE_ENTER;
|
import static android.view.KeyEvent.KEYCODE_ENTER;
|
||||||
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
|
|
||||||
import static java.util.Collections.emptyList;
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@@ -49,16 +41,14 @@ public class TextInputView extends KeyboardAwareLinearLayout {
|
|||||||
@Inject
|
@Inject
|
||||||
RecentEmoji recentEmoji;
|
RecentEmoji recentEmoji;
|
||||||
|
|
||||||
@Nullable
|
TextInputController textInputController;
|
||||||
TextInputListener listener;
|
TextSendController textSendController;
|
||||||
@Nullable
|
|
||||||
TextInputAttachmentController attachmentController;
|
|
||||||
|
|
||||||
AppCompatImageButton emojiToggle;
|
|
||||||
EmojiEditText editText;
|
EmojiEditText editText;
|
||||||
EmojiPopup emojiPopup;
|
|
||||||
View sendButton;
|
View sendButton;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
TextAttachmentController attachmentController;
|
||||||
|
|
||||||
public TextInputView(Context context) {
|
public TextInputView(Context context) {
|
||||||
this(context, null);
|
this(context, null);
|
||||||
}
|
}
|
||||||
@@ -90,34 +80,42 @@ public class TextInputView extends KeyboardAwareLinearLayout {
|
|||||||
|
|
||||||
@CallSuper
|
@CallSuper
|
||||||
protected void setUpViews(Context context, @Nullable AttributeSet attrs) {
|
protected void setUpViews(Context context, @Nullable AttributeSet attrs) {
|
||||||
emojiToggle = findViewById(R.id.emoji_toggle);
|
|
||||||
editText = findViewById(R.id.input_text);
|
|
||||||
emojiPopup = EmojiPopup.Builder
|
|
||||||
.fromRootView(this)
|
|
||||||
.setRecentEmoji(recentEmoji)
|
|
||||||
.setOnEmojiPopupShownListener(this::showKeyboardIcon)
|
|
||||||
.setOnEmojiPopupDismissListener(this::showEmojiIcon)
|
|
||||||
.build(editText);
|
|
||||||
sendButton = findViewById(R.id.btn_send);
|
|
||||||
|
|
||||||
// get attributes
|
// get attributes
|
||||||
TypedArray attributes = context.obtainStyledAttributes(attrs,
|
TypedArray attributes = context.obtainStyledAttributes(attrs,
|
||||||
R.styleable.TextInputView);
|
R.styleable.TextInputView);
|
||||||
String hint = attributes.getString(R.styleable.TextInputView_hint);
|
String hint = attributes.getString(R.styleable.TextInputView_hint);
|
||||||
|
boolean allowEmptyText = attributes
|
||||||
|
.getBoolean(R.styleable.TextInputView_allowEmptyText, false);
|
||||||
|
boolean supportsAttachments = attributes
|
||||||
|
.getBoolean(R.styleable.TextInputView_supportsAttachments, false);
|
||||||
attributes.recycle();
|
attributes.recycle();
|
||||||
|
|
||||||
if (hint != null) setHint(hint);
|
// set up input controller
|
||||||
|
AppCompatImageButton emojiToggle = findViewById(R.id.emoji_toggle);
|
||||||
|
editText = findViewById(R.id.input_text);
|
||||||
|
textInputController = new TextInputController(this, emojiToggle,
|
||||||
|
editText, recentEmoji, allowEmptyText);
|
||||||
|
if (hint != null) textInputController.setHint(hint);
|
||||||
|
|
||||||
emojiToggle.setOnClickListener(v -> emojiPopup.toggle());
|
// set up sending controller
|
||||||
editText.setOnClickListener(v -> showSoftKeyboard());
|
sendButton = findViewById(R.id.btn_send);
|
||||||
|
if (supportsAttachments) {
|
||||||
|
textSendController = new TextAttachmentController(this, sendButton,
|
||||||
|
textInputController);
|
||||||
|
} else {
|
||||||
|
textSendController = new TextSendController(sendButton,
|
||||||
|
textInputController, allowEmptyText);
|
||||||
|
}
|
||||||
|
textInputController.setTextValidityListener(textSendController);
|
||||||
|
|
||||||
|
// support sending with Ctrl+Enter
|
||||||
editText.setOnKeyListener((v, keyCode, event) -> {
|
editText.setOnKeyListener((v, keyCode, event) -> {
|
||||||
if (keyCode == KEYCODE_ENTER && event.isCtrlPressed()) {
|
if (keyCode == KEYCODE_ENTER && event.isCtrlPressed()) {
|
||||||
onSendButtonClicked();
|
textSendController.onSendButtonClicked();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
sendButton.setOnClickListener(v -> onSendButtonClicked());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -141,41 +139,13 @@ public class TextInputView extends KeyboardAwareLinearLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setListener(TextInputListener listener) {
|
public void setListener(SendListener listener) {
|
||||||
this.listener = listener;
|
textSendController.setSendListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Call this during onCreate() to enable image attachment support.
|
|
||||||
* Do not call it twice!
|
|
||||||
*/
|
|
||||||
public void setAttachImageListener(AttachImageListener imageListener) {
|
public void setAttachImageListener(AttachImageListener imageListener) {
|
||||||
if (attachmentController != null) throw new IllegalStateException();
|
attachmentController = (TextAttachmentController) textSendController;
|
||||||
attachmentController = new TextInputAttachmentController(getRootView(),
|
attachmentController.setAttachImageListener(imageListener);
|
||||||
editText, sendButton, imageListener
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showEmojiIcon() {
|
|
||||||
emojiToggle.setImageResource(R.drawable.ic_emoji_toggle);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showKeyboardIcon() {
|
|
||||||
emojiToggle.setImageResource(R.drawable.ic_keyboard);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onSendButtonClicked() {
|
|
||||||
if (listener != null) {
|
|
||||||
Editable editable = editText.getText();
|
|
||||||
String text = editable == null || editable.length() == 0 ?
|
|
||||||
null : editable.toString();
|
|
||||||
List<Uri> imageUris = attachmentController == null ? emptyList() :
|
|
||||||
attachmentController.getUris();
|
|
||||||
listener.onSendClick(text, imageUris);
|
|
||||||
}
|
|
||||||
if (attachmentController != null) {
|
|
||||||
attachmentController.reset();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onImageReceived(@Nullable Intent resultData) {
|
public void onImageReceived(@Nullable Intent resultData) {
|
||||||
@@ -183,61 +153,54 @@ public class TextInputView extends KeyboardAwareLinearLayout {
|
|||||||
attachmentController.onImageReceived(resultData);
|
attachmentController.onImageReceived(resultData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
super.setEnabled(enabled);
|
||||||
|
textInputController.setEnabled(enabled);
|
||||||
|
textSendController.setEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
|
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
|
||||||
return editText.requestFocus(direction, previouslyFocusedRect);
|
return textInputController
|
||||||
|
.requestFocus(direction, previouslyFocusedRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDetachedFromWindow() {
|
public void onDetachedFromWindow() {
|
||||||
super.onDetachedFromWindow();
|
super.onDetachedFromWindow();
|
||||||
if (emojiPopup.isShowing()) emojiPopup.dismiss();
|
textInputController.onDetachedFromWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setText(String text) {
|
public void clearText() {
|
||||||
editText.setText(text);
|
textInputController.clearText();
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return editText.getText() == null || editText.getText().length() == 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHint(@StringRes int res) {
|
public void setHint(@StringRes int res) {
|
||||||
setHint(getContext().getString(res));
|
textInputController.setHint(getContext().getString(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHint(String hint) {
|
public void setMaxTextLength(int maxLength) {
|
||||||
if (attachmentController != null) attachmentController.saveHint(hint);
|
textInputController.setMaxLength(maxLength);
|
||||||
editText.setHint(hint);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSendButtonEnabled(boolean enabled) {
|
|
||||||
sendButton.setEnabled(enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addTextChangedListener(TextWatcher watcher) {
|
|
||||||
editText.addTextChangedListener(watcher);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showSoftKeyboard() {
|
public void showSoftKeyboard() {
|
||||||
Object o = getContext().getSystemService(INPUT_METHOD_SERVICE);
|
textInputController.showSoftKeyboard();
|
||||||
InputMethodManager imm = (InputMethodManager) requireNonNull(o);
|
|
||||||
imm.showSoftInput(editText, SHOW_IMPLICIT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hideSoftKeyboard() {
|
public void hideSoftKeyboard() {
|
||||||
if (emojiPopup.isShowing()) emojiPopup.dismiss();
|
textInputController.hideSoftKeyboard();
|
||||||
IBinder token = editText.getWindowToken();
|
}
|
||||||
Object o = getContext().getSystemService(INPUT_METHOD_SERVICE);
|
|
||||||
InputMethodManager imm = (InputMethodManager) requireNonNull(o);
|
interface TextValidityListener {
|
||||||
imm.hideSoftInputFromWindow(token, 0);
|
void onTextValidityChanged(boolean isEmpty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface AttachImageListener {
|
public interface AttachImageListener {
|
||||||
void onAttachImage(Intent intent);
|
void onAttachImage(Intent intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface TextInputListener {
|
public interface SendListener {
|
||||||
void onSendClick(@Nullable String text, List<Uri> imageUris);
|
void onSendClick(@Nullable String text, List<Uri> imageUris);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package org.briarproject.briar.android.view;
|
||||||
|
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.annotation.UiThread;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.briar.android.view.TextInputView.SendListener;
|
||||||
|
import org.briarproject.briar.android.view.TextInputView.TextValidityListener;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
@NotNullByDefault
|
||||||
|
class TextSendController implements TextValidityListener {
|
||||||
|
|
||||||
|
protected final TextInputController textInput;
|
||||||
|
protected final View sendButton;
|
||||||
|
@Nullable
|
||||||
|
protected SendListener listener;
|
||||||
|
protected boolean enabled = true;
|
||||||
|
|
||||||
|
private final boolean allowEmptyText;
|
||||||
|
private boolean wasEmpty = true;
|
||||||
|
|
||||||
|
TextSendController(View sendButton, TextInputController textInput,
|
||||||
|
boolean allowEmptyText) {
|
||||||
|
this.sendButton = sendButton;
|
||||||
|
this.sendButton.setOnClickListener(v -> onSendButtonClicked());
|
||||||
|
this.sendButton.setEnabled(allowEmptyText);
|
||||||
|
this.textInput = textInput;
|
||||||
|
this.allowEmptyText = allowEmptyText;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextValidityChanged(boolean isEmpty) {
|
||||||
|
sendButton.setEnabled(enabled && !isEmpty);
|
||||||
|
wasEmpty = isEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
sendButton.setOnClickListener(
|
||||||
|
enabled ? v -> onSendButtonClicked() : null);
|
||||||
|
sendButton.setEnabled(!wasEmpty || allowEmptyText);
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSendListener(SendListener listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
void onSendButtonClicked() {
|
||||||
|
if (listener != null) {
|
||||||
|
if (textInput.isTooLong()) {
|
||||||
|
textInput.showError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
listener.onSendClick(textInput.getText(), emptyList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -32,9 +32,9 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginLeft="@dimen/margin_medium"
|
android:layout_marginLeft="@dimen/margin_medium"
|
||||||
android:layout_marginStart="@dimen/margin_medium"
|
android:layout_marginStart="@dimen/margin_medium"
|
||||||
|
android:ellipsize="end"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:ellipsize="end"
|
|
||||||
android:textColor="@color/action_bar_text"
|
android:textColor="@color/action_bar_text"
|
||||||
tools:text="Contact Name of someone who chose a long name"/>
|
tools:text="Contact Name of someone who chose a long name"/>
|
||||||
|
|
||||||
@@ -54,6 +54,7 @@
|
|||||||
android:id="@+id/text_input_container"
|
android:id="@+id/text_input_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:hint="@string/message_hint"/>
|
app:hint="@string/message_hint"
|
||||||
|
app:supportsAttachments="true"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
app:allowEmptyText="true"
|
||||||
app:buttonText="@string/forum_share_button"
|
app:buttonText="@string/forum_share_button"
|
||||||
app:fillHeight="true"
|
app:fillHeight="true"
|
||||||
app:hint="@string/forum_share_message"/>
|
app:hint="@string/forum_share_message"/>
|
||||||
@@ -39,6 +39,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="bottom"
|
android:gravity="bottom"
|
||||||
|
app:allowEmptyText="true"
|
||||||
app:buttonText="@string/blogs_reblog_button"
|
app:buttonText="@string/blogs_reblog_button"
|
||||||
app:hint="@string/blogs_reblog_comment_hint"
|
app:hint="@string/blogs_reblog_comment_hint"
|
||||||
app:maxLines="5"/>
|
app:maxLines="5"/>
|
||||||
|
|||||||
@@ -118,6 +118,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/margin_large"
|
android:layout_marginTop="@dimen/margin_large"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
|
app:allowEmptyText="true"
|
||||||
app:buttonText="@string/introduction_button"
|
app:buttonText="@string/introduction_button"
|
||||||
app:hint="@string/introduction_message_hint"
|
app:hint="@string/introduction_message_hint"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
|||||||
@@ -78,20 +78,6 @@
|
|||||||
android:layout_height="@dimen/text_input_height"
|
android:layout_height="@dimen/text_input_height"
|
||||||
android:layout_gravity="bottom">
|
android:layout_gravity="bottom">
|
||||||
|
|
||||||
<android.support.v7.widget.AppCompatImageButton
|
|
||||||
android:id="@+id/btn_send"
|
|
||||||
android:layout_width="@dimen/text_input_height"
|
|
||||||
android:layout_height="@dimen/text_input_height"
|
|
||||||
android:background="?attr/selectableItemBackground"
|
|
||||||
android:clickable="true"
|
|
||||||
android:contentDescription="@string/send"
|
|
||||||
android:enabled="false"
|
|
||||||
android:focusable="true"
|
|
||||||
android:padding="4dp"
|
|
||||||
android:scaleType="center"
|
|
||||||
android:src="@drawable/social_send_now_white"
|
|
||||||
app:tint="@color/briar_accent"/>
|
|
||||||
|
|
||||||
<android.support.v7.widget.AppCompatImageButton
|
<android.support.v7.widget.AppCompatImageButton
|
||||||
android:id="@+id/imageButton"
|
android:id="@+id/imageButton"
|
||||||
android:layout_width="@dimen/text_input_height"
|
android:layout_width="@dimen/text_input_height"
|
||||||
@@ -107,6 +93,20 @@
|
|||||||
android:visibility="invisible"
|
android:visibility="invisible"
|
||||||
app:tint="?attr/colorControlNormal"/>
|
app:tint="?attr/colorControlNormal"/>
|
||||||
|
|
||||||
|
<android.support.v7.widget.AppCompatImageButton
|
||||||
|
android:id="@+id/btn_send"
|
||||||
|
android:layout_width="@dimen/text_input_height"
|
||||||
|
android:layout_height="@dimen/text_input_height"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:contentDescription="@string/send"
|
||||||
|
android:enabled="false"
|
||||||
|
android:focusable="true"
|
||||||
|
android:padding="4dp"
|
||||||
|
android:scaleType="center"
|
||||||
|
android:src="@drawable/social_send_now_white"
|
||||||
|
app:tint="@color/briar_accent"/>
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
|
|
||||||
<declare-styleable name="TextInputView">
|
<declare-styleable name="TextInputView">
|
||||||
<attr name="hint" format="string"/>
|
<attr name="hint" format="string"/>
|
||||||
|
<attr name="allowEmptyText" format="boolean"/>
|
||||||
|
<attr name="supportsAttachments" format="boolean"/>
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
<declare-styleable name="LargeTextInputView">
|
<declare-styleable name="LargeTextInputView">
|
||||||
|
|||||||
Reference in New Issue
Block a user