Skip to content

Commit 30d60d5

Browse files
committed
PDF search.
Upping compose compile version to 1.5.7 Upping kotlin gradle plugin.
1 parent 417836d commit 30d60d5

16 files changed

+528
-11
lines changed

Diff for: app/src/main/java/org/zotero/android/architecture/ScreenArguments.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import org.zotero.android.pdf.annotation.data.PdfAnnotationArgs
44
import org.zotero.android.pdf.annotationmore.data.PdfAnnotationMoreArgs
55
import org.zotero.android.pdf.annotationmore.editpage.data.PdfAnnotationEditPageArgs
66
import org.zotero.android.pdf.colorpicker.data.PdfReaderColorPickerArgs
7+
import org.zotero.android.pdf.reader.pdfsearch.data.PdfReaderSearchArgs
78
import org.zotero.android.pdf.settings.data.PdfSettingsArgs
89
import org.zotero.android.pdffilter.data.PdfFilterArgs
910
import org.zotero.android.screens.addnote.data.AddOrEditNoteArgs
@@ -19,7 +20,6 @@ import org.zotero.android.screens.mediaviewer.video.VideoPlayerArgs
1920
import org.zotero.android.screens.share.sharecollectionpicker.data.ShareCollectionPickerArgs
2021
import org.zotero.android.screens.sortpicker.data.SortPickerArgs
2122
import org.zotero.android.screens.tagpicker.data.TagPickerArgs
22-
import org.zotero.android.uicomponents.addbyidentifier.data.AddByIdentifierPickerArgs
2323
import org.zotero.android.uicomponents.singlepicker.SinglePickerArgs
2424

2525
object ScreenArguments {
@@ -43,5 +43,5 @@ object ScreenArguments {
4343
lateinit var pdfAnnotationEditPageArgs: PdfAnnotationEditPageArgs
4444
lateinit var pdfReaderColorPickerArgs: PdfReaderColorPickerArgs
4545
lateinit var shareCollectionPickerArgs: ShareCollectionPickerArgs
46-
lateinit var addByIdentifierPickerArgs: AddByIdentifierPickerArgs
46+
lateinit var pdfReaderSearchArgs: PdfReaderSearchArgs
4747
}

Diff for: app/src/main/java/org/zotero/android/pdf/reader/PdfReaderModes.kt

+30
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import androidx.compose.animation.ContentTransform
77
import androidx.compose.animation.SizeTransform
88
import androidx.compose.animation.core.tween
99
import androidx.compose.animation.slideInHorizontally
10+
import androidx.compose.animation.slideInVertically
1011
import androidx.compose.animation.slideOutHorizontally
12+
import androidx.compose.animation.slideOutVertically
1113
import androidx.compose.animation.with
1214
import androidx.compose.foundation.background
1315
import androidx.compose.foundation.layout.Box
@@ -23,6 +25,7 @@ import androidx.compose.ui.Modifier
2325
import androidx.compose.ui.unit.IntOffset
2426
import androidx.compose.ui.unit.dp
2527
import org.zotero.android.architecture.ui.CustomLayoutSize
28+
import org.zotero.android.pdf.reader.pdfsearch.PdfReaderSearchScreen
2629
import org.zotero.android.pdf.reader.sidebar.PdfReaderSidebar
2730
import org.zotero.android.pdf.reader.sidebar.SidebarDivider
2831
import org.zotero.android.uicomponents.theme.CustomTheme
@@ -113,6 +116,21 @@ internal fun PdfReaderPhoneMode(
113116
}
114117
}
115118
}
119+
AnimatedContent(targetState = viewState.showPdfSearch, transitionSpec = {
120+
createPdfSearchTransitionSpec()
121+
}, label = "") { showScreen ->
122+
if (showScreen) {
123+
Column(
124+
modifier = Modifier
125+
.fillMaxSize()
126+
.background(CustomTheme.colors.pdfAnnotationsFormBackground)
127+
) {
128+
PdfReaderSearchScreen(
129+
onBack = vMInterface::hidePdfSearch
130+
)
131+
}
132+
}
133+
}
116134
}
117135
}
118136

