[android] Require users of TextInputView to set its controller

This commit is contained in:
Torsten Grote
2018-12-04 12:39:31 -02:00
parent 419f2d966a
commit c7f4e976ed
13 changed files with 132 additions and 124 deletions

View File

@@ -20,6 +20,7 @@ import org.briarproject.briar.android.controller.handler.UiResultExceptionHandle
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.SendListener; import org.briarproject.briar.android.view.TextInputView.SendListener;
import org.briarproject.briar.android.view.TextSendController;
import java.util.List; import java.util.List;
@@ -81,6 +82,9 @@ public class ReblogFragment extends BaseFragment implements SendListener {
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);
TextSendController sendController =
new TextSendController(ui.input, this, true);
ui.input.setSendController(sendController);
ui.input.setEnabled(false); ui.input.setEnabled(false);
ui.input.setMaxTextLength(MAX_BLOG_POST_TEXT_LENGTH); ui.input.setMaxTextLength(MAX_BLOG_POST_TEXT_LENGTH);
showProgressBar(); showProgressBar();
@@ -117,7 +121,6 @@ public class ReblogFragment extends BaseFragment implements SendListener {
ui.post.bindItem(item); ui.post.bindItem(item);
ui.post.hideReblogButton(); ui.post.hideReblogButton();
ui.input.setListener(this);
ui.input.setEnabled(true); ui.input.setEnabled(true);
ui.scrollView.post(() -> ui.scrollView.fullScroll(FOCUS_DOWN)); ui.scrollView.post(() -> ui.scrollView.fullScroll(FOCUS_DOWN));
} }

View File

@@ -22,6 +22,7 @@ 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.SendListener; import org.briarproject.briar.android.view.TextInputView.SendListener;
import org.briarproject.briar.android.view.TextSendController;
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;
@@ -75,8 +76,10 @@ 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);
TextSendController sendController =
new TextSendController(input, this, false);
input.setSendController(sendController);
input.setMaxTextLength(MAX_BLOG_POST_TEXT_LENGTH); input.setMaxTextLength(MAX_BLOG_POST_TEXT_LENGTH);
input.setListener(this);
progressBar = findViewById(R.id.progressBar); progressBar = findViewById(R.id.progressBar);
} }

View File

@@ -63,9 +63,11 @@ import org.briarproject.briar.android.forum.ForumActivity;
import org.briarproject.briar.android.introduction.IntroductionActivity; import org.briarproject.briar.android.introduction.IntroductionActivity;
import org.briarproject.briar.android.privategroup.conversation.GroupActivity; 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.TextAttachmentController;
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.SendListener; import org.briarproject.briar.android.view.TextInputView.SendListener;
import org.briarproject.briar.android.view.TextSendController;
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;
@@ -166,6 +168,7 @@ public class ConversationActivity extends BriarActivity
private BriarRecyclerView list; private BriarRecyclerView list;
private LinearLayoutManager layoutManager; private LinearLayoutManager layoutManager;
private TextInputView textInputView; private TextInputView textInputView;
private TextSendController sendController;
// Fields that are accessed from background threads must be volatile // Fields that are accessed from background threads must be volatile
@Inject @Inject
@@ -255,12 +258,15 @@ 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);
if (FEATURE_FLAG_IMAGE_ATTACHMENTS) {
sendController = new TextAttachmentController(textInputView, this,
this, getWindowManager());
} else {
sendController = new TextSendController(textInputView, this, false);
}
textInputView.setSendController(sendController);
textInputView.setMaxTextLength(MAX_PRIVATE_MESSAGE_TEXT_LENGTH); textInputView.setMaxTextLength(MAX_PRIVATE_MESSAGE_TEXT_LENGTH);
textInputView.setEnabled(false); textInputView.setEnabled(false);
textInputView.setListener(this);
if (FEATURE_FLAG_IMAGE_ATTACHMENTS) {
textInputView.setAttachImageListener(this, getWindowManager());
}
} }
@Override @Override
@@ -278,7 +284,8 @@ public class ConversationActivity extends BriarActivity
snackbar.getView().setBackgroundResource(R.color.briar_primary); snackbar.getView().setBackgroundResource(R.color.briar_primary);
snackbar.show(); snackbar.show();
} else if (request == REQUEST_ATTACH_IMAGE && result == RESULT_OK) { } else if (request == REQUEST_ATTACH_IMAGE && result == RESULT_OK) {
textInputView.onImageReceived(data); // remove cast when removing FEATURE_FLAG_IMAGE_ATTACHMENTS
((TextAttachmentController) sendController).onImageReceived(data);
} }
} }

