#550 In-note-search doesn't jump to occurrence of searchstring

- Fix crash on pressing prev button too often
- Fix scrolling behavior in preview fragment
This commit is contained in:
stefan-niedermann 2020-01-23 08:58:27 +01:00 committed by Niedermann IT-Dienstleistungen
parent f5a2baaf8f
commit 64b09198f9
4 changed files with 57 additions and 53 deletions

View file

@ -1,7 +1,7 @@
package it.niedermann.owncloud.notes.android.fragment; package it.niedermann.owncloud.notes.android.fragment;
import android.app.Activity;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutInfo;
@ -22,6 +22,7 @@ import android.widget.LinearLayout;
import android.widget.ScrollView; import android.widget.ScrollView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.SearchView;
import androidx.appcompat.widget.ShareActionProvider; import androidx.appcompat.widget.ShareActionProvider;
@ -30,6 +31,7 @@ import androidx.core.view.ViewCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException;
import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException; import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException;
import com.nextcloud.android.sso.helper.SingleAccountHelper; import com.nextcloud.android.sso.helper.SingleAccountHelper;
@ -131,14 +133,14 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo
} }
@Override @Override
public void onAttach(Activity activity) { public void onAttach(@NonNull Context context) {
super.onAttach(activity); super.onAttach(context);
try { try {
listener = (NoteFragmentListener) activity; listener = (NoteFragmentListener) context;
} catch (ClassCastException e) { } catch (ClassCastException e) {
throw new ClassCastException(activity.getClass() + " must implement " + NoteFragmentListener.class); throw new ClassCastException(context.getClass() + " must implement " + NoteFragmentListener.class);
} }
db = NoteSQLiteOpenHelper.getInstance(activity); db = NoteSQLiteOpenHelper.getInstance(context);
} }
@Override @Override
@ -160,7 +162,7 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo
} }
@Override @Override
public void onSaveInstanceState(Bundle outState) { public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
saveNote(null); saveNote(null);
outState.putSerializable(SAVEDKEY_NOTE, note); outState.putSerializable(SAVEDKEY_NOTE, note);
@ -180,10 +182,10 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo
} }
@Override @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_note_fragment, menu); inflater.inflate(R.menu.menu_note_fragment, menu);
if (isRequestPinShortcutSupported(getActivity()) && android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (isRequestPinShortcutSupported(Objects.requireNonNull(getActivity())) && android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
menu.add(Menu.NONE, MENU_ID_PIN, 110, R.string.pin_to_homescreen); menu.add(Menu.NONE, MENU_ID_PIN, 110, R.string.pin_to_homescreen);
} }
} }
@ -191,7 +193,7 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo
private int occurence = 1; private int occurence = 1;
@Override @Override
public void onPrepareOptionsMenu(Menu menu) { public void onPrepareOptionsMenu(@NonNull Menu menu) {
super.onPrepareOptionsMenu(menu); super.onPrepareOptionsMenu(menu);
MenuItem itemFavorite = menu.findItem(R.id.menu_favorite); MenuItem itemFavorite = menu.findItem(R.id.menu_favorite);
prepareFavoriteOption(itemFavorite); prepareFavoriteOption(itemFavorite);
@ -234,12 +236,18 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo
View next = getSearchNextButton(); View next = getSearchNextButton();
if (next != null) { if (next != null) {
next.setOnClickListener(v -> jumpToNthNote(searchQuery, occurence, true)); next.setOnClickListener(v -> {
occurence++;
jumpToNthNote(searchQuery);
});
} }
View prev = getSearchPrevButton(); View prev = getSearchPrevButton();
if (prev != null) { if (prev != null) {
prev.setOnClickListener(v -> jumpToNthNote(searchQuery, occurence, false)); prev.setOnClickListener(v -> {
occurence--;
jumpToNthNote(searchQuery);
});
} }
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@ -250,27 +258,27 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo
@Override @Override
public boolean onQueryTextChange(String newText) { public boolean onQueryTextChange(String newText) {
occurence = 1;
searchQuery = newText; searchQuery = newText;
colorWithText(newText); colorWithText(newText);
occurence = 1;
jumpToNthNote(newText); jumpToNthNote(newText);
return true; return true;
} }
}); });
} }
private void jumpToNthNote(String newText) { private void jumpToNthNote(String newText) {
jumpToNthNote(newText, 1, true);
}
private void jumpToNthNote(String newText, int occurrence, boolean directionForward) {
if (newText == null || newText.isEmpty()) { if (newText == null || newText.isEmpty()) {
// No search term // No search term
return; return;
} }
if (occurence < 1) {
// TODO find last occurence
occurence = 1;
return;
}
String currentContent = getContent().toLowerCase(); String currentContent = getContent().toLowerCase();
int indexOfNewText = indexOfNth(currentContent, newText.toLowerCase(), 0, occurrence); int indexOfNewText = indexOfNth(currentContent, newText.toLowerCase(), 0, occurence);
if (indexOfNewText <= 0) { if (indexOfNewText <= 0) {
// Search term not in text // Search term not in text
occurence = 1; occurence = 1;
@ -288,11 +296,6 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo
if (numberLine >= 0) { if (numberLine >= 0) {
getScrollView().smoothScrollTo(0, getLayout().getLineTop(numberLine)); getScrollView().smoothScrollTo(0, getLayout().getLineTop(numberLine));
} }
if(directionForward) {
this.occurence++;
} else {
this.occurence--;
}
} }
private static int indexOfNth(String input, String value, int startIndex, int nth) { private static int indexOfNth(String input, String value, int startIndex, int nth) {
@ -310,8 +313,9 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo
protected abstract Layout getLayout(); protected abstract Layout getLayout();
protected abstract View getSearchNextButton(); protected abstract FloatingActionButton getSearchNextButton();
protected abstract View getSearchPrevButton();
protected abstract FloatingActionButton getSearchPrevButton();
private void prepareFavoriteOption(MenuItem item) { private void prepareFavoriteOption(MenuItem item) {
item.setIcon(note.isFavorite() ? R.drawable.ic_star_white_24dp : R.drawable.ic_star_border_white_24dp); item.setIcon(note.isFavorite() ? R.drawable.ic_star_white_24dp : R.drawable.ic_star_border_white_24dp);

View file

@ -113,12 +113,12 @@ public class NoteEditFragment extends BaseNoteFragment {
} }
@Override @Override
protected View getSearchNextButton() { protected FloatingActionButton getSearchNextButton() {
return searchNext; return searchNext;
} }
@Override @Override
protected View getSearchPrevButton() { protected FloatingActionButton getSearchPrevButton() {
return searchPrev; return searchPrev;
} }

View file

@ -80,12 +80,12 @@ public class NotePreviewFragment extends BaseNoteFragment {
} }
@Override @Override
protected View getSearchNextButton() { protected FloatingActionButton getSearchNextButton() {
return searchNext; return searchNext;
} }
@Override @Override
protected View getSearchPrevButton() { protected FloatingActionButton getSearchPrevButton() {
return searchPrev; return searchPrev;
} }

View file

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/swiperefreshlayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:background="@color/bg_normal"
tools:context="it.niedermann.owncloud.notes.android.activity.NotesListViewActivity"
tools:ignore="MergeRootFrame">
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swiperefreshlayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:background="@color/bg_normal"
tools:context="it.niedermann.owncloud.notes.android.activity.NotesListViewActivity"
tools:ignore="MergeRootFrame">
<ScrollView <ScrollView
android:id="@+id/editContentContainer" android:id="@+id/editContentContainer"
@ -30,20 +30,20 @@
android:textColor="@color/fg_default" android:textColor="@color/fg_default"
android:textIsSelectable="true" /> android:textIsSelectable="true" />
</ScrollView> </ScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/searchPrev" android:id="@+id/searchPrev"
style="@style/fab" style="@style/fab"
android:layout_gravity="bottom|end" android:layout_gravity="bottom|end"
android:translationY="-56dp" android:translationY="-56dp"
app:fabSize="mini" app:fabSize="mini"
app:srcCompat="@drawable/ic_keyboard_arrow_up_white_24dp" /> app:srcCompat="@drawable/ic_keyboard_arrow_up_white_24dp" />
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/searchNext" android:id="@+id/searchNext"
style="@style/fab" style="@style/fab"
android:layout_gravity="bottom|end" android:layout_gravity="bottom|end"
app:fabSize="mini" app:fabSize="mini"
app:srcCompat="@drawable/ic_keyboard_arrow_down_white_24dp" /> app:srcCompat="@drawable/ic_keyboard_arrow_down_white_24dp" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>