@@ -127,3 +145,15 @@ private fun AnimatedContentTransitionScope<Boolean>.createSidebarTransitionSpec(
127145
sizeAnimationSpec = { _, _ -> tween() }
128146
))
129147
}
148+
149+
private fun AnimatedContentTransitionScope<Boolean>.createPdfSearchTransitionSpec(): ContentTransform {
150+
val intOffsetSpec = tween<IntOffset>()
151+
return (slideInVertically(intOffsetSpec) { it } with
152+
slideOutVertically(intOffsetSpec) { it }).using(
153+
// Disable clipping since the faded slide-in/out should
154+
// be displayed out of bounds.
155+
SizeTransform(
156+
clip = false,
157+
sizeAnimationSpec = { _, _ -> tween() }
158+
))
159+
}

Diff for: app/src/main/java/org/zotero/android/pdf/reader/PdfReaderScreen.kt

+4
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,13 @@ internal fun PdfReaderScreen(
126126
onBack = onBack,
127127
onShowHideSideBar = viewModel::toggleSideBar,
128128
toPdfSettings = viewModel::navigateToPdfSettings,
129+
showPdfSearch = viewState.showPdfSearch,
129130
toggleToolbarButton = viewModel::toggleToolbarButton,
130131
isToolbarButtonSelected = viewState.showCreationToolbar,
131132
showSideBar = viewState.showSideBar,
133+
onShowHidePdfSearch = viewModel::togglePdfSearch,
134+
viewModel = viewModel,
135+
viewState = viewState
132136
)
133137
}
134138
}

Diff for: app/src/main/java/org/zotero/android/pdf/reader/PdfReaderTopBar.kt

+33-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package org.zotero.android.pdf.reader
22

3+
import androidx.compose.foundation.layout.Box
34
import androidx.compose.runtime.Composable
45
import androidx.compose.ui.res.stringResource
6+
import org.zotero.android.architecture.ui.CustomLayoutSize
7+
import org.zotero.android.pdf.reader.pdfsearch.PdfReaderSearchPopup
58
import org.zotero.android.uicomponents.Drawables
69
import org.zotero.android.uicomponents.Strings
710
import org.zotero.android.uicomponents.icon.IconWithPadding
@@ -15,10 +18,15 @@ internal fun PdfReaderTopBar(
1518
onBack: () -> Unit,
1619
onShowHideSideBar: () -> Unit,
1720
toPdfSettings: () -> Unit,
21+
onShowHidePdfSearch: () -> Unit,
1822
toggleToolbarButton:() -> Unit,
1923
isToolbarButtonSelected: Boolean,
2024
showSideBar: Boolean,
25+
showPdfSearch: Boolean,
26+
viewState: PdfReaderViewState,
27+
viewModel: PdfReaderVMInterface
2128
) {
29+
val isTablet = CustomLayoutSize.calculateLayoutType().isTablet()
2230
NewCustomTopBar(
2331
backgroundColor = CustomTheme.colors.surface,
2432
leftContainerContent = listOf(
@@ -46,9 +54,33 @@ internal fun PdfReaderTopBar(
4654
isSelected = isToolbarButtonSelected
4755
)
4856
},
57+
{
58+
if (isTablet) {
59+
Box {
60+
IconWithPadding(
61+
drawableRes = Drawables.search_24px,
62+
onClick = onShowHidePdfSearch
63+
)
64+
if (viewState.showPdfSearch) {
65+
PdfReaderSearchPopup(
66+
viewState = viewState,
67+
viewModel = viewModel,
68+
)
69+
}
70+
}
71+
} else {
72+
ToggleIconWithPadding(
73+
drawableRes = Drawables.search_24px,
74+
onToggle = {
75+
onShowHidePdfSearch()
76+
},
77+
isSelected = showPdfSearch
78+
)
79+
}
80+
},
4981
{
5082
IconWithPadding(drawableRes = Drawables.settings_24px, onClick = toPdfSettings)
51-
}
83+
},
5284
)
5385
)
5486
}

Diff for: app/src/main/java/org/zotero/android/pdf/reader/PdfReaderVMInterface.kt

+2
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,6 @@ interface PdfReaderVMInterface {
4747
fun onOutlineItemChevronTapped(outline: Outline)
4848
fun selectThumbnail(row: PdfReaderThumbnailRow)
4949
fun loadThumbnailPreviews(pageIndex: Int)
50+
fun hidePdfSearch()
51+
fun togglePdfSearch()
5052
}

Diff for: app/src/main/java/org/zotero/android/pdf/reader/PdfReaderViewModel.kt