View File

@@ -24,6 +24,7 @@ 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.SendListener; import org.briarproject.briar.android.view.TextInputView.SendListener;
import org.briarproject.briar.android.view.TextSendController;
import org.briarproject.briar.api.introduction.IntroductionManager; import org.briarproject.briar.api.introduction.IntroductionManager;
import java.util.List; import java.util.List;
@@ -102,6 +103,9 @@ 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);
TextSendController sendController =
new TextSendController(ui.message, this, true);
ui.message.setSendController(sendController);
ui.message.setMaxTextLength(MAX_INTRODUCTION_TEXT_LENGTH); ui.message.setMaxTextLength(MAX_INTRODUCTION_TEXT_LENGTH);
ui.message.setEnabled(false); ui.message.setEnabled(false);
@@ -162,9 +166,6 @@ public class IntroductionMessageFragment extends BaseFragment
ui.progressBar.setVisibility(GONE); ui.progressBar.setVisibility(GONE);
if (possible) { if (possible) {
// set button action
ui.message.setListener(IntroductionMessageFragment.this);
// show views // show views
ui.notPossible.setVisibility(GONE); ui.notPossible.setVisibility(GONE);
ui.message.setVisibility(VISIBLE); ui.message.setVisibility(VISIBLE);

View File

@@ -18,6 +18,7 @@ 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.SendListener; import org.briarproject.briar.android.view.TextInputView.SendListener;
import org.briarproject.briar.android.view.TextSendController;
import java.util.List; import java.util.List;
@@ -44,10 +45,12 @@ 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);
TextSendController sendController =
new TextSendController(message, this, true);
message.setSendController(sendController);
message.setMaxTextLength(listener.getMaximumTextLength()); message.setMaxTextLength(listener.getMaximumTextLength());
message.setButtonText(getString(getButtonText())); message.setButtonText(getString(getButtonText()));
message.setHint(getHintText()); message.setHint(getHintText());
message.setListener(this);
return v; return v;
} }

View File

@@ -29,6 +29,7 @@ import org.briarproject.briar.android.threaded.ThreadListController.ThreadListLi
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.SendListener; import org.briarproject.briar.android.view.TextInputView.SendListener;
import org.briarproject.briar.android.view.TextSendController;
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;
@@ -88,8 +89,10 @@ 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);
TextSendController sendController =
new TextSendController(textInput, this, false);
textInput.setSendController(sendController);
textInput.setMaxTextLength(getMaxTextLength()); textInput.setMaxTextLength(getMaxTextLength());
textInput.setListener(this);
list = findViewById(R.id.list); list = findViewById(R.id.list);
layoutManager = new LinearLayoutManager(this); layoutManager = new LinearLayoutManager(this);
// FIXME pre-fetching messes with read state, find better solution #1289 // FIXME pre-fetching messes with read state, find better solution #1289

View File

@@ -71,7 +71,7 @@ public class LargeTextInputView extends TextInputView {
} }
public void setButtonText(String text) { public void setButtonText(String text) {
((Button) sendButton).setText(text); ((Button) findViewById(R.id.btn_send)).setText(text);
} }
} }

View File

