mirror of https://github.com/M66B/FairEmail.git
Added search in draft
This commit is contained in:
parent
8a32f70378
commit
e887264890
|
@ -49,6 +49,7 @@ import android.graphics.Color;
|
|||
import android.graphics.ImageDecoder;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.net.ConnectivityManager;
|
||||
|
@ -68,7 +69,9 @@ import android.security.KeyChain;
|
|||
import android.system.ErrnoException;
|
||||
import android.text.Editable;
|
||||
import android.text.Html;
|
||||
import android.text.Layout;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextPaint;
|
||||
|
@ -96,6 +99,7 @@ import android.view.MenuItem;
|
|||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Button;
|
||||
|
@ -107,6 +111,7 @@ import android.widget.ImageButton;
|
|||
import android.widget.ImageView;
|
||||
import android.widget.MultiAutoCompleteTextView;
|
||||
import android.widget.RadioGroup;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.SpinnerAdapter;
|
||||
import android.widget.TextView;
|
||||
|
@ -265,6 +270,7 @@ public class FragmentCompose extends FragmentBase {
|
|||
private ImageButton ibReferenceEdit;
|
||||
private ImageButton ibReferenceImages;
|
||||
private View vwAnchor;
|
||||
private TextViewAutoCompleteAction etSearch;
|
||||
private BottomNavigationView style_bar;
|
||||
private BottomNavigationView media_bar;
|
||||
private BottomNavigationView bottom_navigation;
|
||||
|
@ -306,6 +312,8 @@ public class FragmentCompose extends FragmentBase {
|
|||
private long[] pgpKeyIds;
|
||||
private long pgpSignKeyId;
|
||||
|
||||
private int searchIndex = 0;
|
||||
|
||||
private static final int REDUCED_IMAGE_SIZE = 1440; // pixels
|
||||
private static final int REDUCED_IMAGE_QUALITY = 90; // percent
|
||||
// http://regex.info/blog/lightroom-goodies/jpeg-quality
|
||||
|
@ -384,6 +392,7 @@ public class FragmentCompose extends FragmentBase {
|
|||
ibReferenceEdit = view.findViewById(R.id.ibReferenceEdit);
|
||||
ibReferenceImages = view.findViewById(R.id.ibReferenceImages);
|
||||
vwAnchor = view.findViewById(R.id.vwAnchor);
|
||||
etSearch = view.findViewById(R.id.etSearch);
|
||||
style_bar = view.findViewById(R.id.style_bar);
|
||||
media_bar = view.findViewById(R.id.media_bar);
|
||||
bottom_navigation = view.findViewById(R.id.bottom_navigation);
|
||||
|
@ -947,6 +956,49 @@ public class FragmentCompose extends FragmentBase {
|
|||
}
|
||||
});
|
||||
|
||||
etSearch.setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
if (!hasFocus)
|
||||
endSearch();
|
||||
}
|
||||
});
|
||||
|
||||
etSearch.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
endSearch();
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
etSearch.setActionRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
performSearch(true);
|
||||
}
|
||||
});
|
||||
|
||||
etSearch.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
performSearch(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
// Do nothing
|
||||
}
|
||||
});
|
||||
|
||||
style_bar.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
|
||||
@Override
|
||||
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
|
||||
|
@ -1036,6 +1088,7 @@ public class FragmentCompose extends FragmentBase {
|
|||
ibReferenceEdit.setVisibility(View.GONE);
|
||||
ibReferenceImages.setVisibility(View.GONE);
|
||||
tvReference.setVisibility(View.GONE);
|
||||
etSearch.setVisibility(View.GONE);
|
||||
style_bar.setVisibility(View.GONE);
|
||||
media_bar.setVisibility(View.GONE);
|
||||
bottom_navigation.setVisibility(View.GONE);
|
||||
|
@ -1891,6 +1944,9 @@ public class FragmentCompose extends FragmentBase {
|
|||
} else if (itemId == R.id.menu_answer_create) {
|
||||
onMenuAnswerCreate();
|
||||
return true;
|
||||
} else if (itemId == R.id.title_search_in_text) {
|
||||
startSearch();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_clear) {
|
||||
StyleHelper.apply(R.id.menu_clear, getViewLifecycleOwner(), null, etBody);
|
||||
return true;
|
||||
|
@ -6925,6 +6981,80 @@ public class FragmentCompose extends FragmentBase {
|
|||
return -1;
|
||||
}
|
||||
|
||||
private void startSearch() {
|
||||
etSearch.setText(null);
|
||||
etSearch.setVisibility(View.VISIBLE);
|
||||
etSearch.requestFocus();
|
||||
Helper.showKeyboard(etSearch);
|
||||
}
|
||||
|
||||
private void endSearch() {
|
||||
Helper.hideKeyboard(etSearch);
|
||||
etSearch.setVisibility(View.GONE);
|
||||
clearSearch();
|
||||
}
|
||||
|
||||
private void performSearch(boolean next) {
|
||||
clearSearch();
|
||||
|
||||
searchIndex = (next ? searchIndex + 1 : 1);
|
||||
String query = etSearch.getText().toString().toLowerCase();
|
||||
String text = etBody.getText().toString().toLowerCase();
|
||||
|
||||
int pos = -1;
|
||||
for (int i = 0; i < searchIndex; i++)
|
||||
pos = (pos < 0 ? text.indexOf(query) : text.indexOf(query, pos + 1));
|
||||
|
||||
// Wrap around
|
||||
if (pos < 0 && searchIndex > 1) {
|
||||
searchIndex = 1;
|
||||
pos = text.indexOf(query);
|
||||
}
|
||||
|
||||
// Scroll to found text
|
||||
if (pos >= 0) {
|
||||
Context context = etBody.getContext();
|
||||
int color = Helper.resolveColor(context, R.attr.colorHighlight);
|
||||
SpannableString ss = new SpannableString(etBody.getText());
|
||||
ss.setSpan(new BackgroundColorSpan(color),
|
||||
pos, pos + query.length(), Spannable.SPAN_COMPOSING);
|
||||
ss.setSpan(new RelativeSizeSpan(HtmlHelper.FONT_LARGE),
|
||||
pos, pos + query.length(), Spannable.SPAN_COMPOSING);
|
||||
etBody.setText(ss);
|
||||
|
||||
Layout layout = etBody.getLayout();
|
||||
if (layout != null) {
|
||||
int line = layout.getLineForOffset(pos);
|
||||
int y = layout.getLineTop(line);
|
||||
int dy = context.getResources().getDimensionPixelSize(R.dimen.search_in_text_margin);
|
||||
|
||||
Rect rect = new Rect();
|
||||
etBody.getDrawingRect(rect);
|
||||
ScrollView scroll = view.findViewById(R.id.scroll);
|
||||
scroll.offsetDescendantRectToMyCoords(etBody, rect);
|
||||
scroll.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
scroll.scrollTo(0, rect.top + y - dy);
|
||||
} catch (Throwable ex) {
|
||||
Log.e(ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
boolean hasNext = (pos >= 0 &&
|
||||
(text.indexOf(query) != pos ||
|
||||
text.indexOf(query, pos + 1) >= 0));
|
||||
etSearch.setActionEnabled(hasNext);
|
||||
}
|
||||
|
||||
private void clearSearch() {
|
||||
etBody.clearComposingText();
|
||||
}
|
||||
|
||||
private AdapterView.OnItemSelectedListener identitySelected = new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<eu.faircode.email.ScrollViewEx
|
||||
android:id="@+id/scroll"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/media_bar"
|
||||
|
@ -561,9 +562,25 @@
|
|||
android:id="@+id/vwAnchor"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/style_bar"
|
||||
app:layout_constraintBottom_toTopOf="@+id/etSearch"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<eu.faircode.email.TextViewAutoCompleteAction
|
||||
android:id="@+id/etSearch"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/colorSeparator"
|
||||
android:completionThreshold="2"
|
||||
android:hint="@string/title_search_for_hint"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="text"
|
||||
android:maxLines="1"
|
||||
android:padding="6dp"
|
||||
app:end_drawable="@drawable/twotone_fast_forward_24"
|
||||
app:layout_constraintBottom_toTopOf="@id/style_bar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
android:id="@+id/style_bar"
|
||||
android:layout_width="0dp"
|
||||
|
|
|
@ -98,6 +98,12 @@
|
|||
<menu />
|
||||
</item>
|
||||
|
||||
<item
|
||||
android:id="@+id/title_search_in_text"
|
||||
android:icon="@drawable/twotone_manage_search_24"
|
||||
android:title="@string/title_button_search_text"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_clear"
|
||||
android:icon="@drawable/twotone_format_clear_24"
|
||||
|
|
Loading…
Reference in New Issue