1616import android .graphics .drawable .Drawable ;
1717import android .os .Build ;
1818import android .os .Bundle ;
19- import android .os .Handler ;
2019import android .text .TextUtils ;
2120import android .util .Log ;
2221import android .util .TypedValue ;
7170import net .gsantner .opoc .wrapper .GsTextWatcherAdapter ;
7271
7372import java .io .File ;
74- import java .lang .reflect .Field ;
7573
7674@ SuppressWarnings ({"UnusedReturnValue" })
7775@ SuppressLint ("NonConstantResourceId" )
@@ -242,7 +240,7 @@ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
242240 // This works well to preserve keyboard state.
243241 if (activity != null ) {
244242 final Window window = activity .getWindow ();
245- // Setting via a windowmanager state is much more robust than using show/hide
243+ // Setting via a window manager state is much more robust than using show/hide
246244 final int adjustResize = WindowManager .LayoutParams .SOFT_INPUT_ADJUST_RESIZE ;
247245 final int unchanged = WindowManager .LayoutParams .SOFT_INPUT_STATE_UNCHANGED | adjustResize ;
248246 final int hidden = WindowManager .LayoutParams .SOFT_INPUT_STATE_ALWAYS_HIDDEN | adjustResize ;
@@ -261,14 +259,12 @@ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
261259 });
262260 }
263261
264- _verticalScrollView .getViewTreeObserver ().addOnGlobalLayoutListener (() -> {
265- _hlEditor .post (() -> {
266- final int height = _verticalScrollView .getHeight ();
267- if (height != _hlEditor .getMinHeight ()) {
268- _hlEditor .setMinHeight (height );
269- }
270- });
271- });
262+ _verticalScrollView .getViewTreeObserver ().addOnGlobalLayoutListener (() -> _hlEditor .post (() -> {
263+ final int height = _verticalScrollView .getHeight ();
264+ if (height != _hlEditor .getMinHeight ()) {
265+ _hlEditor .setMinHeight (height );
266+ }
267+ }));
272268 }
273269
274270 @ Override
@@ -355,93 +351,7 @@ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
355351 menu .findItem (R .id .action_load_epub ).setVisible (isExperimentalFeaturesEnabled );
356352
357353 // SearchView (View Mode)
358- _menuSearchViewForViewMode = (SearchView ) menu .findItem (R .id .action_search_view ).getActionView ();
359- if (_menuSearchViewForViewMode != null ) {
360- _menuSearchViewForViewMode .setQueryHint (getString (R .string .search ));
361- _menuSearchViewForViewMode .setOnQueryTextListener (new SearchView .OnQueryTextListener () {
362- private String searchText = "" ;
363- private final Runnable searchTask = new Runnable () {
364- @ Override
365- public void run () {
366- _webView .findAllAsync (searchText );
367- searchText = "" ;
368- }
369- };
370-
371- @ Override
372- public boolean onQueryTextChange (String text ) {
373- Handler handler = _webView .getHandler ();
374- handler .removeCallbacks (searchTask );
375- searchText = text ;
376- handler .postDelayed (searchTask , 500 );
377- return true ;
378- }
379-
380- @ Override
381- public boolean onQueryTextSubmit (String query ) {
382- if (_menuSearchViewForViewMode .isSubmitButtonEnabled ()) {
383- _webView .findNext (true );
384- return true ;
385- }
386- return false ;
387- }
388- });
389-
390- ViewGroup mSearchPlate = null ;
391- try {
392- Field mSearchPlateField = SearchView .class .getDeclaredField ("mSearchPlate" );
393- mSearchPlateField .setAccessible (true );
394- mSearchPlate = (ViewGroup ) mSearchPlateField .get (_menuSearchViewForViewMode );
395- } catch (NoSuchFieldException e ) {
396- Log .e (DocumentEditAndViewFragment .class .getName (), e .getMessage () == null ? "" : e .getMessage ());
397- } catch (IllegalAccessException e ) {
398- throw new RuntimeException (e );
399- }
400- if (mSearchPlate == null ) {
401- _menuSearchViewForViewMode .setSubmitButtonEnabled (true );
402- return ;
403- }
404-
405- Context searchViewContext = _menuSearchViewForViewMode .getContext ();
406- LinearLayout linearLayout = new LinearLayout (searchViewContext );
407-
408- // Add search result TextView
409- TextView resultTextView = new TextView (searchViewContext );
410- resultTextView .setGravity (Gravity .CENTER );
411- LinearLayout .LayoutParams layoutParams = new LinearLayout .LayoutParams (
412- LinearLayout .LayoutParams .WRAP_CONTENT ,
413- LinearLayout .LayoutParams .MATCH_PARENT
414- );
415- layoutParams .setMarginEnd (14 );
416- resultTextView .setLayoutParams (layoutParams );
417- linearLayout .addView (resultTextView );
418-
419- // Add previous match Button
420- ImageButton previousButton = new ImageButton (searchViewContext );
421- previousButton .setImageResource (R .drawable .ic_baseline_keyboard_arrow_up_24 );
422- linearLayout .addView (previousButton );
423-
424- // Add next match Button
425- ImageButton nextButton = new ImageButton (searchViewContext );
426- nextButton .setImageResource (R .drawable .ic_baseline_keyboard_arrow_down_24 );
427- linearLayout .addView (nextButton );
428-
429- // Apply to SearchView
430- mSearchPlate .addView (linearLayout , 1 );
431-
432- // Set listeners
433- previousButton .setOnClickListener (v -> _webView .findNext (false ));
434- nextButton .setOnClickListener (v -> _webView .findNext (true ));
435- _webView .setFindListener ((activeMatchOrdinal , numberOfMatches , isDoneCounting ) -> {
436- if (isDoneCounting ) {
437- String searchResult = "" ;
438- if (numberOfMatches > 0 ) {
439- searchResult = (activeMatchOrdinal + 1 ) + "/" + numberOfMatches ;
440- }
441- resultTextView .setText (searchResult );
442- }
443- });
444- }
354+ setSearchView ((SearchView ) menu .findItem (R .id .action_search_view ).getActionView ());
445355
446356 // Set various initial states
447357 updateMenuToggleStates (_document .getFormat ());
@@ -780,6 +690,86 @@ private void showHideActionBar() {
780690 }
781691 }
782692
693+ private void setSearchView (SearchView searchView ) {
694+ _menuSearchViewForViewMode = searchView ;
695+ if (_menuSearchViewForViewMode == null ) {
696+ return ;
697+ }
698+
699+ _menuSearchViewForViewMode .setQueryHint (getString (R .string .search ));
700+ _menuSearchViewForViewMode .setOnQueryTextListener (new SearchView .OnQueryTextListener () {
701+ private String searchText = "" ;
702+ private final Runnable searchTask = TextViewUtils .makeDebounced (_webView .getHandler (), 500 , () -> _webView .findAllAsync (searchText ));
703+
704+ @ Override
705+ public boolean onQueryTextSubmit (String query ) {
706+ if (_menuSearchViewForViewMode .isSubmitButtonEnabled ()) {
707+ _webView .findNext (true );
708+ return true ;
709+ }
710+ return false ;
711+ }
712+
713+ @ Override
714+ public boolean onQueryTextChange (String text ) {
715+ searchText = text ;
716+ searchTask .run ();
717+ return true ;
718+ }
719+ });
720+
721+ // Because SearchView doesn't provide a public API to add custom buttons
722+ // We must get the searchPlate (the layout containing the text field and close button) from SearchView
723+ // This approach is more robust than reflection
724+ int searchPlateId = searchView .getContext ().getResources ().getIdentifier ("android:id/search_plate" , null , null );
725+ ViewGroup searchPlate = searchView .findViewById (searchPlateId );
726+ if (searchPlate == null ) {
727+ // Ensure that SearchView is always available even if getting searchPlate fails
728+ _menuSearchViewForViewMode .setSubmitButtonEnabled (true );
729+ return ;
730+ }
731+
732+ Context searchViewContext = _menuSearchViewForViewMode .getContext ();
733+ LinearLayout linearLayout = new LinearLayout (searchViewContext );
734+
735+ // Add search result TextView
736+ TextView resultTextView = new TextView (searchViewContext );
737+ resultTextView .setGravity (Gravity .CENTER );
738+ LinearLayout .LayoutParams layoutParams = new LinearLayout .LayoutParams (
739+ LinearLayout .LayoutParams .WRAP_CONTENT ,
740+ LinearLayout .LayoutParams .MATCH_PARENT
741+ );
742+ layoutParams .setMarginEnd (14 );
743+ resultTextView .setLayoutParams (layoutParams );
744+ linearLayout .addView (resultTextView );
745+
746+ // Add previous match Button
747+ ImageButton previousButton = new ImageButton (searchViewContext );
748+ previousButton .setImageResource (R .drawable .ic_baseline_keyboard_arrow_up_24 );
749+ linearLayout .addView (previousButton );
750+
751+ // Add next match Button
752+ ImageButton nextButton = new ImageButton (searchViewContext );
753+ nextButton .setImageResource (R .drawable .ic_baseline_keyboard_arrow_down_24 );
754+ linearLayout .addView (nextButton );
755+
756+ // Apply to SearchView
757+ searchPlate .addView (linearLayout , 1 );
758+
759+ // Set listeners
760+ previousButton .setOnClickListener (v -> _webView .findNext (false ));
761+ nextButton .setOnClickListener (v -> _webView .findNext (true ));
762+ _webView .setFindListener ((activeMatchOrdinal , numberOfMatches , isDoneCounting ) -> {
763+ if (isDoneCounting ) {
764+ String searchResult = "" ;
765+ if (numberOfMatches > 0 ) {
766+ searchResult = (activeMatchOrdinal + 1 ) + "/" + numberOfMatches ;
767+ }
768+ resultTextView .setText (searchResult );
769+ }
770+ });
771+ }
772+
783773 private void setMarginBottom (final View view , final int marginBottom ) {
784774 final ViewGroup .MarginLayoutParams params = (ViewGroup .MarginLayoutParams ) view .getLayoutParams ();
785775 if (params != null ) {
0 commit comments