+36-2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import com.pspdfkit.preferences.PSPDFKitPreferences
4747
import com.pspdfkit.ui.PdfFragment
4848
import com.pspdfkit.ui.PdfUiFragment
4949
import com.pspdfkit.ui.PdfUiFragmentBuilder
50+
import com.pspdfkit.ui.search.SearchResultHighlighter
5051
import com.pspdfkit.ui.special_mode.controller.AnnotationCreationController
5152
import com.pspdfkit.ui.special_mode.controller.AnnotationSelectionController
5253
import com.pspdfkit.ui.special_mode.controller.AnnotationTool
@@ -135,6 +136,8 @@ import org.zotero.android.pdf.data.PdfAnnotationChanges
135136
import org.zotero.android.pdf.data.PdfReaderArgs
136137
import org.zotero.android.pdf.data.PdfReaderCurrentThemeEventStream
137138
import org.zotero.android.pdf.data.PdfReaderThemeDecider
139+
import org.zotero.android.pdf.reader.pdfsearch.data.PdfReaderSearchArgs
140+
import org.zotero.android.pdf.reader.pdfsearch.data.PdfReaderSearchResultSelected
138141
import org.zotero.android.pdf.reader.sidebar.data.Outline
139142
import org.zotero.android.pdf.reader.sidebar.data.PdfReaderOutlineOptionsWithChildren
140143
import org.zotero.android.pdf.reader.sidebar.data.PdfReaderSliderOptions
@@ -221,6 +224,8 @@ class PdfReaderViewModel @Inject constructor(
221224

222225
private var toolHistory = mutableListOf<AnnotationTool>()
223226

227+
private lateinit var searchResultHighlighter: SearchResultHighlighter
228+
224229
val screenArgs: PdfReaderArgs by lazy {
225230
val argsEncoded = stateHandle.get<String>(ARG_PDF_SCREEN).require()
226231
navigationParamsMarshaller.decodeObjectFromBase64(argsEncoded)
@@ -330,8 +335,13 @@ class PdfReaderViewModel @Inject constructor(
330335
)
331336
}
332337
}
338+
}
333339

334-
340+
@Subscribe(threadMode = ThreadMode.MAIN)
341+
fun onEvent(result: PdfReaderSearchResultSelected) {
342+
searchResultHighlighter.setSearchResults(listOf(result.searchResult))
343+
searchResultHighlighter.setSelectedSearchResult(result.searchResult)
344+
this.pdfUiFragment.pageIndex = result.searchResult.pageIndex
335345
}
336346

