Added search in draft

This commit is contained in:
M66B 2022-06-08 08:40:29 +02:00
parent 8a32f70378
commit e887264890
3 changed files with 154 additions and 1 deletions

View File

@ -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) {

View File

@ -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"

View File

@ -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"