@@ -6,6 +6,7 @@ import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.design.widget.FloatingActionButton; import android.support.design.widget.FloatingActionButton;
@@ -13,7 +14,6 @@ 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.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams; import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager; import android.view.WindowManager;
@@ -28,6 +28,7 @@ import com.bumptech.glide.request.target.Target;
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;
import org.briarproject.briar.android.view.TextInputView.AttachImageListener; import org.briarproject.briar.android.view.TextInputView.AttachImageListener;
import org.briarproject.briar.android.view.TextInputView.SendListener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -53,21 +54,21 @@ import static java.util.Collections.singletonList;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
@UiThread @UiThread
class TextAttachmentController extends TextSendController { public class TextAttachmentController extends TextSendController {
private final AppCompatImageButton imageButton; private final AppCompatImageButton imageButton;
private final ViewGroup imageLayout; private final ViewGroup imageLayout;
private final ImageView imageView; private final ImageView imageView;
@Nullable private final AttachImageListener imageListener;
private AttachImageListener imageListener;
private CharSequence textHint; private CharSequence textHint;
private List<Uri> imageUris = emptyList(); private List<Uri> imageUris = emptyList();
TextAttachmentController(View v, View sendButton, public TextAttachmentController(TextInputView v, SendListener listener,
TextInputController textInput) { AttachImageListener imageListener, WindowManager windowManager) {
super(sendButton, textInput, true); super(v, listener, true);
this.imageListener = imageListener;
imageLayout = v.findViewById(R.id.imageLayout); imageLayout = v.findViewById(R.id.imageLayout);
imageView = v.findViewById(R.id.imageView); imageView = v.findViewById(R.id.imageView);
@@ -82,11 +83,7 @@ class TextAttachmentController extends TextSendController {
textInput.clearText(); textInput.clearText();
reset(); reset();
}); });
}
public void setAttachImageListener(AttachImageListener imageListener,
WindowManager windowManager) {
this.imageListener = imageListener;
// set preview size based on screen height // set preview size based on screen height
DisplayMetrics displayMetrics = new DisplayMetrics(); DisplayMetrics displayMetrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(displayMetrics); windowManager.getDefaultDisplay().getMetrics(displayMetrics);
@@ -98,20 +95,16 @@ class TextAttachmentController extends TextSendController {
} }
@Override @Override
public void onTextValidityChanged(boolean isEmpty) { public void onTextIsEmptyChanged(boolean isEmpty) {
if (imageUris.isEmpty()) showImageButton(isEmpty); if (imageUris.isEmpty()) showImageButton(isEmpty);
} }
@Override @Override
void onSendButtonClicked() { void onSendButtonClicked() {
if (listener != null) { if (canSend()) {
if (textInput.isTooLong()) {
textInput.showError();
return;
}
listener.onSendClick(textInput.getText(), imageUris); listener.onSendClick(textInput.getText(), imageUris);
reset();
} }
reset();
} }
private void onImageButtonClicked() { private void onImageButtonClicked() {
@@ -124,7 +117,7 @@ class TextAttachmentController extends TextSendController {
requireNonNull(imageListener).onAttachImage(intent); requireNonNull(imageListener).onAttachImage(intent);
} }
void onImageReceived(@Nullable Intent resultData) { public void onImageReceived(@Nullable Intent resultData) {
if (resultData == null) return; if (resultData == null) return;
if (resultData.getData() != null) { if (resultData.getData() != null) {
imageUris = singletonList(resultData.getData()); imageUris = singletonList(resultData.getData());
@@ -205,7 +198,7 @@ class TextAttachmentController extends TextSendController {
} else { } else {
sendButton.setVisibility(VISIBLE); sendButton.setVisibility(VISIBLE);
// enable/disable buttons right away to allow fast sending // enable/disable buttons right away to allow fast sending
sendButton.setEnabled(true); sendButton.setEnabled(enabled);
imageButton.setEnabled(false); imageButton.setEnabled(false);
if (SDK_INT <= 15) { if (SDK_INT <= 15) {
imageButton.setVisibility(INVISIBLE); imageButton.setVisibility(INVISIBLE);
@@ -231,6 +224,7 @@ class TextAttachmentController extends TextSendController {
showImageButton(true); showImageButton(true);
} }
@Override
public Parcelable onSaveInstanceState(@Nullable Parcelable superState) { public Parcelable onSaveInstanceState(@Nullable Parcelable superState) {
SavedState state = SavedState state =
new SavedState(superState == null ? EMPTY_STATE : superState); new SavedState(superState == null ? EMPTY_STATE : superState);
@@ -238,8 +232,9 @@ class TextAttachmentController extends TextSendController {
return state; return state;
} }
@Override
@Nullable @Nullable
public Parcelable onRestoreInstanceState(Parcelable inState) { public Parcelable onRestoreInstanceState(@NonNull Parcelable inState) {
SavedState state = (SavedState) inState; SavedState state = (SavedState) inState;
imageUris = state.imageUris; imageUris = state.imageUris;
onNewUris(); onNewUris();

View File

@@ -6,7 +6,6 @@ import android.os.IBinder;
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.support.v7.widget.AppCompatImageButton; import android.support.v7.widget.AppCompatImageButton;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
@@ -21,9 +20,9 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import static android.content.Context.INPUT_METHOD_SERVICE; 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 android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static org.briarproject.bramble.util.StringUtils.utf8IsTooLong;
import static org.briarproject.briar.android.view.TextInputView.TextValidityListener; import static org.briarproject.briar.android.view.TextInputView.TextValidityListener;
@UiThread @UiThread
@@ -35,7 +34,8 @@ class TextInputController implements TextWatcher {
private final EmojiPopup emojiPopup; private final EmojiPopup emojiPopup;
private final EmojiEditText editText; private final EmojiEditText editText;
private @Nullable TextValidityListener listener; @Nullable
private TextValidityListener listener;
private int maxLength = Integer.MAX_VALUE; private int maxLength = Integer.MAX_VALUE;
private final boolean emptyTextAllowed; private final boolean emptyTextAllowed;
private boolean isEmpty = true; private boolean isEmpty = true;
@@ -66,15 +66,16 @@ class TextInputController implements TextWatcher {
@Override @Override
public void onTextChanged(CharSequence s, int start, int before, public void onTextChanged(CharSequence s, int start, int before,
int count) { int count) {
if (emptyTextAllowed || listener == null) return; // Need to start at position 0 to change empty
if (s.toString().trim().length() == 0) { if (start != 0 || emptyTextAllowed || listener == null) return;
if (s.length() == 0) {
if (!isEmpty) { if (!isEmpty) {
isEmpty = true; isEmpty = true;
listener.onTextValidityChanged(true); listener.onTextIsEmptyChanged(true);
} }
} else if (isEmpty) { } else if (isEmpty) {
isEmpty = false; isEmpty = false;
listener.onTextValidityChanged(false); listener.onTextIsEmptyChanged(false);
} }
} }
@@ -86,9 +87,13 @@ class TextInputController implements TextWatcher {
this.maxLength = maxLength; this.maxLength = maxLength;
} }
boolean isEmpty() {
return getText() == null;
}
boolean isTooLong() { boolean isTooLong() {
return editText.getText() != null && return editText.getText() != null &&
editText.getText().toString().trim().length() > maxLength; utf8IsTooLong(editText.getText().toString().trim(), maxLength);
} }
/** /**
@@ -98,9 +103,9 @@ class TextInputController implements TextWatcher {
@Nullable @Nullable
String getText() { String getText() {
Editable editable = editText.getText(); Editable editable = editText.getText();
if (editable == null || editable.toString().trim().length() == 0) String str = editable == null ? null : editable.toString().trim();
return null; if (str == null || str.length() == 0) return null;
return editable.toString().trim(); return str;
} }
void clearText() { void clearText() {
@@ -123,10 +128,6 @@ class TextInputController implements TextWatcher {
this.listener = listener; this.listener = listener;
} }
void showError() {
Snackbar.make(editText, R.string.text_too_long, LENGTH_SHORT).show();
}
boolean requestFocus(int direction, Rect previouslyFocusedRect) { boolean requestFocus(int direction, Rect previouslyFocusedRect) {
return editText.requestFocus(direction, previouslyFocusedRect); return editText.requestFocus(direction, previouslyFocusedRect);
} }

View File

@@ -14,8 +14,6 @@ import android.support.annotation.UiThread;
import android.support.v7.widget.AppCompatImageButton; import android.support.v7.widget.AppCompatImageButton;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import com.vanniktech.emoji.EmojiEditText; import com.vanniktech.emoji.EmojiEditText;
import com.vanniktech.emoji.RecentEmoji; import com.vanniktech.emoji.RecentEmoji;
@@ -43,12 +41,9 @@ public class TextInputView extends KeyboardAwareLinearLayout {
RecentEmoji recentEmoji; RecentEmoji recentEmoji;
TextInputController textInputController; TextInputController textInputController;
@Nullable
TextSendController textSendController; TextSendController textSendController;
EmojiEditText editText; EmojiEditText editText;
View sendButton;
@Nullable
TextAttachmentController attachmentController;
public TextInputView(Context context) { public TextInputView(Context context) {
this(context, null); this(context, null);
@@ -87,8 +82,6 @@ public class TextInputView extends KeyboardAwareLinearLayout {
String hint = attributes.getString(R.styleable.TextInputView_hint); String hint = attributes.getString(R.styleable.TextInputView_hint);
boolean allowEmptyText = attributes boolean allowEmptyText = attributes
.getBoolean(R.styleable.TextInputView_allowEmptyText, false); .getBoolean(R.styleable.TextInputView_allowEmptyText, false);
boolean supportsAttachments = attributes
.getBoolean(R.styleable.TextInputView_supportsAttachments, false);
attributes.recycle(); attributes.recycle();
// set up input controller // set up input controller
@@ -97,16 +90,34 @@ public class TextInputView extends KeyboardAwareLinearLayout {
textInputController = new TextInputController(this, emojiToggle, textInputController = new TextInputController(this, emojiToggle,
editText, recentEmoji, allowEmptyText); editText, recentEmoji, allowEmptyText);
if (hint != null) textInputController.setHint(hint); if (hint != null) textInputController.setHint(hint);
}
// set up sending controller @Nullable
sendButton = findViewById(R.id.btn_send); @Override
if (supportsAttachments) { protected Parcelable onSaveInstanceState() {
textSendController = new TextAttachmentController(this, sendButton, Parcelable superState = super.onSaveInstanceState();
textInputController); if (textSendController != null) {
} else { superState = textSendController.onSaveInstanceState(superState);
textSendController = new TextSendController(sendButton,
textInputController, allowEmptyText);
} }
return superState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (textSendController != null) {
Parcelable outState =
textSendController.onRestoreInstanceState(state);
super.onRestoreInstanceState(outState);
} else {
super.onRestoreInstanceState(state);
}
}
/**
* Call this in onCreate() before any other methods of this class.
*/
public <T extends TextSendController> void setSendController(T controller) {
textSendController = controller;
textInputController.setTextValidityListener(textSendController); textInputController.setTextValidityListener(textSendController);
// support sending with Ctrl+Enter // support sending with Ctrl+Enter
@@ -119,47 +130,15 @@ public class TextInputView extends KeyboardAwareLinearLayout {
}); });
} }
@Nullable public TextInputController getTextInputController() {
@Override return textInputController;
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
if (attachmentController != null) {
superState = attachmentController.onSaveInstanceState(superState);
}
return superState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (attachmentController != null) {
Parcelable outState =
attachmentController.onRestoreInstanceState(state);
super.onRestoreInstanceState(outState);
} else {
super.onRestoreInstanceState(state);
}
}
public void setListener(SendListener listener) {
textSendController.setSendListener(listener);
}
public void setAttachImageListener(AttachImageListener imageListener,
WindowManager windowManager) {
attachmentController = (TextAttachmentController) textSendController;
attachmentController.setAttachImageListener(imageListener, windowManager);
}
public void onImageReceived(@Nullable Intent resultData) {
if (attachmentController == null) throw new IllegalStateException();
attachmentController.onImageReceived(resultData);
} }
@Override @Override
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
super.setEnabled(enabled); super.setEnabled(enabled);
textInputController.setEnabled(enabled); textInputController.setEnabled(enabled);
textSendController.setEnabled(enabled); requireNonNull(textSendController).setEnabled(enabled);
} }
@Override @Override
@@ -195,7 +174,7 @@ public class TextInputView extends KeyboardAwareLinearLayout {
} }
interface TextValidityListener { interface TextValidityListener {
void onTextValidityChanged(boolean isEmpty); void onTextIsEmptyChanged(boolean isEmpty);
} }
public interface AttachImageListener { public interface AttachImageListener {

View File

@@ -1,62 +1,77 @@
package org.briarproject.briar.android.view; package org.briarproject.briar.android.view;
import android.os.Parcelable;
import android.support.annotation.CallSuper;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.design.widget.Snackbar;
import android.view.View; import android.view.View;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.view.TextInputView.SendListener; import org.briarproject.briar.android.view.TextInputView.SendListener;
import org.briarproject.briar.android.view.TextInputView.TextValidityListener; import org.briarproject.briar.android.view.TextInputView.TextValidityListener;
import static android.support.design.widget.Snackbar.LENGTH_SHORT;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
@UiThread @UiThread
@NotNullByDefault @NotNullByDefault
class TextSendController implements TextValidityListener { public class TextSendController implements TextValidityListener {
protected final TextInputController textInput; protected final TextInputController textInput;
protected final View sendButton; protected final View sendButton;
@Nullable protected final SendListener listener;
protected SendListener listener;
protected boolean enabled = true; protected boolean enabled = true;
private final boolean allowEmptyText; private final boolean allowEmptyText;
private boolean wasEmpty = true; private boolean wasEmpty = true;
TextSendController(View sendButton, TextInputController textInput, public TextSendController(TextInputView v, SendListener listener,
boolean allowEmptyText) { boolean allowEmptyText) {
this.sendButton = sendButton; this.sendButton = v.findViewById(R.id.btn_send);
this.sendButton.setOnClickListener(v -> onSendButtonClicked()); this.sendButton.setOnClickListener(view -> onSendButtonClicked());
this.sendButton.setEnabled(allowEmptyText); this.sendButton.setEnabled(allowEmptyText);
this.textInput = textInput; this.listener = listener;
this.textInput = v.getTextInputController();
this.allowEmptyText = allowEmptyText; this.allowEmptyText = allowEmptyText;
} }
@Override @Override
public void onTextValidityChanged(boolean isEmpty) { public void onTextIsEmptyChanged(boolean isEmpty) {
sendButton.setEnabled(enabled && !isEmpty); sendButton.setEnabled(enabled && !isEmpty);
wasEmpty = isEmpty; wasEmpty = isEmpty;
} }
@Nullable
public Parcelable onSaveInstanceState(@Nullable Parcelable superState) {
return superState;
}
@Nullable
public Parcelable onRestoreInstanceState(Parcelable state) {
return state;
}
@CallSuper
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
sendButton.setOnClickListener( sendButton.setEnabled(enabled && (!wasEmpty || allowEmptyText));
enabled ? v -> onSendButtonClicked() : null);
sendButton.setEnabled(!wasEmpty || allowEmptyText);
this.enabled = enabled; this.enabled = enabled;
} }
void setSendListener(SendListener listener) {
this.listener = listener;
}
void onSendButtonClicked() { void onSendButtonClicked() {
if (listener != null) { if (canSend()) {
if (textInput.isTooLong()) {
textInput.showError();
return;
}
listener.onSendClick(textInput.getText(), emptyList()); listener.onSendClick(textInput.getText(), emptyList());
} }
} }
protected boolean canSend() {
if (textInput.isTooLong()) {
Snackbar.make(sendButton, R.string.text_too_long, LENGTH_SHORT)
.show();
return false;
}
return enabled && (allowEmptyText || !textInput.isEmpty());
}
} }

View File

@@ -54,7 +54,6 @@
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>

View File

@@ -22,7 +22,6 @@
<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="allowEmptyText" format="boolean"/>
<attr name="supportsAttachments" format="boolean"/>
</declare-styleable> </declare-styleable>
<declare-styleable name="LargeTextInputView"> <declare-styleable name="LargeTextInputView">