337347
private fun update(pdfSettings: PDFSettings) {
@@ -386,6 +396,8 @@ class PdfReaderViewModel @Inject constructor(
386396
this.containerId = containerId
387397
this.annotationMaxSideSize = annotationMaxSideSize
388398

399+
searchResultHighlighter = SearchResultHighlighter(context)
400+
389401
if (this::pdfUiFragment.isInitialized) {
390402
replaceFragment()
391403
return
@@ -413,6 +425,7 @@ class PdfReaderViewModel @Inject constructor(
413425
this@PdfReaderViewModel.pdfUiFragment.lifecycle.addObserver(object: DefaultLifecycleObserver {
414426
override fun onStart(owner: LifecycleOwner) {
415427
this@PdfReaderViewModel.pdfFragment = pdfUiFragment.pdfFragment!!
428+
this@PdfReaderViewModel.pdfFragment.addDrawableProvider(searchResultHighlighter)
416429
addDocumentListenerOnInit()
417430
addOnAnnotationCreationModeChangeListener()
418431
setOnPreparePopupToolbarListener()
@@ -2226,6 +2239,7 @@ class PdfReaderViewModel @Inject constructor(
22262239
this@PdfReaderViewModel.pdfUiFragment.lifecycle.addObserver(object: DefaultLifecycleObserver {
22272240
override fun onStart(owner: LifecycleOwner) {
22282241
this@PdfReaderViewModel.pdfFragment = pdfUiFragment.pdfFragment!!
2242+
this@PdfReaderViewModel.pdfFragment.addDrawableProvider(searchResultHighlighter)
22292243
addDocumentListener2()
22302244
addOnAnnotationCreationModeChangeListener()
22312245
setOnPreparePopupToolbarListener()
@@ -3190,6 +3204,25 @@ class PdfReaderViewModel @Inject constructor(
31903204
}
31913205

31923206
}
3207+
3208+
override fun togglePdfSearch() {
3209+
ScreenArguments.pdfReaderSearchArgs = PdfReaderSearchArgs(
3210+
pdfDocument = this.document,
3211+
configuration = pdfFragment.configuration
3212+
)
3213+
updateState {
3214+
copy(showPdfSearch = !showPdfSearch)
3215+
}
3216+
3217+
}
3218+
3219+
override fun hidePdfSearch() {
3220+
updateState {
3221+
copy(
3222+
showPdfSearch = false
3223+
)
3224+
}
3225+
}
31933226
}
31943227

31953228
data class PdfReaderViewState(
@@ -3229,7 +3262,8 @@ data class PdfReaderViewState(
32293262
val outlineSearchTerm: String = "",
32303263
val isOutlineEmpty: Boolean = false,
32313264
val thumbnailRows: ImmutableList<PdfReaderThumbnailRow> = persistentListOf(),
3232-
val selectedThumbnail: PdfReaderThumbnailRow? = null
3265+
val selectedThumbnail: PdfReaderThumbnailRow? = null,
3266+
val showPdfSearch: Boolean = false,
32333267
) : ViewState {
32343268

32353269
fun isAnnotationSelected(annotationKey: String): Boolean {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package org.zotero.android.pdf.reader.pdfsearch
2+
3+
import androidx.compose.foundation.layout.height
4+
import androidx.compose.foundation.layout.width
5+
import androidx.compose.foundation.shape.RoundedCornerShape
6+
import androidx.compose.runtime.Composable
7+
import androidx.compose.ui.Modifier
8+
import androidx.compose.ui.draw.shadow
9+
import androidx.compose.ui.platform.LocalDensity
10+
import androidx.compose.ui.unit.IntOffset
11+
import androidx.compose.ui.unit.IntRect
12+
import androidx.compose.ui.unit.IntSize
13+
import androidx.compose.ui.unit.LayoutDirection
14+
import androidx.compose.ui.unit.dp
15+
import androidx.compose.ui.window.Popup
16+
import androidx.compose.ui.window.PopupPositionProvider
17+
import androidx.compose.ui.window.PopupProperties
18+
import org.zotero.android.pdf.reader.PdfReaderVMInterface
19+
import org.zotero.android.pdf.reader.PdfReaderViewState
20+
import org.zotero.android.uicomponents.CustomScaffold
21+
import org.zotero.android.uicomponents.theme.CustomTheme
22+
23+
@Composable
24+
internal fun PdfReaderSearchPopup(
25+
viewState: PdfReaderViewState,
26+
viewModel: PdfReaderVMInterface,
27+
) {
28+
val backgroundColor = CustomTheme.colors.cardBackground
29+
Popup(
30+
properties = PopupProperties(
31+
dismissOnBackPress = true,
32+
dismissOnClickOutside = true,
33+
focusable = true
34+
),
35+
onDismissRequest = viewModel::hidePdfSearch,
36+
popupPositionProvider = createPdfReaderSearchPopupPositionProvider(),
37+
38+
) {
39+
CustomScaffold(
40+
modifier = Modifier
41+
.width(350.dp)
42+
.height(530.dp)
43+
.shadow(
44+
elevation = 4.dp,
45+
shape = RoundedCornerShape(16.dp),
46+
),
47+
backgroundColor = backgroundColor,
48+
) {
49+
PdfReaderSearchScreen(onBack = viewModel::hidePdfSearch)
50+
}
51+
}
52+
}
53+
54+
@Composable
55+
private fun createPdfReaderSearchPopupPositionProvider() = object : PopupPositionProvider {
56+
val localDensity = LocalDensity.current
57+
override fun calculatePosition(
58+
anchorBounds: IntRect,
59+
windowSize: IntSize,
60+
layoutDirection: LayoutDirection,
61+
popupContentSize: IntSize
62+
): IntOffset {
63+
val extraXOffset = with(localDensity) {
64+
12.dp.toPx()
65+
}.toInt()
66+
val extraYOffset = with(localDensity) {
67+
8.dp.toPx()
68+
}.toInt()
69+
70+
val xOffset = windowSize.width - popupContentSize.width - extraXOffset
71+
val yOffset = anchorBounds.bottom + extraYOffset
72+
73+
return IntOffset(
74+
x = xOffset,
75+
y = yOffset
76+
)
77+
}
78+
}

0 commit comments

Comments
 (0)