├── .editorconfig
├── .github
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE
│ ├── Bug_report.md
│ └── Feature_request.md
├── PULL_REQUEST_TEMPLATE.md
├── semantic.yml
└── workflows
│ ├── build.yml
│ └── deploy.yml
├── .gitignore
├── .tool-versions
├── CHANGELOG.md
├── CODEOWNERS
├── LICENSE
├── README.md
├── build.gradle.kts
├── docs
├── guide
│ ├── Migration_2.5.x_2.6.x.md
│ └── Migration_2.x_3.x.md
└── img
│ ├── banner.png
│ ├── codex
│ ├── categories_hits.gif
│ ├── multiple_index.gif
│ ├── query_suggestions.gif
│ ├── query_suggestions_categories.gif
│ ├── query_suggestions_hits.gif
│ ├── query_suggestions_recent.gif
│ └── voice_search.gif
│ ├── examples
│ ├── android.png
│ ├── androidtv.png
│ └── wearos.png
│ ├── guide
│ ├── declarative_ui.gif
│ ├── imperative_ui.gif
│ ├── suggestions.gif
│ └── voice_search.gif
│ └── showcase
│ ├── showcase-compose.gif
│ └── showcase-view.gif
├── examples
├── README.md
├── android
│ ├── README.md
│ ├── build.gradle
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin
│ │ └── com
│ │ │ └── algolia
│ │ │ └── instantsearch
│ │ │ └── examples
│ │ │ └── android
│ │ │ ├── App.kt
│ │ │ ├── Theme.kt
│ │ │ ├── codex
│ │ │ ├── categorieshits
│ │ │ │ ├── MainActivity.kt
│ │ │ │ ├── MainViewModel.kt
│ │ │ │ ├── Product.kt
│ │ │ │ ├── README.md
│ │ │ │ └── SearchScreen.kt
│ │ │ ├── multipleindex
│ │ │ │ ├── Actor.kt
│ │ │ │ ├── MainActivity.kt
│ │ │ │ ├── MainViewModel.kt
│ │ │ │ ├── Movie.kt
│ │ │ │ ├── README.md
│ │ │ │ └── SearchScreen.kt
│ │ │ ├── multisearch
│ │ │ │ ├── Actor.kt
│ │ │ │ ├── ActorAdapter.kt
│ │ │ │ ├── MainActivity.kt
│ │ │ │ ├── MainViewModel.kt
│ │ │ │ ├── Movie.kt
│ │ │ │ ├── MovieAdapter.kt
│ │ │ │ └── README.md
│ │ │ ├── suggestions
│ │ │ │ ├── categories
│ │ │ │ │ ├── MainActivity.kt
│ │ │ │ │ ├── MainViewModel.kt
│ │ │ │ │ ├── README.md
│ │ │ │ │ ├── SearchScreen.kt
│ │ │ │ │ └── Suggestion.kt
│ │ │ │ ├── hits
│ │ │ │ │ ├── MainActivity.kt
│ │ │ │ │ ├── MainViewModel.kt
│ │ │ │ │ ├── Product.kt
│ │ │ │ │ ├── README.md
│ │ │ │ │ ├── SearchScreen.kt
│ │ │ │ │ └── Suggestion.kt
│ │ │ │ ├── query
│ │ │ │ │ ├── MainActivity.kt
│ │ │ │ │ ├── MainViewModel.kt
│ │ │ │ │ ├── README.md
│ │ │ │ │ ├── SearchScreen.kt
│ │ │ │ │ └── Suggestion.kt
│ │ │ │ └── recent
│ │ │ │ │ ├── MainActivity.kt
│ │ │ │ │ ├── MainViewModel.kt
│ │ │ │ │ ├── README.md
│ │ │ │ │ ├── SearchScreen.kt
│ │ │ │ │ └── Suggestion.kt
│ │ │ └── voice
│ │ │ │ ├── MainActivity.kt
│ │ │ │ ├── MainViewModel.kt
│ │ │ │ ├── Product.kt
│ │ │ │ ├── README.md
│ │ │ │ └── SearchScreen.kt
│ │ │ ├── directory
│ │ │ ├── Directory.kt
│ │ │ ├── DirectoryActivity.kt
│ │ │ ├── DirectoryAdapter.kt
│ │ │ └── DirectoryItem.kt
│ │ │ ├── guides
│ │ │ ├── compose
│ │ │ │ ├── ComposeActivity.kt
│ │ │ │ ├── MainViewModel.kt
│ │ │ │ ├── README.md
│ │ │ │ ├── SearchScreen.kt
│ │ │ │ └── theme
│ │ │ │ │ ├── Color.kt
│ │ │ │ │ ├── Shape.kt
│ │ │ │ │ ├── Theme.kt
│ │ │ │ │ └── Type.kt
│ │ │ ├── extension
│ │ │ │ ├── RecyclerView.kt
│ │ │ │ └── SearchView.kt
│ │ │ ├── gettingstarted
│ │ │ │ ├── FacetFragment.kt
│ │ │ │ ├── GettingStartedGuide.kt
│ │ │ │ ├── MyFacetListViewHolder.kt
│ │ │ │ ├── MyViewModel.kt
│ │ │ │ ├── ProductAdapter.kt
│ │ │ │ ├── ProductFragment.kt
│ │ │ │ └── README.md
│ │ │ ├── insights
│ │ │ │ ├── InsightsActivity.kt
│ │ │ │ ├── ListItemAdapter.kt
│ │ │ │ └── README.md
│ │ │ └── model
│ │ │ │ ├── Product.kt
│ │ │ │ └── Suggestion.kt
│ │ │ └── showcase
│ │ │ ├── androidview
│ │ │ ├── Demo.kt
│ │ │ ├── README.md
│ │ │ ├── customdata
│ │ │ │ ├── Banner.kt
│ │ │ │ └── QueryRuleCustomDataShowcase.kt
│ │ │ ├── directory
│ │ │ │ ├── AndroidViewDirectoryShowcase.kt
│ │ │ │ └── Directory.kt
│ │ │ ├── filter
│ │ │ │ ├── clear
│ │ │ │ │ └── FilterClearShowcase.kt
│ │ │ │ ├── current
│ │ │ │ │ └── ShowcaseFilterCurrent.kt
│ │ │ │ ├── facet
│ │ │ │ │ ├── FacetListPersistentShowcase.kt
│ │ │ │ │ ├── FacetListSearchShowcase.kt
│ │ │ │ │ ├── FacetListShowcase.kt
│ │ │ │ │ ├── FacetListViewHolderImpl.kt
│ │ │ │ │ └── dynamic
│ │ │ │ │ │ ├── DynamicFacetShowcase.kt
│ │ │ │ │ │ └── ViewHolderFactory.kt
│ │ │ │ ├── list
│ │ │ │ │ ├── FilterListAdapter.kt
│ │ │ │ │ ├── FilterListFacetShowcase.kt
│ │ │ │ │ ├── FilterListNumericShowcase.kt
│ │ │ │ │ ├── FilterListTagShowcase.kt
│ │ │ │ │ └── FilterListViewHolder.kt
│ │ │ │ ├── map
│ │ │ │ │ └── FilterMapShowcase.kt
│ │ │ │ ├── numeric
│ │ │ │ │ └── comparison
│ │ │ │ │ │ ├── FilterComparisonShowcase.kt
│ │ │ │ │ │ ├── FilterPriceView.kt
│ │ │ │ │ │ └── FilterYearView.kt
│ │ │ │ ├── range
│ │ │ │ │ ├── BoundsTextView.kt
│ │ │ │ │ ├── FilterRangeShowcase.kt
│ │ │ │ │ ├── RangeSliderView.kt
│ │ │ │ │ └── RangeTextView.kt
│ │ │ │ ├── rating
│ │ │ │ │ ├── RatingBarView.kt
│ │ │ │ │ ├── RatingShowcase.kt
│ │ │ │ │ └── RatingTextView.kt
│ │ │ │ └── toggle
│ │ │ │ │ └── FilterToggleShowcase.kt
│ │ │ ├── hierarchical
│ │ │ │ ├── HierarchicalAdapter.kt
│ │ │ │ ├── HierarchicalShowcase.kt
│ │ │ │ └── HierarchicalViewHolder.kt
│ │ │ ├── highlighting
│ │ │ │ ├── HighlightingAdapter.kt
│ │ │ │ ├── HighlightingShowcase.kt
│ │ │ │ └── HighlightingViewHolder.kt
│ │ │ ├── list
│ │ │ │ ├── actor
│ │ │ │ │ ├── Actor.kt
│ │ │ │ │ ├── ActorAdapterNested.kt
│ │ │ │ │ ├── ActorViewHolder.kt
│ │ │ │ │ └── ActorViewHolderSmall.kt
│ │ │ │ ├── header
│ │ │ │ │ └── HeaderViewHolder.kt
│ │ │ │ ├── movie
│ │ │ │ │ ├── Movie.kt
│ │ │ │ │ ├── MovieAdapter.kt
│ │ │ │ │ ├── MovieAdapterNested.kt
│ │ │ │ │ ├── MovieAdapterPaged.kt
│ │ │ │ │ ├── MovieDiffUtil.kt
│ │ │ │ │ ├── MovieViewHolder.kt
│ │ │ │ │ └── MovieViewHolderNested.kt
│ │ │ │ ├── paging
│ │ │ │ │ ├── PagingMultiSearchAdapter.kt
│ │ │ │ │ ├── PagingMultipleIndexItem.kt
│ │ │ │ │ ├── PagingMultipleIndexShowcase.kt
│ │ │ │ │ ├── PagingMultipleIndexViewHolder.kt
│ │ │ │ │ └── PagingSingleIndexShowcase.kt
│ │ │ │ ├── product
│ │ │ │ │ ├── Product.kt
│ │ │ │ │ └── ProductAdapter.kt
│ │ │ │ └── suggestion
│ │ │ │ │ └── Suggestion.kt
│ │ │ ├── loading
│ │ │ │ └── LoadingShowcase.kt
│ │ │ ├── relateditems
│ │ │ │ └── RelatedItemsShowcase.kt
│ │ │ ├── search
│ │ │ │ ├── SearchAsYouTypeShowcase.kt
│ │ │ │ └── SearchOnSubmitShowcase.kt
│ │ │ ├── sortby
│ │ │ │ └── SortByShowcase.kt
│ │ │ └── stats
│ │ │ │ └── StatsShowcase.kt
│ │ │ ├── compose
│ │ │ ├── Demo.kt
│ │ │ ├── README.md
│ │ │ ├── customdata
│ │ │ │ └── QueryRuleCustomDataShowcase.kt
│ │ │ ├── directory
│ │ │ │ ├── ComposeDirectoryShowcase.kt
│ │ │ │ ├── Directory.kt
│ │ │ │ └── DirectoryScreen.kt
│ │ │ ├── filter
│ │ │ │ ├── clear
│ │ │ │ │ └── FilterClearShowcase.kt
│ │ │ │ ├── current
│ │ │ │ │ └── FilterCurrentShowcase.kt
│ │ │ │ ├── facet
│ │ │ │ │ ├── DynamicFacetShowcase.kt
│ │ │ │ │ ├── FacetListPersistentShowcase.kt
│ │ │ │ │ ├── FacetListSearchShowcase.kt
│ │ │ │ │ └── FacetListShowcase.kt
│ │ │ │ ├── list
│ │ │ │ │ ├── FilterListFacetShowcase.kt
│ │ │ │ │ ├── FilterListNumericShowcase.kt
│ │ │ │ │ ├── FilterListScreen.kt
│ │ │ │ │ └── FilterListTagShowcase.kt
│ │ │ │ ├── map
│ │ │ │ │ └── FilterMapShowcase.kt
│ │ │ │ ├── numeric
│ │ │ │ │ └── comparison
│ │ │ │ │ │ └── FilterComparisonShowcase.kt
│ │ │ │ ├── range
│ │ │ │ │ ├── FilterRangeShowcase.kt
│ │ │ │ │ └── RangeUI.kt
│ │ │ │ ├── rating
│ │ │ │ │ └── RatingShowcase.kt
│ │ │ │ └── toggle
│ │ │ │ │ └── FilterToggleShowcase.kt
│ │ │ ├── hierarchical
│ │ │ │ └── HierarchicalShowcase.kt
│ │ │ ├── highlighting
│ │ │ │ └── HighlightingShowcase.kt
│ │ │ ├── list
│ │ │ │ └── paging
│ │ │ │ │ ├── PagingMultipleIndexShowcase.kt
│ │ │ │ │ └── PagingSingleIndexShowcase.kt
│ │ │ ├── loading
│ │ │ │ └── LoadingShowcase.kt
│ │ │ ├── model
│ │ │ │ ├── Actor.kt
│ │ │ │ ├── Banner.kt
│ │ │ │ ├── Movie.kt
│ │ │ │ └── Product.kt
│ │ │ ├── relateditems
│ │ │ │ └── RelatedItemsShowcase.kt
│ │ │ ├── search
│ │ │ │ ├── SearchAsYouTypeShowcase.kt
│ │ │ │ └── SearchOnSubmitShowcase.kt
│ │ │ ├── sortby
│ │ │ │ └── SortByShowcase.kt
│ │ │ ├── stats
│ │ │ │ └── StatsShowcase.kt
│ │ │ └── ui
│ │ │ │ ├── Color.kt
│ │ │ │ ├── Shape.kt
│ │ │ │ ├── Theme.kt
│ │ │ │ ├── Type.kt
│ │ │ │ └── component
│ │ │ │ ├── ActorsList.kt
│ │ │ │ ├── AutoCompleteTextField.kt
│ │ │ │ ├── DropdownTextField.kt
│ │ │ │ ├── FacetList.kt
│ │ │ │ ├── FilterChips.kt
│ │ │ │ ├── FilterList.kt
│ │ │ │ ├── HeaderFilter.kt
│ │ │ │ ├── HeaderFilterConnector.kt
│ │ │ │ ├── HelpDialog.kt
│ │ │ │ ├── MoviesList.kt
│ │ │ │ ├── ProductList.kt
│ │ │ │ ├── RatingBar.kt
│ │ │ │ ├── RestoreFab.kt
│ │ │ │ ├── SearchBox.kt
│ │ │ │ └── TopBar.kt
│ │ │ └── shared
│ │ │ └── customdata
│ │ │ └── TemplateActivity.kt
│ │ └── res
│ │ ├── drawable-anydpi
│ │ └── ic_filter_list.xml
│ │ ├── drawable
│ │ ├── ic_add.xml
│ │ ├── ic_add_shopping_cart.xml
│ │ ├── ic_arrow_down.xml
│ │ ├── ic_arrow_up.xml
│ │ ├── ic_arrow_up_right.xml
│ │ ├── ic_check.xml
│ │ ├── ic_close_hint.xml
│ │ ├── ic_delete.xml
│ │ ├── ic_info.xml
│ │ ├── ic_microphone_outline.xml
│ │ ├── ic_remove.xml
│ │ ├── ic_restore.xml
│ │ ├── ic_search_hint.xml
│ │ ├── query_background.xml
│ │ ├── rectangle.xml
│ │ └── thumb.xml
│ │ ├── layout
│ │ ├── activity_directory.xml
│ │ ├── activity_getting_started.xml
│ │ ├── activity_insights.xml
│ │ ├── activity_query_suggestion.xml
│ │ ├── autocompletetextfield.xml
│ │ ├── filter_chip.xml
│ │ ├── fragment_facet.xml
│ │ ├── fragment_items.xml
│ │ ├── fragment_product.xml
│ │ ├── header_filter.xml
│ │ ├── header_item.xml
│ │ ├── include_list.xml
│ │ ├── include_plus_minus.xml
│ │ ├── include_search.xml
│ │ ├── include_search_info.xml
│ │ ├── list_facet_selectable.xml
│ │ ├── list_item_actor.xml
│ │ ├── list_item_header.xml
│ │ ├── list_item_highlighting.xml
│ │ ├── list_item_large.xml
│ │ ├── list_item_large_buy.xml
│ │ ├── list_item_movie.xml
│ │ ├── list_item_product.xml
│ │ ├── list_item_selectable.xml
│ │ ├── list_item_small.xml
│ │ ├── list_item_suggestion.xml
│ │ ├── list_items.xml
│ │ ├── menu_item.xml
│ │ ├── showcase_directory.xml
│ │ ├── showcase_dynamic_facet_list.xml
│ │ ├── showcase_facet_list.xml
│ │ ├── showcase_facet_list_persistent.xml
│ │ ├── showcase_facet_list_search.xml
│ │ ├── showcase_filter_clear.xml
│ │ ├── showcase_filter_comparison.xml
│ │ ├── showcase_filter_current.xml
│ │ ├── showcase_filter_list.xml
│ │ ├── showcase_filter_map.xml
│ │ ├── showcase_filter_range.xml
│ │ ├── showcase_filter_rating.xml
│ │ ├── showcase_filter_toggle.xml
│ │ ├── showcase_filter_toggle_default.xml
│ │ ├── showcase_hierarchical.xml
│ │ ├── showcase_highlighting.xml
│ │ ├── showcase_loading.xml
│ │ ├── showcase_multisearch.xml
│ │ ├── showcase_paging.xml
│ │ ├── showcase_query_rule_custom_data.xml
│ │ ├── showcase_query_rule_custom_data_template.xml
│ │ ├── showcase_query_suggestion.xml
│ │ ├── showcase_relateditems.xml
│ │ ├── showcase_search.xml
│ │ ├── showcase_search_autocomplete.xml
│ │ ├── showcase_sort_by.xml
│ │ └── showcase_stats.xml
│ │ ├── mipmap-anydpi-v26
│ │ └── ic_launcher.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_adaptive_back.png
│ │ └── ic_launcher_adaptive_fore.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_adaptive_back.png
│ │ └── ic_launcher_adaptive_fore.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_adaptive_back.png
│ │ └── ic_launcher_adaptive_fore.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_adaptive_back.png
│ │ └── ic_launcher_adaptive_fore.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_adaptive_back.png
│ │ └── ic_launcher_adaptive_fore.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
├── androidtv
│ ├── README.md
│ ├── build.gradle
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── com
│ │ │ └── algolia
│ │ │ └── instantsearch
│ │ │ └── examples
│ │ │ └── androidtv
│ │ │ ├── MainActivity.kt
│ │ │ ├── SearchScreen.kt
│ │ │ ├── Show.kt
│ │ │ ├── Suggestion.kt
│ │ │ └── ui
│ │ │ ├── Color.kt
│ │ │ ├── SearchBox.kt
│ │ │ └── Theme.kt
│ │ └── res
│ │ ├── drawable
│ │ ├── ic_launcher.png
│ │ └── movie.png
│ │ ├── layout
│ │ ├── activity_details.xml
│ │ └── activity_main.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.webp
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.webp
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.webp
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.webp
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.webp
│ │ └── values
│ │ ├── strings.xml
│ │ └── themes.xml
└── wearos
│ ├── README.md
│ ├── build.gradle
│ └── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── algolia
│ │ └── instantsearch
│ │ └── example
│ │ └── wearos
│ │ ├── MainActivity.kt
│ │ ├── Show.kt
│ │ ├── ShowAdapter.kt
│ │ ├── ShowsActivity.kt
│ │ └── internal
│ │ ├── CustomScrollingLayoutCallback.kt
│ │ └── GrayscaleTransformation.kt
│ └── res
│ ├── drawable-anydpi
│ └── ic_mic.xml
│ ├── drawable-hdpi
│ └── ic_mic.png
│ ├── drawable-mdpi
│ └── ic_mic.png
│ ├── drawable-xhdpi
│ └── ic_mic.png
│ ├── drawable-xxhdpi
│ └── ic_mic.png
│ ├── drawable
│ └── logo.png
│ ├── layout
│ ├── activity_main.xml
│ ├── activity_shows.xml
│ └── list_item.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.webp
│ ├── mipmap-mdpi
│ └── ic_launcher.webp
│ ├── mipmap-xhdpi
│ └── ic_launcher.webp
│ ├── mipmap-xxhdpi
│ └── ic_launcher.webp
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.webp
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── extensions
├── android-loading
│ ├── README.md
│ ├── build.gradle.kts
│ ├── gradle.properties
│ └── src
│ │ ├── main
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── algolia
│ │ │ └── instantsearch
│ │ │ └── android
│ │ │ └── loading
│ │ │ └── LoadingViewSwipeRefreshLayout.kt
│ │ └── test
│ │ └── kotlin
│ │ ├── TestLoadingViewSwipeRefreshLayout.kt
│ │ └── documentation
│ │ └── DocLoading.kt
├── android-paging3
│ ├── README.md
│ ├── build.gradle.kts
│ ├── gradle.properties
│ └── src
│ │ └── main
│ │ └── kotlin
│ │ └── com
│ │ └── algolia
│ │ └── instantsearch
│ │ └── android
│ │ └── paging3
│ │ ├── Paginator.kt
│ │ ├── facet
│ │ ├── FacetListConnector.kt
│ │ └── internal
│ │ │ └── FacetListConnectionPager.kt
│ │ ├── filterstate
│ │ ├── FilterStateConnection.kt
│ │ └── internal
│ │ │ └── FilterStateConnectionPaginator.kt
│ │ ├── internal
│ │ ├── SearcherPaginator.kt
│ │ └── SearcherPagingSource.kt
│ │ ├── searchbox
│ │ ├── SearchBoxConnection.kt
│ │ └── internal
│ │ │ └── SearchBoxConnectionPaginator.kt
│ │ └── sortby
│ │ ├── SortByConnection.kt
│ │ └── internal
│ │ └── SortByConnectionPaginator.kt
└── coroutines-extensions
│ ├── README.md
│ ├── build.gradle.kts
│ ├── gradle.properties
│ └── src
│ ├── commonMain
│ └── kotlin
│ │ └── com
│ │ └── algolia
│ │ └── instantsearch
│ │ └── coroutines
│ │ └── Subscription.kt
│ └── commonTest
│ └── kotlin
│ └── TestSubscription.kt
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── instantsearch-compose
├── build.gradle.kts
├── gradle.properties
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── algolia
│ │ │ └── instantsearch
│ │ │ └── compose
│ │ │ ├── customdata
│ │ │ ├── QueryRuleCustomDataState.kt
│ │ │ └── internal
│ │ │ │ └── QueryRuleCustomDataStateImpl.kt
│ │ │ ├── filter
│ │ │ ├── clear
│ │ │ │ ├── FilterClear.kt
│ │ │ │ └── internal
│ │ │ │ │ └── FilterClearImpl.kt
│ │ │ ├── current
│ │ │ │ ├── FilterCurrentState.kt
│ │ │ │ └── internal
│ │ │ │ │ └── FilterCurrentStateImpl.kt
│ │ │ ├── facet
│ │ │ │ ├── FacetListState.kt
│ │ │ │ ├── dynamic
│ │ │ │ │ ├── DynamicFacetListState.kt
│ │ │ │ │ └── internal
│ │ │ │ │ │ └── DynamicFacetListStateImpl.kt
│ │ │ │ └── internal
│ │ │ │ │ └── FacetListStateImpl.kt
│ │ │ ├── list
│ │ │ │ ├── FilterListState.kt
│ │ │ │ └── internal
│ │ │ │ │ └── FilterListStateImpl.kt
│ │ │ ├── map
│ │ │ │ ├── FilterMapState.kt
│ │ │ │ └── internal
│ │ │ │ │ └── FilterMapStateImpl.kt
│ │ │ └── toggle
│ │ │ │ ├── FilterToggleState.kt
│ │ │ │ └── internal
│ │ │ │ └── FilterToggleStateImpl.kt
│ │ │ ├── hierarchical
│ │ │ ├── HierarchicalState.kt
│ │ │ └── internal
│ │ │ │ └── HierarchicalStateImpl.kt
│ │ │ ├── highlighting
│ │ │ └── HighlightedString.kt
│ │ │ ├── hits
│ │ │ ├── HitsState.kt
│ │ │ └── internal
│ │ │ │ └── HitsStateImpl.kt
│ │ │ ├── internal
│ │ │ └── Telemetry.kt
│ │ │ ├── item
│ │ │ ├── StatsState.kt
│ │ │ └── internal
│ │ │ │ └── StatsStateImpl.kt
│ │ │ ├── loading
│ │ │ ├── LoadingState.kt
│ │ │ └── internal
│ │ │ │ └── LoadingStateImpl.kt
│ │ │ ├── number
│ │ │ ├── NumberState.kt
│ │ │ ├── internal
│ │ │ │ └── NumberStateImpl.kt
│ │ │ ├── range
│ │ │ │ ├── NumberRangeState.kt
│ │ │ │ └── internal
│ │ │ │ │ └── NumberRangeStateImpl.kt
│ │ │ └── relevantsort
│ │ │ │ ├── RelevantSortState.kt
│ │ │ │ └── internal
│ │ │ │ └── RelevantSortStateImpl.kt
│ │ │ ├── searchbox
│ │ │ ├── SearchBox.kt
│ │ │ ├── SearchBoxState.kt
│ │ │ └── internal
│ │ │ │ ├── SearchBoxComponents.kt
│ │ │ │ └── SearchBoxStateImpl.kt
│ │ │ ├── selectable
│ │ │ ├── SelectableItemState.kt
│ │ │ ├── internal
│ │ │ │ └── SelectableItemStateImpl.kt
│ │ │ └── list
│ │ │ │ ├── SelectableListState.kt
│ │ │ │ └── internal
│ │ │ │ └── SelectableListStateImpl.kt
│ │ │ └── sortby
│ │ │ ├── SortByState.kt
│ │ │ └── internal
│ │ │ └── SortByStateImpl.kt
│ └── res
│ │ └── values
│ │ └── strings.xml
│ └── test
│ └── kotlin
│ └── com
│ └── algolia
│ └── instantsearch
│ └── compose
│ ├── customdata
│ └── QueryRuleCustomDataStateTest.kt
│ ├── filter
│ ├── DynamicFacetListStateTest.kt
│ ├── FacetListStateTest.kt
│ ├── FilterClearTest.kt
│ ├── FilterCurrentStateTest.kt
│ ├── FilterMapStateTest.kt
│ └── FilterToggleStateTest.kt
│ ├── hierarchical
│ └── HierarchicalStateTest.kt
│ ├── hits
│ └── HitsStateTest.kt
│ ├── item
│ └── StatsTest.kt
│ ├── loading
│ └── LoadingStateTest.kt
│ ├── number
│ ├── NumberRangeStateTest.kt
│ └── RelevantSortStateTest.kt
│ ├── searchbox
│ └── SearchBoxStateTest.kt
│ ├── selectable
│ ├── SelectableItemStateTest.kt
│ └── SelectableListStateTest.kt
│ ├── sortby
│ └── SortByStateTest.kt
│ └── telemetry
│ └── TestTelemetry.kt
├── instantsearch-core
├── build.gradle.kts
├── gradle.properties
└── src
│ ├── commonMain
│ └── kotlin
│ │ └── com
│ │ └── algolia
│ │ └── instantsearch
│ │ └── core
│ │ ├── Callback.kt
│ │ ├── InstantSearch.kt
│ │ ├── Presenter.kt
│ │ ├── connection
│ │ ├── AbstractConnection.kt
│ │ ├── Connection.kt
│ │ └── ConnectionHandler.kt
│ │ ├── highlighting
│ │ ├── HighlightTags.kt
│ │ ├── HighlightToken.kt
│ │ ├── HighlightTokenizer.kt
│ │ └── HighlightedString.kt
│ │ ├── hits
│ │ ├── HitsConnection.kt
│ │ ├── HitsView.kt
│ │ └── internal
│ │ │ └── HitsConnectionView.kt
│ │ ├── internal
│ │ ├── Collections.kt
│ │ ├── Exception.kt
│ │ └── Telemetry.kt
│ │ ├── loading
│ │ ├── LoadingConnection.kt
│ │ ├── LoadingView.kt
│ │ ├── LoadingViewModel.kt
│ │ └── internal
│ │ │ └── LoadingConnectionView.kt
│ │ ├── map
│ │ └── MapViewModel.kt
│ │ ├── number
│ │ ├── Computation.kt
│ │ ├── DefaultNumberPresenter.kt
│ │ ├── NumberConnection.kt
│ │ ├── NumberPresenter.kt
│ │ ├── NumberView.kt
│ │ ├── NumberViewModel.kt
│ │ ├── internal
│ │ │ └── NumberConnectionView.kt
│ │ └── range
│ │ │ ├── NumberRangeConnection.kt
│ │ │ ├── NumberRangeView.kt
│ │ │ ├── NumberRangeViewModel.kt
│ │ │ ├── Range.kt
│ │ │ └── internal
│ │ │ ├── Extensions.kt
│ │ │ └── NumberRangeConnectionView.kt
│ │ ├── relevantsort
│ │ ├── RelevantSortConnection.kt
│ │ ├── RelevantSortConnector.kt
│ │ ├── RelevantSortPresenter.kt
│ │ ├── RelevantSortPriority.kt
│ │ ├── RelevantSortView.kt
│ │ ├── RelevantSortViewModel.kt
│ │ └── internal
│ │ │ └── RelevantSortConnectionView.kt
│ │ ├── searchbox
│ │ ├── SearchBoxConnection.kt
│ │ ├── SearchBoxView.kt
│ │ ├── SearchBoxViewModel.kt
│ │ └── internal
│ │ │ └── SearchBoxConnectionView.kt
│ │ ├── searcher
│ │ ├── Constants.kt
│ │ ├── Debouncer.kt
│ │ ├── Searcher.kt
│ │ ├── SearcherConnection.kt
│ │ ├── Sequencer.kt
│ │ └── internal
│ │ │ └── SearcherConnectionView.kt
│ │ ├── selectable
│ │ ├── SelectableItemConnection.kt
│ │ ├── SelectableItemView.kt
│ │ ├── SelectableItemViewModel.kt
│ │ ├── internal
│ │ │ └── SelectableItemConnectionView.kt
│ │ ├── list
│ │ │ ├── SelectableItem.kt
│ │ │ ├── SelectableListConnection.kt
│ │ │ ├── SelectableListView.kt
│ │ │ ├── SelectableListViewModel.kt
│ │ │ ├── SelectionMode.kt
│ │ │ └── internal
│ │ │ │ └── SelectableListConnectionView.kt
│ │ └── map
│ │ │ ├── SelectableMapConnection.kt
│ │ │ ├── SelectableMapView.kt
│ │ │ ├── SelectableMapViewModel.kt
│ │ │ └── internal
│ │ │ └── SelectableMapConnectionView.kt
│ │ ├── subscription
│ │ ├── Subscription.kt
│ │ ├── SubscriptionEvent.kt
│ │ ├── SubscriptionMerge.kt
│ │ ├── SubscriptionValue.kt
│ │ └── internal
│ │ │ └── SubscriptionOnce.kt
│ │ └── tree
│ │ ├── Extensions.kt
│ │ ├── Node.kt
│ │ ├── Tree.kt
│ │ ├── TreeConnection.kt
│ │ ├── TreePresenter.kt
│ │ ├── TreeView.kt
│ │ ├── TreeViewModel.kt
│ │ └── internal
│ │ └── TreeConnectionView.kt
│ ├── commonTest
│ └── kotlin
│ │ ├── Test.kt
│ │ ├── connection
│ │ └── TestConnection.kt
│ │ ├── highlighting
│ │ └── TestHighlightTokenizer.kt
│ │ ├── loading
│ │ └── TestLoadingConnectView.kt
│ │ ├── map
│ │ └── TestMapViewModel.kt
│ │ ├── number
│ │ ├── TestComputation.kt
│ │ ├── TestNumberConnectView.kt
│ │ ├── TestNumberPresenter.kt
│ │ ├── TestNumberViewModel.kt
│ │ └── range
│ │ │ ├── TestNumberRangeConnectView.kt
│ │ │ └── TestNumberRangeViewModel.kt
│ │ ├── relevantsort
│ │ ├── TestRelevantSortConnectView.kt
│ │ └── TestRelevantSortViewModel.kt
│ │ ├── searchbox
│ │ ├── TestSearchBoxConnectView.kt
│ │ └── TestSearchBoxViewModel.kt
│ │ ├── searcher
│ │ ├── TestDebouncer.kt
│ │ └── TestSequencer.kt
│ │ ├── selectable
│ │ ├── TestFilterToggleConnectView.kt
│ │ ├── TestSelectableItemViewModel.kt
│ │ ├── list
│ │ │ ├── TestSelectableListConnectView.kt
│ │ │ └── TestSelectableListViewModel.kt
│ │ └── map
│ │ │ ├── TestSelectableMapConnectView.kt
│ │ │ └── TestSelectableMapViewModel.kt
│ │ ├── subscription
│ │ ├── TestSubscription.kt
│ │ ├── TestSubscriptionEvent.kt
│ │ └── TestSubscriptionValue.kt
│ │ └── tree
│ │ ├── TestHierarchicalNode.kt
│ │ ├── TestTreeConnectView.kt
│ │ └── TestTreePresenter.kt
│ └── jvmMain
│ └── kotlin
│ └── com
│ └── algolia
│ └── instantsearch
│ └── core
│ └── internal
│ └── Collections.kt
├── instantsearch-insights
├── README.md
├── build.gradle.kts
├── gradle.properties
└── src
│ ├── androidMain
│ ├── AndroidManifest.xml
│ └── kotlin
│ │ └── com
│ │ └── algolia
│ │ └── instantsearch
│ │ └── insights
│ │ ├── Insights.kt
│ │ └── internal
│ │ ├── InsightsController.kt
│ │ ├── data
│ │ ├── local
│ │ │ └── InsightsPrefsRepository.kt
│ │ └── settings
│ │ │ └── InsightsEventSettings.kt
│ │ ├── extension
│ │ ├── Platform.kt
│ │ ├── SharedPreferencesDelegate.kt
│ │ ├── Time.kt
│ │ └── UUID.kt
│ │ ├── logging
│ │ └── InsightsLogger.kt
│ │ └── worker
│ │ ├── InsightsWorkManager.kt
│ │ └── InsightsWorker.kt
│ ├── androidUnitTest
│ └── kotlin
│ │ └── com
│ │ └── algolia
│ │ └── instantsearch
│ │ └── insights
│ │ ├── AndroidTestDatabaseSharedPreferences.kt
│ │ ├── InsightsAndroidTest.kt
│ │ ├── InsightsTest.kt
│ │ ├── MockDistantRepository.kt
│ │ ├── MockLocalRepository.kt
│ │ ├── TestInsightsEventUploader.kt
│ │ └── util
│ │ └── WorkerManager.kt
│ ├── commonMain
│ └── kotlin
│ │ └── com
│ │ └── algolia
│ │ └── instantsearch
│ │ └── insights
│ │ ├── FilterTrackable.kt
│ │ ├── HitsAfterSearchTrackable.kt
│ │ ├── Insights.kt
│ │ ├── exception
│ │ └── InsightsException.kt
│ │ └── internal
│ │ ├── InsightsController.kt
│ │ ├── InsightsMap.kt
│ │ ├── cache
│ │ ├── InsightsCache.kt
│ │ └── InsightsEventCache.kt
│ │ ├── data
│ │ ├── distant
│ │ │ ├── InsightsDistantRepository.kt
│ │ │ └── InsightsHttpRepository.kt
│ │ ├── local
│ │ │ ├── InsightsLocalRepository.kt
│ │ │ ├── mapper
│ │ │ │ ├── FilterFacetMapper.kt
│ │ │ │ ├── InsightsEventDOMapper.kt
│ │ │ │ ├── InsightsEventsMapper.kt
│ │ │ │ └── Mapper.kt
│ │ │ └── model
│ │ │ │ ├── FilterFacetDO.kt
│ │ │ │ └── InsightsEventDO.kt
│ │ └── settings
│ │ │ └── InsightsSettings.kt
│ │ ├── event
│ │ └── EventResponse.kt
│ │ ├── extension
│ │ ├── Insights.kt
│ │ ├── InsightsEvent.kt
│ │ ├── Json.kt
│ │ ├── Map.kt
│ │ ├── Time.kt
│ │ └── UUID.kt
│ │ ├── logging
│ │ └── InsightsLogger.kt
│ │ ├── uploader
│ │ ├── InsightsEventUploader.kt
│ │ └── InsightsUploader.kt
│ │ └── worker
│ │ └── InsightsManager.kt
│ └── jvmMain
│ └── kotlin
│ └── com
│ └── algolia
│ └── instantsearch
│ └── insights
│ └── internal
│ ├── extension
│ ├── Time.kt
│ └── UUID.kt
│ └── logging
│ └── InsightsLogger.kt
├── instantsearch-utils
├── build.gradle.kts
├── gradle.properties
└── src
│ ├── androidMain
│ └── kotlin
│ │ └── com
│ │ └── algolia
│ │ └── instantsearch
│ │ └── platform
│ │ └── Env.kt
│ ├── commonJvm
│ └── kotlin
│ │ └── com
│ │ └── algolia
│ │ └── instantsearch
│ │ ├── encode
│ │ └── Gzip.kt
│ │ └── util
│ │ └── uuid.kt
│ ├── commonMain
│ ├── kotlin
│ │ └── com
│ │ │ └── algolia
│ │ │ └── instantsearch
│ │ │ ├── Annotations.kt
│ │ │ ├── encode
│ │ │ └── Gzip.kt
│ │ │ ├── platform
│ │ │ └── Env.kt
│ │ │ └── util
│ │ │ ├── agent.kt
│ │ │ └── uuid.kt
│ └── templates
│ │ └── BuildConfig.kt
│ └── jvmMain
│ └── kotlin
│ └── com
│ └── algolia
│ └── instantsearch
│ └── platform
│ └── Env.kt
├── instantsearch
├── build.gradle.kts
├── gradle.properties
└── src
│ ├── androidMain
│ └── kotlin
│ │ └── com
│ │ └── algolia
│ │ └── instantsearch
│ │ ├── android
│ │ ├── ViewGroup.kt
│ │ ├── filter
│ │ │ ├── clear
│ │ │ │ └── DefaultFilterClearView.kt
│ │ │ ├── current
│ │ │ │ └── DefaultFilterCurrentView.kt
│ │ │ ├── facet
│ │ │ │ ├── FacetListAdapter.kt
│ │ │ │ ├── FacetListViewHolder.kt
│ │ │ │ └── dynamic
│ │ │ │ │ ├── DynamicFacetListAdapter.kt
│ │ │ │ │ ├── DynamicFacetListViewHolder.kt
│ │ │ │ │ └── DynamicFacetModel.kt
│ │ │ ├── map
│ │ │ │ └── FilterMapViewRadioGroup.kt
│ │ │ └── toggle
│ │ │ │ └── FilterToggleViewCompoundButton.kt
│ │ ├── highlighting
│ │ │ └── Extensions.kt
│ │ ├── hits
│ │ │ ├── HitsArrayAdapter.kt
│ │ │ ├── HitsViewConnection.kt
│ │ │ └── internal
│ │ │ │ └── HitsArrayAdapterConnection.kt
│ │ ├── internal
│ │ │ └── Telemetry.kt
│ │ ├── item
│ │ │ ├── StatsTextView.kt
│ │ │ └── StatsTextViewSpanned.kt
│ │ ├── list
│ │ │ └── Extensions.kt
│ │ ├── searchbox
│ │ │ ├── DefaultSearchBoxView.kt
│ │ │ ├── SearchBoxAutoCompleteTextView.kt
│ │ │ ├── SearchBoxViewAppCompat.kt
│ │ │ └── SearchBoxViewEditText.kt
│ │ ├── sortby
│ │ │ ├── SortByViewAutocomplete.kt
│ │ │ └── SortByViewSpinner.kt
│ │ └── stats
│ │ │ ├── StatsTextView.kt
│ │ │ └── StatsTextViewSpanned.kt
│ │ ├── extension
│ │ └── Console.kt
│ │ └── searcher
│ │ └── internal
│ │ └── Actual.kt
│ ├── androidUnitTest
│ └── kotlin
│ │ ├── MainCoroutineRule.kt
│ │ ├── customdata
│ │ └── TestQueryRuleCustomData.kt
│ │ ├── documentation
│ │ ├── guide
│ │ │ └── GuideAnalyticsTag.kt
│ │ └── widget
│ │ │ ├── DocClearFilters.kt
│ │ │ ├── DocCurrentFilters.kt
│ │ │ ├── DocFacetList.kt
│ │ │ ├── DocFilterComparison.kt
│ │ │ ├── DocFilterList.kt
│ │ │ ├── DocFilterListFacet.kt
│ │ │ ├── DocFilterListNumeric.kt
│ │ │ ├── DocFilterListTag.kt
│ │ │ ├── DocFilterMap.kt
│ │ │ ├── DocFilterState.kt
│ │ │ ├── DocFilterToggle.kt
│ │ │ ├── DocHierarchicalMenu.kt
│ │ │ ├── DocHighlighting.kt
│ │ │ ├── DocHits.kt
│ │ │ ├── DocNumericRange.kt
│ │ │ ├── DocSearchBox.kt
│ │ │ ├── DocSortBy.kt
│ │ │ └── DocStats.kt
│ │ ├── instrumented
│ │ ├── InstrumentedTest.kt
│ │ ├── filter
│ │ │ ├── clear
│ │ │ │ └── TestFilterClearViewImpl.kt
│ │ │ ├── current
│ │ │ │ └── TestFilterCurrentViewImpl.kt
│ │ │ ├── map
│ │ │ │ └── TestSelectableMapViewRadioGroup.kt
│ │ │ └── toggle
│ │ │ │ └── TestFilterToggleViewCompoundButton.kt
│ │ ├── highlighting
│ │ │ └── TestExtensions.kt
│ │ ├── searchbox
│ │ │ ├── TestSearchBoxAutoCompleteTextView.kt
│ │ │ ├── TestSearchBoxEditText.kt
│ │ │ └── TestSearchBoxSearchView.kt
│ │ ├── sortby
│ │ │ └── TestSortByViewSpinner.kt
│ │ └── stats
│ │ │ ├── TestStatsTextView.kt
│ │ │ └── TestStatsTextViewSpanned.kt
│ │ ├── relatedItems
│ │ └── TestSearcherConnectionRelatedItems.kt
│ │ └── tracker
│ │ ├── TestFiltersTracker.kt
│ │ └── TestHitsTracker.kt
│ ├── commonMain
│ └── kotlin
│ │ └── com
│ │ └── algolia
│ │ └── instantsearch
│ │ ├── attribute
│ │ ├── AttributeMatchAndReplace.kt
│ │ ├── AttributePresenter.kt
│ │ └── DefaultAttributePresenter.kt
│ │ ├── customdata
│ │ ├── QueryRuleCustomDataConnection.kt
│ │ ├── QueryRuleCustomDataConnector.kt
│ │ ├── QueryRuleCustomDataPresenter.kt
│ │ ├── QueryRuleCustomDataViewModel.kt
│ │ └── internal
│ │ │ └── QueryRuleCustomDataConnectionSearcherForHits.kt
│ │ ├── extension
│ │ ├── Console.kt
│ │ ├── KSerializer.kt
│ │ ├── Telemetry.kt
│ │ └── Utils.kt
│ │ ├── filter
│ │ ├── DefaultFilterPresenter.kt
│ │ ├── FilterPresenter.kt
│ │ ├── clear
│ │ │ ├── ClearMode.kt
│ │ │ ├── FilterClearConnection.kt
│ │ │ ├── FilterClearConnector.kt
│ │ │ ├── FilterClearView.kt
│ │ │ ├── FilterClearViewModel.kt
│ │ │ └── internal
│ │ │ │ ├── FilterClearConnectionFilterState.kt
│ │ │ │ └── FilterClearConnectionView.kt
│ │ ├── current
│ │ │ ├── DefaultFilterCurrentPresenter.kt
│ │ │ ├── FilterAndID.kt
│ │ │ ├── FilterCurrentConnection.kt
│ │ │ ├── FilterCurrentConnector.kt
│ │ │ ├── FilterCurrentPresenter.kt
│ │ │ ├── FilterCurrentView.kt
│ │ │ ├── FilterCurrentViewModel.kt
│ │ │ └── internal
│ │ │ │ ├── FilterAndID.kt
│ │ │ │ ├── FilterCurrentConnectionFilterState.kt
│ │ │ │ └── FilterCurrentConnectionView.kt
│ │ ├── facet
│ │ │ ├── DefaultFacetListPresenter.kt
│ │ │ ├── FacetListConnection.kt
│ │ │ ├── FacetListConnector.kt
│ │ │ ├── FacetListItem.kt
│ │ │ ├── FacetListPresenter.kt
│ │ │ ├── FacetListView.kt
│ │ │ ├── FacetListViewModel.kt
│ │ │ ├── FacetSortCriterion.kt
│ │ │ ├── dynamic
│ │ │ │ ├── AttributedFacets.kt
│ │ │ │ ├── DynamicFacetConnection.kt
│ │ │ │ ├── DynamicFacetListConnector.kt
│ │ │ │ ├── DynamicFacetListView.kt
│ │ │ │ ├── DynamicFacetListViewModel.kt
│ │ │ │ ├── SelectionsPerAttribute.kt
│ │ │ │ └── internal
│ │ │ │ │ ├── DynamicFacetListConnectionFilterState.kt
│ │ │ │ │ ├── DynamicFacetListConnectionSearcherIndex.kt
│ │ │ │ │ ├── DynamicFacetListConnectionView.kt
│ │ │ │ │ └── FacetsOrder.kt
│ │ │ └── internal
│ │ │ │ ├── FacetListConnectionFilterState.kt
│ │ │ │ ├── FacetListConnectionSearcher.kt
│ │ │ │ ├── FacetListConnectionSearcherForFacets.kt
│ │ │ │ └── FacetListConnectionView.kt
│ │ ├── list
│ │ │ ├── FilterListConnection.kt
│ │ │ ├── FilterListConnector.kt
│ │ │ ├── FilterListView.kt
│ │ │ ├── FilterListViewModel.kt
│ │ │ └── internal
│ │ │ │ ├── Extensions.kt
│ │ │ │ └── FilterListConnectionFilterState.kt
│ │ ├── map
│ │ │ ├── FilterMapConnection.kt
│ │ │ ├── FilterMapConnector.kt
│ │ │ ├── FilterMapView.kt
│ │ │ ├── FilterMapViewModel.kt
│ │ │ └── internal
│ │ │ │ └── FilterMapConnectionFilterState.kt
│ │ ├── numeric
│ │ │ └── comparison
│ │ │ │ ├── FilterComparisonComputeBounds.kt
│ │ │ │ ├── FilterComparisonConnectFilterState.kt
│ │ │ │ ├── FilterComparisonConnector.kt
│ │ │ │ └── internal
│ │ │ │ └── FilterComparisonConnectionFilterState.kt
│ │ ├── range
│ │ │ ├── FilterRangeConnection.kt
│ │ │ ├── FilterRangeConnectionSearcher.kt
│ │ │ ├── FilterRangeConnector.kt
│ │ │ ├── FilterRangeViewModel.kt
│ │ │ └── internal
│ │ │ │ ├── FilterRangeConnectionFilterState.kt
│ │ │ │ ├── FilterRangeConnectionSearcherImpl.kt
│ │ │ │ └── Mapper.kt
│ │ ├── state
│ │ │ ├── DSL.kt
│ │ │ ├── Extension.kt
│ │ │ ├── FilterGroupDescriptor.kt
│ │ │ ├── FilterGroupID.kt
│ │ │ ├── FilterGroupsDSL.kt
│ │ │ ├── FilterOperator.kt
│ │ │ ├── FilterState.kt
│ │ │ ├── Filters.kt
│ │ │ ├── MutableFilters.kt
│ │ │ └── internal
│ │ │ │ ├── DefaultMutableFilters.kt
│ │ │ │ └── FiltersImpl.kt
│ │ └── toggle
│ │ │ ├── FilterToggleConnection.kt
│ │ │ ├── FilterToggleConnector.kt
│ │ │ ├── FilterToggleView.kt
│ │ │ ├── FilterToggleViewModel.kt
│ │ │ └── internal
│ │ │ └── FilterToggleConnectionFilterState.kt
│ │ ├── hierarchical
│ │ ├── DefaultHierarchicalPresenter.kt
│ │ ├── Extensions.kt
│ │ ├── HierarchicalConnection.kt
│ │ ├── HierarchicalConnector.kt
│ │ ├── HierarchicalFilter.kt
│ │ ├── HierarchicalItem.kt
│ │ ├── HierarchicalNode.kt
│ │ ├── HierarchicalPath.kt
│ │ ├── HierarchicalPresenter.kt
│ │ ├── HierarchicalTree.kt
│ │ ├── HierarchicalView.kt
│ │ ├── HierarchicalViewModel.kt
│ │ └── internal
│ │ │ ├── Extensions.kt
│ │ │ ├── HierarchicalConnectionFilterState.kt
│ │ │ └── HierarchicalConnectionSearcher.kt
│ │ ├── highlighting
│ │ └── Highlightable.kt
│ │ ├── index
│ │ ├── IndexNamePresenter.kt
│ │ └── IndexPresenter.kt
│ │ ├── loading
│ │ ├── LoadingConnection.kt
│ │ ├── LoadingConnector.kt
│ │ └── internal
│ │ │ └── LoadingConnectionSearcher.kt
│ │ ├── relateditems
│ │ ├── MatchingPattern.kt
│ │ ├── SearcherConnectionRelatedItems.kt
│ │ └── internal
│ │ │ ├── FilterFacetAndID.kt
│ │ │ ├── RelatedItemsConnectionView.kt
│ │ │ └── extensions
│ │ │ ├── FilterState.kt
│ │ │ ├── Indexable.kt
│ │ │ ├── MatchingPattern.kt
│ │ │ └── Unquote.kt
│ │ ├── relevantsort
│ │ ├── RelevantSortConnection.kt
│ │ ├── RelevantSortConnector.kt
│ │ └── internal
│ │ │ ├── RelevantSortConnectionSearcherForHits.kt
│ │ │ └── RelevantSortConnectorSearcherForHits.kt
│ │ ├── searchbox
│ │ ├── SearchBoxConnection.kt
│ │ ├── SearchBoxConnector.kt
│ │ ├── SearchMode.kt
│ │ └── internal
│ │ │ └── SearchBoxConnectionSearcher.kt
│ │ ├── searcher
│ │ ├── Extensions.kt
│ │ ├── FilterGroupsHolder.kt
│ │ ├── IndexNameHolder.kt
│ │ ├── SearcherAnswers.kt
│ │ ├── SearcherConnection.kt
│ │ ├── SearcherForFacets.kt
│ │ ├── SearcherForHits.kt
│ │ ├── SearcherPlaces.kt
│ │ ├── SearcherQuery.kt
│ │ ├── SearcherScope.kt
│ │ ├── facets
│ │ │ ├── FacetsSearcher.kt
│ │ │ ├── SearchForFacetQuery.kt
│ │ │ └── internal
│ │ │ │ ├── DefaultFacetsSearcher.kt
│ │ │ │ └── FacetsSearchService.kt
│ │ ├── hits
│ │ │ ├── HitsSearcher.kt
│ │ │ ├── SearchForQuery.kt
│ │ │ └── internal
│ │ │ │ ├── DefaultHitsSearchService.kt
│ │ │ │ └── DefaultHitsSearcher.kt
│ │ ├── internal
│ │ │ ├── Expected.kt
│ │ │ ├── FacetsSearcherConnectionFilterState.kt
│ │ │ ├── HitsSearcherConnectionFilterState.kt
│ │ │ ├── Loading.kt
│ │ │ ├── SearcherAnswersConnectionFilterState.kt
│ │ │ ├── SearcherExceptionHandler.kt
│ │ │ └── withUserAgent.kt
│ │ └── multi
│ │ │ ├── MultiSearcher.kt
│ │ │ └── internal
│ │ │ ├── DefaultMultiSearcher.kt
│ │ │ ├── MultiSearchComponent.kt
│ │ │ ├── MultiSearchOperation.kt
│ │ │ ├── MultiSearchService.kt
│ │ │ ├── SearchService.kt
│ │ │ └── extension
│ │ │ └── ResponseMultiSearch.kt
│ │ ├── sortby
│ │ ├── SortByConnection.kt
│ │ ├── SortByConnector.kt
│ │ ├── SortByView.kt
│ │ ├── SortByViewModel.kt
│ │ └── internal
│ │ │ ├── DefaultSortByConnector.kt
│ │ │ └── SortByConnectionSearcher.kt
│ │ ├── stats
│ │ ├── DefaultStatsPresenter.kt
│ │ ├── StatsConnection.kt
│ │ ├── StatsConnector.kt
│ │ ├── StatsPresenter.kt
│ │ ├── StatsView.kt
│ │ ├── StatsViewModel.kt
│ │ └── internal
│ │ │ ├── StatsConnectionSearcher.kt
│ │ │ └── StatsConnectionView.kt
│ │ └── tracker
│ │ ├── FilterTracker.kt
│ │ ├── HitsTracker.kt
│ │ └── internal
│ │ ├── FilterDataTracker.kt
│ │ ├── HitsDataTracker.kt
│ │ ├── InsightsScope.kt
│ │ ├── InsightsTracker.kt
│ │ ├── QueryIDContainer.kt
│ │ ├── SubscriptionJob.kt
│ │ └── TrackableSearcher.kt
│ ├── commonTest
│ └── kotlin
│ │ ├── Client.kt
│ │ ├── ClientInsights.kt
│ │ ├── Extensions.kt
│ │ ├── filter
│ │ ├── TestFilterPresenterImpl.kt
│ │ ├── clear
│ │ │ └── TestFilterClearConnectFilterState.kt
│ │ ├── current
│ │ │ └── TestCurrentFiltersConnectFilterState.kt
│ │ ├── facet
│ │ │ ├── TestFacetListConnectFilterState.kt
│ │ │ ├── TestFacetListConnectSearcher.kt
│ │ │ ├── TestFacetListConnectSearcherForFacets.kt
│ │ │ ├── TestFacetListConnectView.kt
│ │ │ ├── TestFacetListPresenter.kt
│ │ │ ├── TestFacetListViewModel.kt
│ │ │ └── TestFacetsOrder.kt
│ │ ├── list
│ │ │ └── TestFilterListConnectFilterState.kt
│ │ ├── map
│ │ │ └── TestFilterMapConnectFilterState.kt
│ │ ├── numeric
│ │ │ └── comparison
│ │ │ │ ├── TestFilterComparisonComputeBounds.kt
│ │ │ │ └── TestFilterComparisonConnectFilterState.kt
│ │ ├── range
│ │ │ ├── TestFilterRangeConnectFilterState.kt
│ │ │ └── TestFilterRangeConnectSearcher.kt
│ │ ├── state
│ │ │ └── TestFilterState.kt
│ │ └── toggle
│ │ │ └── TestFilterToggleConnectFilterState.kt
│ │ ├── hierarchical
│ │ ├── TestExtensions.kt
│ │ ├── TestHierarchicalConnectFilterState.kt
│ │ └── TestHierarchicalPresenter.kt
│ │ ├── highlighting
│ │ └── TestHighlightable.kt
│ │ ├── index
│ │ └── TestIndexPresenter.kt
│ │ ├── loading
│ │ └── TestLoadingConnectSearcher.kt
│ │ ├── relatedItems
│ │ ├── Product.kt
│ │ ├── TestMachingPattern.kt
│ │ └── mockHitsView.kt
│ │ ├── searchbox
│ │ └── TestSearchBoxConnectSearcher.kt
│ │ ├── searcher
│ │ ├── Extensions.kt
│ │ ├── MockSearcher.kt
│ │ ├── TestSearcherAnswers.kt
│ │ ├── TestSearcherForFacets.kt
│ │ ├── TestSearcherMultipleIndex.kt
│ │ └── TestSearcherSingleIndex.kt
│ │ ├── sortby
│ │ └── TestSortByConnectSearcher.kt
│ │ ├── stats
│ │ ├── TestStatsConnectSearcher.kt
│ │ └── TestStatsPresenter.kt
│ │ └── telemetry
│ │ └── TestTelemetry.kt
│ ├── jvmMain
│ └── kotlin
│ │ └── com
│ │ └── algolia
│ │ └── instantsearch
│ │ ├── extension
│ │ └── Console.kt
│ │ └── searcher
│ │ └── internal
│ │ └── Actual.kt
│ └── jvmTest
│ └── kotlin
│ └── kotlin
│ └── MainCoroutineRule.kt
├── renovate.json
└── settings.gradle.kts
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_size = 4
7 | indent_style = space
8 | insert_final_newline = true
9 | max_line_length = 120
10 | tab_width = 4
11 |
12 | # noinspection EditorConfigKeyCorrectness
13 | [*.{kt, kts}]
14 | kotlin_imports_layout = idea
15 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve our libraries
4 |
5 | ---
6 |
7 |
10 |
11 | **Describe the bug 🐛**
12 | A clear and concise description of what the bug is.
13 |
14 | **To Reproduce 🔍**
15 | Steps to reproduce the behavior:
16 | 1. Go to '...'
17 | 2. Click on '....'
18 | 3. Scroll down to '....'
19 | 4. See error
20 |
21 | **Expected behavior 💭**
22 | A clear and concise description of what you expected to happen.
23 |
24 | **Screenshots 🖥**
25 | If applicable, add screenshots to help explain your problem.
26 |
27 | **Environment:**
28 | - OS: [e.g. iOS / windows / mac / android]
29 | - Library Version [e.g. 2.0.0]
30 |
31 | **Additional context**
32 | Add any other context about the problem here.
33 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 |
5 | ---
6 |
7 |
10 |
11 | **Is your feature request related to a problem? Please describe 🙏**
12 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
13 |
14 | **Describe the solution you'd like 🤔**
15 | A clear and concise description of what you want to happen.
16 |
17 | **Describe alternatives you've considered ✨**
18 | A clear and concise description of any alternative solutions or features you've considered.
19 |
20 | **Additional context**
21 | Add any other context or screenshots about the feature request here.
22 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | | Q | A
2 | | ----------------- | ----------
3 | | Bug fix? | yes/no
4 | | New feature? | yes/no
5 | | BC breaks? | no
6 | | Related Issue | Fix #...
7 | | Need Doc update | yes/no
8 |
9 |
10 | ## Describe your change
11 |
12 |
16 |
17 | ## What problem is this fixing?
18 |
19 |
23 |
--------------------------------------------------------------------------------
/.github/semantic.yml:
--------------------------------------------------------------------------------
1 | # Always validate the PR title, and ignore the commits
2 | titleOnly: true
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | .idea
3 | .gradle
4 | .DS_STORE
5 | *.iml
6 | local.properties
7 | *.hprof
8 | .bundle/
9 | local.settings.gradle.kts
10 |
11 | # fastlane
12 | **/fastlane/report.xml
13 | **/fastlane/Preview.html
14 | **/fastlane/screenshots
15 | **/fastlane/test_output
16 |
17 | # bitrise
18 | .bitrise.yml
19 | bitrise.yml
20 |
--------------------------------------------------------------------------------
/.tool-versions:
--------------------------------------------------------------------------------
1 | java temurin-11.0.22+7
2 |
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @Aallam
2 |
--------------------------------------------------------------------------------
/docs/img/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/docs/img/banner.png
--------------------------------------------------------------------------------
/docs/img/codex/categories_hits.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/docs/img/codex/categories_hits.gif
--------------------------------------------------------------------------------
/docs/img/codex/multiple_index.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/docs/img/codex/multiple_index.gif
--------------------------------------------------------------------------------
/docs/img/codex/query_suggestions.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/docs/img/codex/query_suggestions.gif
--------------------------------------------------------------------------------
/docs/img/codex/query_suggestions_categories.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/docs/img/codex/query_suggestions_categories.gif
--------------------------------------------------------------------------------
/docs/img/codex/query_suggestions_hits.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/docs/img/codex/query_suggestions_hits.gif
--------------------------------------------------------------------------------
/docs/img/codex/query_suggestions_recent.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/docs/img/codex/query_suggestions_recent.gif
--------------------------------------------------------------------------------
/docs/img/codex/voice_search.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/docs/img/codex/voice_search.gif
--------------------------------------------------------------------------------
/docs/img/examples/android.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/docs/img/examples/android.png
--------------------------------------------------------------------------------
/docs/img/examples/androidtv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/docs/img/examples/androidtv.png
--------------------------------------------------------------------------------
/docs/img/examples/wearos.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/docs/img/examples/wearos.png
--------------------------------------------------------------------------------
/docs/img/guide/declarative_ui.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/docs/img/guide/declarative_ui.gif
--------------------------------------------------------------------------------
/docs/img/guide/imperative_ui.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/docs/img/guide/imperative_ui.gif
--------------------------------------------------------------------------------
/docs/img/guide/suggestions.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/docs/img/guide/suggestions.gif
--------------------------------------------------------------------------------
/docs/img/guide/voice_search.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/docs/img/guide/voice_search.gif
--------------------------------------------------------------------------------
/docs/img/showcase/showcase-compose.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/docs/img/showcase/showcase-compose.gif
--------------------------------------------------------------------------------
/docs/img/showcase/showcase-view.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/docs/img/showcase/showcase-view.gif
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # InstantSearch examples
2 |
3 | * [InstantSearch examples for Android](android)
4 | * [InstantSearch examples for WearOS](wearos)
5 | * [InstantSearch examples for Android TV](androidtv)
6 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/codex/categorieshits/Product.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.codex.categorieshits
2 |
3 | import com.algolia.instantsearch.highlighting.Highlightable
4 | import com.algolia.search.model.Attribute
5 | import com.algolia.search.model.ObjectID
6 | import com.algolia.search.model.indexing.Indexable
7 | import kotlinx.serialization.Serializable
8 | import kotlinx.serialization.json.JsonObject
9 |
10 | @Serializable
11 | data class Product(
12 | val name: String,
13 | val description: String,
14 | val image: String,
15 | override val objectID: ObjectID,
16 | override val _highlightResult: JsonObject?
17 | ) : Indexable, Highlightable {
18 |
19 | val highlightedName
20 | get() = getHighlight(Attribute("name"))
21 |
22 | val highlightedDescription
23 | get() = getHighlight(Attribute("description"))
24 | }
25 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/codex/categorieshits/README.md:
--------------------------------------------------------------------------------
1 | # Categories & Hits implementation example
2 |
3 | Search experience consisting of two results sections:
4 | - Products categories lists
5 | - Products list
6 |
7 | Demonstrates simultaneous search for hits and facet values.
8 |
9 |
10 |
11 | ## How to run this example
12 |
13 | ### 1. Clone this repository
14 |
15 | ```sh
16 | git clone git@github.com:algolia/instantsearch-android.git
17 | ```
18 |
19 | ### 2. Build the project
20 |
21 | ```sh
22 | ./gradlew :examples:android:assembleDebug
23 | ```
24 |
25 | ### 3. Install the app
26 |
27 | ```sh
28 | ./gradlew :examples:android:installDebug
29 | ```
30 |
31 | ### 4. Launch the example
32 |
33 | The example is under `Code Exchange > Categpries Hits`.
34 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/codex/multipleindex/Actor.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.codex.multipleindex
2 |
3 | import com.algolia.instantsearch.highlighting.Highlightable
4 | import com.algolia.search.model.Attribute
5 | import com.algolia.search.model.ObjectID
6 | import com.algolia.search.model.indexing.Indexable
7 | import kotlinx.serialization.Serializable
8 | import kotlinx.serialization.json.JsonObject
9 |
10 | @Serializable
11 | data class Actor(
12 | val name: String,
13 | override val objectID: ObjectID,
14 | override val _highlightResult: JsonObject?
15 | ) : Indexable, Highlightable {
16 |
17 | val highlightedName
18 | get() = getHighlight(Attribute("name"))
19 | }
20 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/codex/multipleindex/Movie.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.codex.multipleindex
2 |
3 | import com.algolia.instantsearch.highlighting.Highlightable
4 | import com.algolia.search.model.Attribute
5 | import com.algolia.search.model.ObjectID
6 | import com.algolia.search.model.indexing.Indexable
7 | import kotlinx.serialization.Serializable
8 | import kotlinx.serialization.json.JsonObject
9 |
10 | @Serializable
11 | data class Movie(
12 | val title: String,
13 | override val objectID: ObjectID,
14 | override val _highlightResult: JsonObject?
15 | ) : Indexable, Highlightable {
16 |
17 | val highlightedTitle
18 | get() = getHighlight(Attribute("title"))
19 | }
20 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/codex/multipleindex/README.md:
--------------------------------------------------------------------------------
1 | # Multi-index search implementation example
2 |
3 | Search experience consisting of search in two indices:
4 | - Actors
5 | - Films
6 |
7 | Demonstrates simultaneous search in multiple indices
8 |
9 |
10 |
11 | ## How to run this example
12 |
13 | ### 1. Clone this repository
14 |
15 | ```sh
16 | git clone git@github.com:algolia/instantsearch-android.git
17 | ```
18 |
19 | ### 2. Build the project
20 |
21 | ```sh
22 | ./gradlew :examples:android:assembleDebug
23 | ```
24 |
25 | ### 3. Install the app
26 |
27 | ```sh
28 | ./gradlew :examples:android:installDebug
29 | ```
30 |
31 | ### 4. Launch the example
32 |
33 | The example is under `Code Exchange > Multiple Index`.
34 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/codex/multisearch/Actor.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.codex.multisearch
2 |
3 | import com.algolia.instantsearch.highlighting.Highlightable
4 | import com.algolia.search.model.Attribute
5 | import com.algolia.search.model.ObjectID
6 | import com.algolia.search.model.indexing.Indexable
7 | import kotlinx.serialization.Serializable
8 | import kotlinx.serialization.json.JsonObject
9 |
10 | @Serializable
11 | data class Actor(
12 | val name: String,
13 | override val objectID: ObjectID,
14 | override val _highlightResult: JsonObject?
15 | ) : Indexable, Highlightable {
16 |
17 | val highlightedName
18 | get() = getHighlight(Attribute("name"))
19 | }
20 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/codex/multisearch/Movie.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.codex.multisearch
2 |
3 | import com.algolia.instantsearch.highlighting.Highlightable
4 | import com.algolia.search.model.Attribute
5 | import com.algolia.search.model.ObjectID
6 | import com.algolia.search.model.indexing.Indexable
7 | import kotlinx.serialization.Serializable
8 | import kotlinx.serialization.json.JsonObject
9 |
10 | @Serializable
11 | data class Movie(
12 | val title: String,
13 | override val objectID: ObjectID,
14 | override val _highlightResult: JsonObject?
15 | ) : Indexable, Highlightable {
16 |
17 | val highlightedTitle
18 | get() = getHighlight(Attribute("title"))
19 | }
20 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/codex/multisearch/README.md:
--------------------------------------------------------------------------------
1 | # Multi-index search implementation example
2 |
3 | Search experience consisting of search in two indices:
4 | - Actors
5 | - Films
6 |
7 | Demonstrates simultaneous search in multiple indices
8 |
9 | ## How to run this example
10 |
11 | ### 1. Clone this repository
12 |
13 | ```sh
14 | git clone git@github.com:algolia/instantsearch-android.git
15 | ```
16 |
17 | ### 2. Build the project
18 |
19 | ```sh
20 | ./gradlew :examples:android:assembleDebug
21 | ```
22 |
23 | ### 3. Install the app
24 |
25 | ```sh
26 | ./gradlew :examples:android:installDebug
27 | ```
28 |
29 | ### 4. Launch the example
30 |
31 | The example is under `Code Exchange > Multiple Index`.
32 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/codex/suggestions/categories/Suggestion.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.codex.suggestions.categories
2 |
3 | import com.algolia.instantsearch.core.highlighting.HighlightedString
4 | import com.algolia.instantsearch.highlighting.Highlightable
5 | import com.algolia.search.model.Attribute
6 | import kotlinx.serialization.Serializable
7 | import kotlinx.serialization.json.JsonObject
8 |
9 | @Serializable
10 | data class Suggestion(
11 | val query: String,
12 | override val _highlightResult: JsonObject? = null
13 | ) : Highlightable {
14 |
15 | val highlightedQuery: HighlightedString?
16 | get() = getHighlight(Attribute("query"))
17 | }
18 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/codex/suggestions/hits/Product.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.codex.suggestions.hits
2 |
3 | import com.algolia.search.model.ObjectID
4 | import com.algolia.search.model.indexing.Indexable
5 | import kotlinx.serialization.Serializable
6 |
7 | @Serializable
8 | data class Product(
9 | val name: String,
10 | val description: String,
11 | val image: String,
12 | override val objectID: ObjectID
13 | ) : Indexable
14 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/codex/suggestions/hits/Suggestion.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.codex.suggestions.hits
2 |
3 | import com.algolia.instantsearch.core.highlighting.HighlightedString
4 | import com.algolia.instantsearch.highlighting.Highlightable
5 | import com.algolia.search.model.Attribute
6 | import kotlinx.serialization.Serializable
7 | import kotlinx.serialization.json.JsonObject
8 |
9 | @Serializable
10 | data class Suggestion(
11 | val query: String,
12 | override val _highlightResult: JsonObject? = null
13 | ) : Highlightable {
14 |
15 | val highlightedQuery: HighlightedString?
16 | get() = getHighlight(Attribute("query"))
17 | }
18 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/codex/suggestions/query/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.codex.suggestions.query
2 |
3 | import android.os.Bundle
4 | import androidx.activity.ComponentActivity
5 | import androidx.activity.compose.setContent
6 | import androidx.activity.viewModels
7 | import androidx.compose.material.MaterialTheme
8 |
9 | class MainActivity : ComponentActivity() {
10 |
11 | private val viewModel: MainViewModel by viewModels()
12 |
13 | override fun onCreate(savedInstanceState: Bundle?) {
14 | super.onCreate(savedInstanceState)
15 | setContent {
16 | MaterialTheme {
17 | SearchScreen(
18 | searchBoxState = viewModel.searchBoxState,
19 | suggestionsState = viewModel.suggestionsState,
20 | )
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/codex/suggestions/query/Suggestion.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.codex.suggestions.query
2 |
3 | import com.algolia.instantsearch.core.highlighting.HighlightedString
4 | import com.algolia.instantsearch.highlighting.Highlightable
5 | import com.algolia.search.model.Attribute
6 | import kotlinx.serialization.Serializable
7 | import kotlinx.serialization.json.JsonObject
8 |
9 | @Serializable
10 | data class Suggestion(
11 | val query: String,
12 | override val _highlightResult: JsonObject? = null
13 | ) : Highlightable {
14 |
15 | val highlightedQuery: HighlightedString?
16 | get() = getHighlight(Attribute("query"))
17 | }
18 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/codex/suggestions/recent/Suggestion.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.codex.suggestions.recent
2 |
3 | import com.algolia.instantsearch.core.highlighting.HighlightedString
4 | import com.algolia.instantsearch.highlighting.Highlightable
5 | import com.algolia.search.model.Attribute
6 | import kotlinx.serialization.Serializable
7 | import kotlinx.serialization.json.JsonObject
8 |
9 | @Serializable
10 | data class Suggestion(
11 | val query: String,
12 | override val _highlightResult: JsonObject? = null
13 | ) : Highlightable {
14 |
15 | val highlightedQuery: HighlightedString?
16 | get() = getHighlight(Attribute("query"))
17 | }
18 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/codex/voice/Product.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.codex.voice
2 |
3 | import com.algolia.instantsearch.highlighting.Highlightable
4 | import com.algolia.search.model.Attribute
5 | import com.algolia.search.model.ObjectID
6 | import com.algolia.search.model.indexing.Indexable
7 | import kotlinx.serialization.Serializable
8 | import kotlinx.serialization.json.JsonObject
9 |
10 | @Serializable
11 | data class Product(
12 | val name: String,
13 | val description: String,
14 | val image: String,
15 | override val objectID: ObjectID,
16 | override val _highlightResult: JsonObject?
17 | ) : Indexable, Highlightable {
18 |
19 | val highlightedName
20 | get() = getHighlight(Attribute("name"))
21 |
22 | val highlightedDescription
23 | get() = getHighlight(Attribute("description"))
24 | }
25 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/compose/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.guides.compose.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val Purple200 = Color(0xFFBB86FC)
6 | val Purple500 = Color(0xFF6200EE)
7 | val Purple700 = Color(0xFF3700B3)
8 | val Teal200 = Color(0xFF03DAC5)
9 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/compose/theme/Shape.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.guides.compose.theme
2 |
3 | import androidx.compose.foundation.shape.RoundedCornerShape
4 | import androidx.compose.material.Shapes
5 | import androidx.compose.ui.unit.dp
6 |
7 | val Shapes = Shapes(
8 | small = RoundedCornerShape(4.dp),
9 | medium = RoundedCornerShape(4.dp),
10 | large = RoundedCornerShape(0.dp)
11 | )
12 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/compose/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.guides.compose.theme
2 |
3 | import androidx.compose.material.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.FontFamily
6 | import androidx.compose.ui.text.font.FontWeight
7 | import androidx.compose.ui.unit.sp
8 |
9 | // Set of Material typography styles to start with
10 | val Typography = Typography(
11 | body1 = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp
15 | )
16 | )
17 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/model/Product.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.guides.model
2 |
3 | import com.algolia.instantsearch.core.highlighting.HighlightedString
4 | import com.algolia.instantsearch.highlighting.Highlightable
5 | import com.algolia.search.model.Attribute
6 | import com.algolia.search.model.ObjectID
7 | import com.algolia.search.model.indexing.Indexable
8 | import kotlinx.serialization.Serializable
9 | import kotlinx.serialization.json.JsonObject
10 |
11 | @Serializable
12 | data class Product(
13 | val name: String,
14 | val image: String,
15 | val price: Double,
16 | val description: String,
17 | override val objectID: ObjectID,
18 | override val _highlightResult: JsonObject?
19 | ) : Indexable, Highlightable {
20 |
21 | val highlightedName: HighlightedString?
22 | get() = getHighlight(Attribute("name"))
23 | }
24 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/guides/model/Suggestion.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.guides.model
2 |
3 | import com.algolia.instantsearch.core.highlighting.HighlightedString
4 | import com.algolia.instantsearch.highlighting.Highlightable
5 | import com.algolia.search.model.Attribute
6 | import com.algolia.search.model.ObjectID
7 | import com.algolia.search.model.indexing.Indexable
8 | import kotlinx.serialization.Serializable
9 | import kotlinx.serialization.json.JsonObject
10 |
11 | @Serializable
12 | data class Suggestion(
13 | val query: String,
14 | override val objectID: ObjectID,
15 | override val _highlightResult: JsonObject?
16 | ) : Indexable, Highlightable {
17 |
18 | val highlightedQuery: HighlightedString?
19 | get() = getHighlight(Attribute("query"))
20 | }
21 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/androidview/README.md:
--------------------------------------------------------------------------------
1 | # InstantSearch Android Showcase
2 |
3 | A showcase of widgets available in `com.algolia:instantsearch-android`.
4 |
5 |
6 |
7 | ## How to run this example
8 |
9 | ### 1. Clone this repository
10 |
11 | ```sh
12 | git clone git@github.com:algolia/instantsearch-android.git
13 | ```
14 |
15 | ### 2. Build the project
16 |
17 | ```sh
18 | ./gradlew :examples:android:assembleDebug
19 | ```
20 |
21 | ### 3. Install the app
22 |
23 | ```sh
24 | ./gradlew :examples:android:installDebug
25 | ```
26 |
27 | ### 4. Launch the example
28 |
29 | The widgets showcase is under `Showcase > Imperative UI`.
30 |
31 | ## Additional resources
32 | Learn more about InstantSearch Android [widgets](https://www.algolia.com/doc/guides/building-search-ui/widgets/showcase/android/) in the Algolia documentation.
33 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/androidview/customdata/Banner.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.showcase.androidview.customdata
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class Banner(
7 | val title: String?,
8 | val banner: String?,
9 | val link: String,
10 | val redirect: String?
11 | )
12 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/androidview/filter/list/FilterListViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.showcase.androidview.filter.list
2 |
3 | import android.view.View
4 | import androidx.recyclerview.widget.RecyclerView
5 | import com.algolia.instantsearch.examples.android.databinding.ListItemSelectableBinding
6 |
7 | class FilterListViewHolder(val binding: ListItemSelectableBinding) :
8 | RecyclerView.ViewHolder(binding.root) {
9 |
10 | fun bind(text: String, selected: Boolean, onClickListener: View.OnClickListener) {
11 | binding.root.setOnClickListener(onClickListener)
12 | binding.selectableItemName.text = text
13 | binding.selectableItemIcon.visibility = if (selected) View.VISIBLE else View.INVISIBLE
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/androidview/filter/range/BoundsTextView.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.showcase.androidview.filter.range
2 |
3 | import android.widget.TextView
4 | import com.algolia.instantsearch.core.Callback
5 | import com.algolia.instantsearch.core.number.range.NumberRangeView
6 | import com.algolia.instantsearch.core.number.range.Range
7 |
8 |
9 | class BoundsTextView(val view: TextView) : NumberRangeView {
10 |
11 | override var onRangeChanged: Callback>? = null
12 |
13 | private var bounds: Range? = null
14 |
15 | override fun setBounds(bounds: Range?) {
16 | this.bounds = bounds
17 | view.text = bounds?.let {
18 | "Bounds: ${it.min} to ${it.max}"
19 | } ?: "No bounds"
20 | }
21 |
22 | override fun setRange(range: Range?) = Unit
23 | }
24 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/androidview/filter/rating/RatingTextView.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.showcase.androidview.filter.rating
2 |
3 | import android.widget.TextView
4 | import com.algolia.instantsearch.core.Callback
5 | import com.algolia.instantsearch.core.number.range.NumberRangeView
6 | import com.algolia.instantsearch.core.number.range.Range
7 |
8 | class RatingTextView(private val textView: TextView) : NumberRangeView {
9 |
10 | override var onRangeChanged: Callback>? = null
11 |
12 | override fun setBounds(bounds: Range?) = Unit
13 |
14 | override fun setRange(range: Range?) {
15 | textView.text = "${range?.min ?: 0}"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/androidview/list/actor/Actor.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.showcase.androidview.list.actor
2 |
3 | import com.algolia.search.model.ObjectID
4 | import com.algolia.search.model.indexing.Indexable
5 | import kotlinx.serialization.Serializable
6 |
7 | @Serializable
8 | data class Actor(
9 | val name: String,
10 | override val objectID: ObjectID
11 | ) : Indexable
12 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/androidview/list/actor/ActorViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.showcase.androidview.list.actor
2 |
3 | import androidx.recyclerview.widget.RecyclerView
4 | import com.algolia.instantsearch.examples.android.databinding.ListItemActorBinding
5 |
6 | class ActorViewHolder(private val binding: ListItemActorBinding) :
7 | RecyclerView.ViewHolder(binding.root) {
8 |
9 | fun bind(actor: Actor) {
10 | binding.actorName.text = actor.name
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/androidview/list/actor/ActorViewHolderSmall.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.showcase.androidview.list.actor
2 |
3 | import androidx.recyclerview.widget.RecyclerView
4 | import com.algolia.instantsearch.examples.android.databinding.ListItemSmallBinding
5 |
6 | class ActorViewHolderSmall(val binding: ListItemSmallBinding) :
7 | RecyclerView.ViewHolder(binding.root) {
8 |
9 | fun bind(actor: Actor) {
10 | binding.itemName.text = actor.name
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/androidview/list/header/HeaderViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.showcase.androidview.list.header
2 |
3 | import android.widget.TextView
4 | import androidx.recyclerview.widget.RecyclerView
5 |
6 | class HeaderViewHolder(val view: TextView) : RecyclerView.ViewHolder(view) {
7 |
8 | fun bind(string: String) {
9 | view.text = string
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/androidview/list/movie/MovieDiffUtil.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.showcase.androidview.list.movie
2 |
3 | import androidx.recyclerview.widget.DiffUtil
4 |
5 | object MovieDiffUtil : DiffUtil.ItemCallback() {
6 |
7 | override fun areItemsTheSame(
8 | oldItem: Movie,
9 | newItem: Movie
10 | ): Boolean {
11 | return oldItem.objectID == newItem.objectID
12 | }
13 |
14 | override fun areContentsTheSame(
15 | oldItem: Movie,
16 | newItem: Movie
17 | ): Boolean {
18 | return oldItem == newItem
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/androidview/list/movie/MovieViewHolderNested.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.showcase.androidview.list.movie
2 |
3 | import androidx.recyclerview.widget.RecyclerView
4 | import coil.load
5 | import com.algolia.instantsearch.examples.android.databinding.ListItemMovieBinding
6 |
7 | class MovieViewHolderNested(private val binding: ListItemMovieBinding) :
8 | RecyclerView.ViewHolder(binding.root) {
9 |
10 | fun bind(movie: Movie) {
11 | binding.itemTitle.text = movie.title
12 | binding.itemSubtitle.text = movie.genre.sorted().joinToString { it }
13 | binding.itemCaption.text = movie.year
14 | binding.itemImage.load(movie.image) {
15 | placeholder(android.R.drawable.ic_media_play)
16 | error(android.R.drawable.ic_media_play)
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/androidview/list/paging/PagingMultipleIndexItem.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.showcase.androidview.list.paging
2 |
3 | import com.algolia.instantsearch.examples.android.showcase.androidview.list.actor.ActorAdapterNested
4 | import com.algolia.instantsearch.examples.android.showcase.androidview.list.movie.MovieAdapterNested
5 |
6 |
7 | sealed class PagingMultipleIndexItem {
8 |
9 | data class Header(val name: String): PagingMultipleIndexItem()
10 |
11 | data class Movies(val adapter: MovieAdapterNested) : PagingMultipleIndexItem()
12 |
13 | data class Actors(val adapter: ActorAdapterNested) : PagingMultipleIndexItem()
14 | }
15 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/androidview/list/product/Product.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.showcase.androidview.list.product
2 |
3 | import com.algolia.search.model.ObjectID
4 | import com.algolia.search.model.indexing.Indexable
5 | import kotlinx.serialization.SerialName
6 | import kotlinx.serialization.Serializable
7 |
8 | @Serializable
9 | data class Product(
10 | val name: String,
11 | val description: String,
12 | val brand: String? = null,
13 | val categories: List,
14 | val type: String,
15 | val price: Double,
16 | @SerialName("price_range") val priceRange: String,
17 | val image: String,
18 | val url: String,
19 | @SerialName("free_shipping") val freeShipping: Boolean,
20 | val rating: Int = 0,
21 | val popularity: Long,
22 | override val objectID: ObjectID
23 | ) : Indexable
24 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/androidview/list/suggestion/Suggestion.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.showcase.androidview.list.suggestion
2 |
3 | import com.algolia.instantsearch.core.highlighting.HighlightedString
4 | import com.algolia.instantsearch.highlighting.Highlightable
5 | import com.algolia.search.model.Attribute
6 | import com.algolia.search.model.ObjectID
7 | import com.algolia.search.model.indexing.Indexable
8 | import kotlinx.serialization.Serializable
9 | import kotlinx.serialization.json.JsonObject
10 |
11 | @Serializable
12 | data class Suggestion(
13 | val query: String,
14 | override val objectID: ObjectID,
15 | override val _highlightResult: JsonObject?
16 | ) : Indexable, Highlightable {
17 |
18 | val highlightedQuery: HighlightedString?
19 | get() = getHighlight(Attribute("query"))
20 | }
21 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/compose/model/Actor.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.showcase.compose.model
2 |
3 | import com.algolia.search.model.ObjectID
4 | import com.algolia.search.model.indexing.Indexable
5 | import kotlinx.serialization.Serializable
6 |
7 | @Serializable
8 | data class Actor(
9 | val name: String,
10 | override val objectID: ObjectID
11 | ) : Indexable
12 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/compose/model/Banner.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.showcase.compose.model
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class Banner(
7 | val title: String?,
8 | val banner: String?,
9 | val link: String
10 | )
11 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/compose/model/Product.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.showcase.compose.model
2 |
3 | import com.algolia.search.model.ObjectID
4 | import com.algolia.search.model.indexing.Indexable
5 | import kotlinx.serialization.SerialName
6 | import kotlinx.serialization.Serializable
7 |
8 | @Serializable
9 | data class Product(
10 | val name: String,
11 | val description: String,
12 | val brand: String? = null,
13 | val categories: List,
14 | val type: String,
15 | val price: Double,
16 | @SerialName("price_range") val priceRange: String,
17 | val image: String,
18 | val url: String,
19 | @SerialName("free_shipping") val freeShipping: Boolean,
20 | val rating: Int = 0,
21 | val popularity: Long,
22 | override val objectID: ObjectID
23 | ) : Indexable
24 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/compose/ui/Shape.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.showcase.compose.ui
2 |
3 | import androidx.compose.foundation.shape.RoundedCornerShape
4 | import androidx.compose.material.Shapes
5 | import androidx.compose.ui.unit.dp
6 |
7 | val Shapes = Shapes(
8 | small = RoundedCornerShape(4.dp),
9 | medium = RoundedCornerShape(4.dp),
10 | large = RoundedCornerShape(0.dp)
11 | )
12 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/compose/ui/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.showcase.compose.ui
2 |
3 | import androidx.compose.material.MaterialTheme
4 | import androidx.compose.material.lightColors
5 | import androidx.compose.runtime.Composable
6 |
7 | private val LightColorPalette = lightColors(
8 | primary = BlueDark,
9 | onPrimary = White,
10 | secondary = White,
11 | onSecondary = GreyDark,
12 | onSurface = BlackLight,
13 | background = WhiteLight,
14 | onBackground = BlackLight,
15 |
16 | )
17 |
18 | @Composable
19 | fun ShowcaseTheme(content: @Composable() () -> Unit) {
20 | MaterialTheme(
21 | colors = LightColorPalette,
22 | typography = Typography,
23 | shapes = Shapes,
24 | content = content
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/examples/android/src/main/kotlin/com/algolia/instantsearch/examples/android/showcase/compose/ui/component/RestoreFab.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.android.showcase.compose.ui.component
2 |
3 | import androidx.compose.material.FloatingActionButton
4 | import androidx.compose.material.Icon
5 | import androidx.compose.material.MaterialTheme
6 | import androidx.compose.material.icons.Icons
7 | import androidx.compose.material.icons.filled.Restore
8 | import androidx.compose.runtime.Composable
9 |
10 | @Composable
11 | fun RestoreFab(onClick: () -> Unit = {}) {
12 | FloatingActionButton(
13 | backgroundColor = MaterialTheme.colors.background,
14 | onClick = onClick
15 | ) {
16 | Icon(
17 | imageVector = Icons.Default.Restore,
18 | contentDescription = "restore"
19 | )
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/drawable-anydpi/ic_filter_list.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/drawable/ic_add.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/drawable/ic_add_shopping_cart.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/drawable/ic_arrow_down.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/drawable/ic_arrow_up.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/drawable/ic_arrow_up_right.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/drawable/ic_check.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/drawable/ic_close_hint.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/drawable/ic_delete.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/drawable/ic_info.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/drawable/ic_microphone_outline.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/drawable/ic_remove.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/drawable/ic_restore.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/drawable/ic_search_hint.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/drawable/query_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/drawable/rectangle.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/drawable/thumb.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/layout/activity_getting_started.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/layout/autocompletetextfield.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/layout/filter_chip.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/layout/fragment_items.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/layout/header_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/layout/list_item_actor.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/layout/list_item_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/layout/menu_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/examples/android/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/android/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/android/src/main/res/mipmap-hdpi/ic_launcher_adaptive_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/android/src/main/res/mipmap-hdpi/ic_launcher_adaptive_back.png
--------------------------------------------------------------------------------
/examples/android/src/main/res/mipmap-hdpi/ic_launcher_adaptive_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/android/src/main/res/mipmap-hdpi/ic_launcher_adaptive_fore.png
--------------------------------------------------------------------------------
/examples/android/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/android/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/android/src/main/res/mipmap-mdpi/ic_launcher_adaptive_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/android/src/main/res/mipmap-mdpi/ic_launcher_adaptive_back.png
--------------------------------------------------------------------------------
/examples/android/src/main/res/mipmap-mdpi/ic_launcher_adaptive_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/android/src/main/res/mipmap-mdpi/ic_launcher_adaptive_fore.png
--------------------------------------------------------------------------------
/examples/android/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/android/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/android/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/android/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_back.png
--------------------------------------------------------------------------------
/examples/android/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/android/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png
--------------------------------------------------------------------------------
/examples/android/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/android/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/android/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/android/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png
--------------------------------------------------------------------------------
/examples/android/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/android/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png
--------------------------------------------------------------------------------
/examples/android/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/android/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/android/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/android/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png
--------------------------------------------------------------------------------
/examples/android/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/android/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png
--------------------------------------------------------------------------------
/examples/androidtv/README.md:
--------------------------------------------------------------------------------
1 | # InstantSearch examples for Android TV
2 |
3 | 
4 |
5 | ## How to run this example
6 |
7 | ### 1. Clone this repository
8 |
9 | ```sh
10 | git clone git@github.com:algolia/instantsearch-android.git
11 | ```
12 |
13 | ### 2. Build the project
14 |
15 | ```sh
16 | ./gradlew :examples:androidtv:assembleDebug
17 | ```
18 |
19 | ### 3. Install the app
20 |
21 | ```sh
22 | ./gradlew :examples:androidtv:installDebug
23 | ```
24 |
--------------------------------------------------------------------------------
/examples/androidtv/src/main/java/com/algolia/instantsearch/examples/androidtv/Suggestion.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.androidtv
2 |
3 | import com.algolia.instantsearch.core.highlighting.HighlightedString
4 | import com.algolia.instantsearch.highlighting.Highlightable
5 | import com.algolia.search.model.Attribute
6 | import kotlinx.serialization.Serializable
7 | import kotlinx.serialization.json.JsonObject
8 |
9 | @Serializable
10 | data class Suggestion(
11 | val query: String,
12 | override val _highlightResult: JsonObject? = null
13 | ) : Highlightable {
14 |
15 | val highlightedQuery: HighlightedString?
16 | get() = getHighlight(Attribute("query"))
17 | }
18 |
--------------------------------------------------------------------------------
/examples/androidtv/src/main/java/com/algolia/instantsearch/examples/androidtv/ui/Color.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.androidtv.ui
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val blue200 = Color(0xFF3377FF)
6 | val blue = Color(0xFF2266EE)
7 | val teal200 = Color(0XFF68E4DC)
8 | val errorColor = Color(0xFFB00020)
9 | val golden = Color(0xFFF2A23C)
10 | val grey = Color(0xFF989B9E)
11 | val searchBackground = Color(0xFF3D3D3D)
12 | val white = Color(0xFFDDDDDD)
13 |
--------------------------------------------------------------------------------
/examples/androidtv/src/main/java/com/algolia/instantsearch/examples/androidtv/ui/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.examples.androidtv.ui
2 |
3 | import androidx.compose.material.MaterialTheme
4 | import androidx.compose.material.darkColors
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.graphics.Color.Companion.Black
7 | import androidx.compose.ui.graphics.Color.Companion.White
8 |
9 | @Composable
10 | fun AppTheme(content: @Composable() () -> Unit) {
11 | MaterialTheme(
12 | colors = ColorPalette,
13 | content = content
14 | )
15 | }
16 |
17 | private val ColorPalette = darkColors(
18 | primary = blue200,
19 | primaryVariant = blue,
20 | secondary = teal200,
21 | background = Black,
22 | surface = Black,
23 | onPrimary = Black,
24 | onSecondary = White,
25 | onBackground = White,
26 | onSurface = White,
27 | error = errorColor
28 | )
29 |
--------------------------------------------------------------------------------
/examples/androidtv/src/main/res/drawable/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/androidtv/src/main/res/drawable/ic_launcher.png
--------------------------------------------------------------------------------
/examples/androidtv/src/main/res/drawable/movie.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/androidtv/src/main/res/drawable/movie.png
--------------------------------------------------------------------------------
/examples/androidtv/src/main/res/layout/activity_details.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/androidtv/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/androidtv/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/androidtv/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/examples/androidtv/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/androidtv/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/examples/androidtv/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/androidtv/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/examples/androidtv/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/androidtv/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/examples/androidtv/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/androidtv/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/examples/androidtv/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AndroidTV Showcase
3 |
4 |
--------------------------------------------------------------------------------
/examples/androidtv/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/examples/wearos/README.md:
--------------------------------------------------------------------------------
1 | # InstantSearch examples for WearOS
2 |
3 | 
4 |
5 | ## How to run this example
6 |
7 | ### 1. Clone this repository
8 |
9 | ```sh
10 | git clone git@github.com:algolia/instantsearch-android.git
11 | ```
12 |
13 | ### 2. Build the project
14 |
15 | ```sh
16 | ./gradlew :examples:wearos:assembleDebug
17 | ```
18 |
19 | ### 3. Install the app
20 |
21 | ```sh
22 | ./gradlew :examples:wearos:installDebug
23 | ```
24 |
--------------------------------------------------------------------------------
/examples/wearos/src/main/res/drawable-anydpi/ic_mic.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/wearos/src/main/res/drawable-hdpi/ic_mic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/wearos/src/main/res/drawable-hdpi/ic_mic.png
--------------------------------------------------------------------------------
/examples/wearos/src/main/res/drawable-mdpi/ic_mic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/wearos/src/main/res/drawable-mdpi/ic_mic.png
--------------------------------------------------------------------------------
/examples/wearos/src/main/res/drawable-xhdpi/ic_mic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/wearos/src/main/res/drawable-xhdpi/ic_mic.png
--------------------------------------------------------------------------------
/examples/wearos/src/main/res/drawable-xxhdpi/ic_mic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/wearos/src/main/res/drawable-xxhdpi/ic_mic.png
--------------------------------------------------------------------------------
/examples/wearos/src/main/res/drawable/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/wearos/src/main/res/drawable/logo.png
--------------------------------------------------------------------------------
/examples/wearos/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/wearos/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/examples/wearos/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/wearos/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/examples/wearos/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/wearos/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/examples/wearos/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/wearos/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/examples/wearos/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/examples/wearos/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/examples/wearos/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | 0dp
8 |
9 |
14 | 5dp
15 |
--------------------------------------------------------------------------------
/examples/wearos/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | WearOS Showcase
3 |
7 | microphone
8 | shows search
9 |
10 |
--------------------------------------------------------------------------------
/examples/wearos/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/extensions/android-loading/README.md:
--------------------------------------------------------------------------------
1 | # InstantSearch Android Loading
2 |
3 | You can add [Swiperefreshlayout](https://developer.android.com/jetpack/androidx/releases/swiperefreshlayout) support by adding the following line to your build.gradle's dependencies:
4 |
5 | ```groovy
6 | implementation "com.algolia:instantsearch-android-loading:$instantsearch_version"
7 | ```
8 |
--------------------------------------------------------------------------------
/extensions/android-loading/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=InstantSearch Android Loading Extension
2 | POM_ARTIFACT_ID=instantsearch-android-loading
3 |
--------------------------------------------------------------------------------
/extensions/android-loading/src/main/kotlin/com/algolia/instantsearch/android/loading/LoadingViewSwipeRefreshLayout.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.android.loading
2 |
3 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
4 | import com.algolia.instantsearch.core.loading.LoadingView
5 |
6 | public class LoadingViewSwipeRefreshLayout(
7 | public val swipeRefreshLayout: SwipeRefreshLayout,
8 | ) : LoadingView {
9 |
10 | override var onReload: ((Unit) -> Unit)? = null
11 |
12 | init {
13 | swipeRefreshLayout.setOnRefreshListener { onReload?.invoke(Unit) }
14 | }
15 |
16 | override fun setIsLoading(isLoading: Boolean) {
17 | swipeRefreshLayout.isRefreshing = isLoading
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/extensions/android-paging3/README.md:
--------------------------------------------------------------------------------
1 | # InstantSearch Android Paging 3
2 |
3 | You can add [Paging 3](https://developer.android.com/topic/libraries/architecture/paging/v3-overview) support by adding the following line to your build.gradle's dependencies:
4 |
5 | ```groovy
6 | implementation "com.algolia:instantsearch-android-paging3:$instantsearch_version"
7 | ```
8 |
--------------------------------------------------------------------------------
/extensions/android-paging3/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=InstantSearch Android Paging3 Extension
2 | POM_ARTIFACT_ID=instantsearch-android-paging3
3 |
--------------------------------------------------------------------------------
/extensions/android-paging3/src/main/kotlin/com/algolia/instantsearch/android/paging3/filterstate/FilterStateConnection.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.android.paging3.filterstate
2 |
3 | import com.algolia.instantsearch.android.paging3.Paginator
4 | import com.algolia.instantsearch.android.paging3.filterstate.internal.FilterStateConnectionPaginator
5 | import com.algolia.instantsearch.core.connection.Connection
6 | import com.algolia.instantsearch.filter.state.FilterState
7 |
8 | /**
9 | * Create a connection between a [Paginator] and [FilterState].
10 | *
11 | * @param paginator paginator to be connected
12 | */
13 | public fun FilterState.connectPaginator(paginator: Paginator): Connection {
14 | return FilterStateConnectionPaginator(paginator, this)
15 | }
16 |
--------------------------------------------------------------------------------
/extensions/coroutines-extensions/README.md:
--------------------------------------------------------------------------------
1 | # InstantSearch Coroutines Extensions
2 |
3 | You can add coroutines extensions (e.g Flow) support by adding the following line to your build.gradle's dependencies:
4 |
5 | ```groovy
6 | implementation "com.algolia:instantsearch-coroutines-extensions:$instantsearch_version"
7 | ```
8 |
--------------------------------------------------------------------------------
/extensions/coroutines-extensions/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=InstantSearch Coroutines Extensions
2 | POM_ARTIFACT_ID=instantsearch-coroutines-extensions
3 |
--------------------------------------------------------------------------------
/extensions/coroutines-extensions/src/commonMain/kotlin/com/algolia/instantsearch/coroutines/Subscription.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.coroutines
2 |
3 | import com.algolia.instantsearch.ExperimentalInstantSearch
4 | import com.algolia.instantsearch.core.subscription.Subscription
5 | import kotlinx.coroutines.channels.awaitClose
6 | import kotlinx.coroutines.flow.Flow
7 | import kotlinx.coroutines.flow.callbackFlow
8 |
9 | /**
10 | * Turns this [Subscription] into a _(hot)_ [Flow] which emits whenever there is a new value.
11 | */
12 | @ExperimentalInstantSearch
13 | public fun Subscription.asFlow(): Flow {
14 | return callbackFlow {
15 | val callback: (T) -> Unit = { event: T -> trySend(event) }
16 | val subscription = this@asFlow
17 | subscription.subscribe(callback)
18 | awaitClose { subscription.unsubscribe(callback) }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/algolia/instantsearch-android/eaf37a61303a8b9c5688a596e8aeab29dfbba398/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
4 | networkTimeout=10000
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/instantsearch-compose/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=InstantSearch Compose
2 | POM_ARTIFACT_ID=instantsearch-compose
3 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/main/java/com/algolia/instantsearch/compose/customdata/QueryRuleCustomDataState.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("FunctionName")
2 |
3 | package com.algolia.instantsearch.compose.customdata
4 |
5 | import com.algolia.instantsearch.compose.customdata.internal.QueryRuleCustomDataStateImpl
6 | import com.algolia.instantsearch.customdata.QueryRuleCustomDataPresenter
7 |
8 | /**
9 | * [QueryRuleCustomDataPresenter] for compose.
10 | */
11 | public interface QueryRuleCustomDataState : QueryRuleCustomDataPresenter {
12 |
13 | /**
14 | * Custom data item value.
15 | */
16 | public val item: T?
17 | }
18 |
19 | /**
20 | * Creates an instance of [QueryRuleCustomDataState].
21 | *
22 | * @param initialItem initial item value
23 | */
24 | public fun QueryRuleCustomDataState(initialItem: T? = null): QueryRuleCustomDataState {
25 | return QueryRuleCustomDataStateImpl(initialItem)
26 | }
27 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/main/java/com/algolia/instantsearch/compose/customdata/internal/QueryRuleCustomDataStateImpl.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.customdata.internal
2 |
3 | import androidx.compose.runtime.getValue
4 | import androidx.compose.runtime.mutableStateOf
5 | import androidx.compose.runtime.setValue
6 | import com.algolia.instantsearch.compose.customdata.QueryRuleCustomDataState
7 | import com.algolia.instantsearch.compose.internal.trace
8 |
9 | internal class QueryRuleCustomDataStateImpl(initialItem: T?) : QueryRuleCustomDataState {
10 |
11 | override var item: T? by mutableStateOf(initialItem)
12 |
13 | init {
14 | trace()
15 | }
16 |
17 | override fun invoke(newItem: T?) {
18 | this.item = newItem
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/main/java/com/algolia/instantsearch/compose/filter/clear/FilterClear.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.filter.clear
2 |
3 | import com.algolia.instantsearch.compose.filter.clear.internal.FilterClearImpl
4 | import com.algolia.instantsearch.filter.clear.FilterClearView
5 |
6 | /**
7 | * [FilterClearView] for compose.
8 | */
9 | public interface FilterClear : FilterClearView {
10 |
11 | /**
12 | * Triggers filter clear.
13 | */
14 | public fun clear()
15 | }
16 |
17 | /**
18 | * Creates an instance of [FilterClear].
19 | */
20 | public fun FilterClear(): FilterClear {
21 | return FilterClearImpl()
22 | }
23 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/main/java/com/algolia/instantsearch/compose/filter/clear/internal/FilterClearImpl.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.filter.clear.internal
2 |
3 | import com.algolia.instantsearch.compose.filter.clear.FilterClear
4 | import com.algolia.instantsearch.compose.internal.trace
5 | import com.algolia.instantsearch.core.Callback
6 |
7 | /**
8 | * Implementation of [FilterClear].
9 | */
10 | internal class FilterClearImpl : FilterClear {
11 |
12 | override var onClear: Callback? = null
13 |
14 | init {
15 | trace()
16 | }
17 |
18 | override fun clear() {
19 | onClear?.invoke(Unit)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/main/java/com/algolia/instantsearch/compose/filter/facet/FacetListState.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.filter.facet
2 |
3 | import com.algolia.instantsearch.compose.filter.facet.internal.FacetListStateImpl
4 | import com.algolia.instantsearch.compose.selectable.list.SelectableListState
5 | import com.algolia.instantsearch.core.selectable.list.SelectableItem
6 | import com.algolia.instantsearch.filter.facet.FacetListView
7 | import com.algolia.search.model.search.Facet
8 |
9 | /**
10 | * [FacetListView] for compose.
11 | */
12 | public interface FacetListState : FacetListView, SelectableListState
13 |
14 | /**
15 | * Creates an instance of [FacetListState].
16 | *
17 | * @param facets selectable facets list
18 | */
19 | public fun FacetListState(facets: List> = emptyList()): FacetListState {
20 | return FacetListStateImpl(facets)
21 | }
22 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/main/java/com/algolia/instantsearch/compose/filter/facet/internal/FacetListStateImpl.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.filter.facet.internal
2 |
3 | import com.algolia.instantsearch.compose.filter.facet.FacetListState
4 | import com.algolia.instantsearch.compose.internal.trace
5 | import com.algolia.instantsearch.compose.selectable.list.SelectableListState
6 | import com.algolia.instantsearch.core.selectable.list.SelectableItem
7 | import com.algolia.search.model.search.Facet
8 |
9 | /**
10 | * [FacetListState] implementation.
11 | *
12 | * @param items initial facets list value
13 | */
14 | internal class FacetListStateImpl(
15 | items: List>
16 | ) : FacetListState, SelectableListState by SelectableListState(items) {
17 |
18 | init {
19 | trace()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/main/java/com/algolia/instantsearch/compose/filter/list/internal/FilterListStateImpl.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.filter.list.internal
2 |
3 | import com.algolia.instantsearch.compose.filter.list.FilterListState
4 | import com.algolia.instantsearch.compose.internal.trace
5 | import com.algolia.instantsearch.compose.selectable.list.SelectableListState
6 | import com.algolia.instantsearch.core.selectable.list.SelectableItem
7 | import com.algolia.search.model.filter.Filter
8 |
9 | /**
10 | * [FilterListState] implementation.
11 | *
12 | * @param items initial filters list items
13 | */
14 | internal class FilterListStateImpl(
15 | items: List>
16 | ) : FilterListState, SelectableListState by SelectableListState(items) {
17 |
18 | init {
19 | trace()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/main/java/com/algolia/instantsearch/compose/filter/toggle/internal/FilterToggleStateImpl.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.filter.toggle.internal
2 |
3 | import com.algolia.instantsearch.compose.filter.toggle.FilterToggleState
4 | import com.algolia.instantsearch.compose.internal.trace
5 | import com.algolia.instantsearch.compose.selectable.SelectableItemState
6 |
7 | internal class FilterToggleStateImpl(
8 | text: String,
9 | isSelected: Boolean
10 | ) : FilterToggleState, SelectableItemState by SelectableItemState(text, isSelected) {
11 |
12 | init {
13 | trace()
14 | }
15 |
16 | override fun changeSelection(isSelected: Boolean) {
17 | onSelectionChanged?.invoke(isSelected)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/main/java/com/algolia/instantsearch/compose/hierarchical/HierarchicalState.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.hierarchical
2 |
3 | import com.algolia.instantsearch.compose.hierarchical.internal.HierarchicalStateImpl
4 | import com.algolia.instantsearch.hierarchical.HierarchicalItem
5 | import com.algolia.instantsearch.hierarchical.HierarchicalView
6 |
7 | /**
8 | * [HierarchicalView] for compose.
9 | */
10 | public interface HierarchicalState : HierarchicalView {
11 |
12 | /**
13 | * Hierarchical items list.
14 | */
15 | public val hierarchicalItems: List
16 | }
17 |
18 | /**
19 | * Creates an instance of HierarchicalState.
20 | *
21 | * @param hierarchicalItems initial hierarchical items list
22 | */
23 | public fun HierarchicalState(hierarchicalItems: List = emptyList()): HierarchicalState {
24 | return HierarchicalStateImpl(hierarchicalItems)
25 | }
26 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/main/java/com/algolia/instantsearch/compose/hits/HitsState.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("FunctionName")
2 |
3 | package com.algolia.instantsearch.compose.hits
4 |
5 | import com.algolia.instantsearch.compose.hits.internal.HitsStateImpl
6 | import com.algolia.instantsearch.core.hits.HitsView
7 |
8 | /**
9 | * [HitsView] for compose.
10 | */
11 | public interface HitsState : HitsView {
12 |
13 | /**
14 | * Hits list.
15 | */
16 | public val hits: List
17 | }
18 |
19 | /**
20 | * Creates an instance of [HitsState].
21 | *
22 | * @param hits initial hits list value
23 | */
24 | public fun HitsState(hits: List = emptyList()): HitsState {
25 | return HitsStateImpl(hits)
26 | }
27 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/main/java/com/algolia/instantsearch/compose/hits/internal/HitsStateImpl.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.hits.internal
2 |
3 | import androidx.compose.runtime.getValue
4 | import androidx.compose.runtime.mutableStateOf
5 | import androidx.compose.runtime.setValue
6 | import com.algolia.instantsearch.compose.hits.HitsState
7 | import com.algolia.instantsearch.compose.internal.trace
8 |
9 | /**
10 | * Implementation of [HitsState].
11 | *
12 | * @param hitsList initial hits list value
13 | */
14 | internal class HitsStateImpl(hitsList: List) : HitsState {
15 |
16 | @set:JvmName("_hits")
17 | override var hits: List by mutableStateOf(hitsList)
18 |
19 | init {
20 | trace()
21 | }
22 |
23 | override fun setHits(hits: List) {
24 | this.hits = hits
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/main/java/com/algolia/instantsearch/compose/item/internal/StatsStateImpl.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.item.internal
2 |
3 | import androidx.compose.runtime.getValue
4 | import androidx.compose.runtime.mutableStateOf
5 | import androidx.compose.runtime.setValue
6 | import com.algolia.instantsearch.compose.internal.trace
7 | import com.algolia.instantsearch.compose.item.StatsState
8 |
9 | /**
10 | * [StatsState] implementation.
11 | */
12 | internal class StatsStateImpl(stats: T) : StatsState {
13 |
14 | override var stats by mutableStateOf(stats)
15 |
16 | init {
17 | trace()
18 | }
19 |
20 | override fun setText(text: T) {
21 | this.stats = text
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/main/java/com/algolia/instantsearch/compose/loading/LoadingState.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.loading
2 |
3 | import com.algolia.instantsearch.compose.loading.internal.LoadingStateImpl
4 | import com.algolia.instantsearch.core.loading.LoadingView
5 |
6 | /**
7 | * [LoadingView] for compose.
8 | */
9 | public interface LoadingState : LoadingView {
10 |
11 | /**
12 | * true when loading, false otherwise.
13 | */
14 | public val loading: Boolean
15 |
16 | /**
17 | * Request a reload. Used typically for swipe to refresh.
18 | */
19 | public fun reload()
20 | }
21 |
22 | /**
23 | * Creates an instance of [LoadingState].
24 | *
25 | * @param loading initial loading value
26 | */
27 | public fun LoadingState(loading: Boolean = false): LoadingState {
28 | return LoadingStateImpl(loading)
29 | }
30 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/main/java/com/algolia/instantsearch/compose/number/relevantsort/internal/RelevantSortStateImpl.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.number.relevantsort.internal
2 |
3 | import androidx.compose.runtime.getValue
4 | import androidx.compose.runtime.mutableStateOf
5 | import androidx.compose.runtime.setValue
6 | import com.algolia.instantsearch.compose.internal.trace
7 | import com.algolia.instantsearch.compose.number.relevantsort.RelevantSortState
8 |
9 | /**
10 | * Implementation of [RelevantSortState].
11 | *
12 | * @param value initial value
13 | */
14 | internal class RelevantSortStateImpl(value: T) : RelevantSortState {
15 |
16 | override var sort: T by mutableStateOf(value)
17 | override var didToggle: (() -> Unit)? = null
18 |
19 | init {
20 | trace()
21 | }
22 |
23 | override fun updateView(input: T) {
24 | this.sort = input
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/main/java/com/algolia/instantsearch/compose/selectable/list/internal/SelectableListStateImpl.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.selectable.list.internal
2 |
3 | import androidx.compose.runtime.getValue
4 | import androidx.compose.runtime.mutableStateOf
5 | import androidx.compose.runtime.setValue
6 | import com.algolia.instantsearch.compose.selectable.list.SelectableListState
7 | import com.algolia.instantsearch.core.Callback
8 | import com.algolia.instantsearch.core.selectable.list.SelectableItem
9 |
10 | internal class SelectableListStateImpl(
11 | items: List>
12 | ) : SelectableListState {
13 |
14 | @set:JvmName("_items")
15 | override var items by mutableStateOf(items)
16 | override var onSelection: Callback? = null
17 |
18 | override fun setItems(items: List>) {
19 | this.items = items
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Search…
3 |
4 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/test/kotlin/com/algolia/instantsearch/compose/customdata/QueryRuleCustomDataStateTest.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.customdata
2 |
3 | import androidx.compose.runtime.snapshots.Snapshot
4 | import kotlin.test.Test
5 | import kotlin.test.assertEquals
6 |
7 | public class QueryRuleCustomDataStateTest {
8 |
9 | @Test
10 | public fun testItem() {
11 | val customData = QueryRuleCustomDataState("")
12 | Snapshot.takeSnapshot {
13 | customData("android")
14 | assertEquals("android", customData.item)
15 | }
16 | assertEquals("", customData.item)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/test/kotlin/com/algolia/instantsearch/compose/filter/FilterClearTest.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.filter
2 |
3 | import com.algolia.instantsearch.compose.filter.clear.FilterClear
4 | import kotlin.test.Test
5 | import kotlin.test.assertEquals
6 |
7 | public class FilterClearTest {
8 |
9 | @Test
10 | public fun testClear() {
11 | var action = ""
12 | val filterClear = FilterClear().apply {
13 | onClear = { action = "cleared" }
14 | }
15 | assertEquals("", action)
16 | filterClear.clear()
17 | assertEquals("cleared", action)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/test/kotlin/com/algolia/instantsearch/compose/hierarchical/HierarchicalStateTest.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.hierarchical
2 |
3 | import androidx.compose.runtime.snapshots.Snapshot
4 | import com.algolia.instantsearch.hierarchical.HierarchicalItem
5 | import com.algolia.search.model.search.Facet
6 | import kotlin.test.Test
7 | import kotlin.test.assertEquals
8 |
9 | public class HierarchicalStateTest {
10 |
11 | @Test
12 | public fun testHierarchicalItems() {
13 | val hierarchicalState = HierarchicalState()
14 | Snapshot.takeSnapshot {
15 | val elements = listOf(HierarchicalItem(Facet("android", 1), "Android", 1))
16 | hierarchicalState.setTree(elements)
17 | assertEquals(elements, hierarchicalState.hierarchicalItems)
18 | }
19 | assertEquals(emptyList(), hierarchicalState.hierarchicalItems)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/test/kotlin/com/algolia/instantsearch/compose/hits/HitsStateTest.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.hits
2 |
3 | import androidx.compose.runtime.snapshots.Snapshot
4 | import kotlin.test.Test
5 | import kotlin.test.assertEquals
6 |
7 | public class HitsStateTest {
8 |
9 | @Test
10 | public fun testHits() {
11 | val hits = listOf("Apple", "Samsung", "Sony", "LG")
12 | val hitsState = HitsState(hits)
13 | Snapshot.takeSnapshot {
14 | hitsState.setHits(emptyList())
15 | assertEquals(emptyList(), hitsState.hits)
16 | }
17 | assertEquals(hits, hitsState.hits)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/test/kotlin/com/algolia/instantsearch/compose/item/StatsTest.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.item
2 |
3 | import androidx.compose.runtime.snapshots.Snapshot
4 | import kotlin.test.Test
5 | import kotlin.test.assertEquals
6 |
7 | public class StatsTest {
8 |
9 | @Test
10 | public fun testStats() {
11 | val stats = StatsTextState("0")
12 | Snapshot.takeSnapshot {
13 | stats.setText("42")
14 | assertEquals("42", stats.stats)
15 | }
16 | assertEquals("0", stats.stats)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/test/kotlin/com/algolia/instantsearch/compose/loading/LoadingStateTest.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.loading
2 |
3 | import androidx.compose.runtime.snapshots.Snapshot
4 | import kotlin.test.Test
5 | import kotlin.test.assertEquals
6 |
7 | public class LoadingStateTest {
8 |
9 | @Test
10 | public fun testLoading() {
11 | val loadingState = LoadingState(false)
12 | loadingState.onReload = { loadingState.setIsLoading(true) }
13 | Snapshot.takeSnapshot {
14 | loadingState.reload()
15 | assertEquals(true, loadingState.loading)
16 | }
17 | assertEquals(false, loadingState.loading)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/test/kotlin/com/algolia/instantsearch/compose/searchbox/SearchBoxStateTest.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.searchbox
2 |
3 | import androidx.compose.runtime.snapshots.Snapshot
4 | import kotlin.test.Test
5 | import kotlin.test.assertEquals
6 |
7 | public class SearchBoxStateTest {
8 |
9 | @Test
10 | public fun test() {
11 | var query: String? = null
12 | val init = ""
13 | val searchBoxState = SearchBoxState(init)
14 | searchBoxState.onQueryChanged = { query = it }
15 | Snapshot.takeSnapshot {
16 | val text = "phone"
17 | searchBoxState.setText(text)
18 | assertEquals(text, searchBoxState.query)
19 | assertEquals(text, query)
20 | }
21 | assertEquals(init, searchBoxState.query)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/instantsearch-compose/src/test/kotlin/com/algolia/instantsearch/compose/selectable/SelectableListStateTest.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.compose.selectable
2 |
3 | import androidx.compose.runtime.snapshots.Snapshot
4 | import com.algolia.instantsearch.compose.selectable.list.SelectableListState
5 | import kotlin.test.Test
6 | import kotlin.test.assertEquals
7 |
8 | public class SelectableListStateTest {
9 |
10 | private val initList = listOf("item" to true)
11 | private val selectableListState = SelectableListState(initList)
12 |
13 | @Test
14 | public fun testItems() {
15 | val itemState = selectableListState.items
16 | assertEquals(initList, itemState)
17 | Snapshot.takeSnapshot {
18 | val items = listOf("newItem" to false)
19 | selectableListState.setItems(items)
20 | assertEquals(items, itemState)
21 | }
22 | assertEquals(initList, itemState)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/instantsearch-core/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=InstantSearch Core
2 | POM_ARTIFACT_ID=instantsearch-core
3 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/Callback.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core
2 |
3 | public typealias Callback = ((T) -> Unit)
4 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/InstantSearch.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core
2 |
3 | import com.algolia.instantsearch.BuildConfig
4 |
5 | public object InstantSearch {
6 |
7 | public const val version: String = BuildConfig.version
8 |
9 | public const val userAgent: String = "InstantSearchAndroid ($version)"
10 | }
11 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/Presenter.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core
2 |
3 | public typealias Presenter = (I) -> O
4 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/connection/AbstractConnection.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.connection
2 |
3 | /**
4 | * Abstract [Connection] implementation.
5 | */
6 | public abstract class AbstractConnection : Connection {
7 |
8 | final override var isConnected: Boolean = false
9 | private set
10 |
11 | override fun connect() {
12 | isConnected = true
13 | }
14 |
15 | override fun disconnect() {
16 | isConnected = false
17 | }
18 | }
19 |
20 | @Deprecated("use AbstractConnection instead", replaceWith = ReplaceWith("AbstractConnection"))
21 | public typealias ConnectionImpl = AbstractConnection
22 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/connection/Connection.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.connection
2 |
3 | public interface Connection {
4 |
5 | public val isConnected: Boolean
6 |
7 | public fun connect()
8 |
9 | public fun disconnect()
10 | }
11 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/highlighting/HighlightTags.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.highlighting
2 |
3 | public const val DefaultPreTag: String = ""
4 | public const val DefaultPostTag: String = ""
5 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/highlighting/HighlightToken.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.highlighting
2 |
3 | /**
4 | * A token from a highlighted string.
5 | */
6 | public data class HighlightToken(val content: String, val highlighted: Boolean = false)
7 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/highlighting/HighlightedString.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.highlighting
2 |
3 | public data class HighlightedString(
4 | val original: String,
5 | val tokens: List
6 | ) : Comparable {
7 |
8 | public val highlightedTokens: List
9 | get() = tokens.filter { it.highlighted }.map { it.content }
10 |
11 | override fun compareTo(other: HighlightedString): Int {
12 | return original.compareTo(other.original)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/hits/HitsConnection.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.hits
2 |
3 | import com.algolia.instantsearch.core.Presenter
4 | import com.algolia.instantsearch.core.connection.Connection
5 | import com.algolia.instantsearch.core.hits.internal.HitsConnectionView
6 | import com.algolia.instantsearch.core.searcher.Searcher
7 |
8 | /**
9 | * Connects [Searcher] to a [HitsView].
10 | *
11 | * @param adapter hits view adapter
12 | * @param past subscribe to past result
13 | * @param presenter hits presenter
14 | */
15 | public fun Searcher.connectHitsView(
16 | adapter: HitsView,
17 | past: Boolean = false,
18 | presenter: Presenter>
19 | ): Connection {
20 | return HitsConnectionView(this, adapter, presenter, past)
21 | }
22 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/hits/HitsView.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.hits
2 |
3 | public interface HitsView {
4 |
5 | public fun setHits(hits: List)
6 | }
7 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/internal/Collections.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.internal
2 |
3 | /**
4 | * Creates a copy-on-write list.
5 | */
6 | internal expect fun frozenCopyOnWriteSet(collection: Collection? = null): MutableSet
7 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/internal/Exception.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("FunctionName")
2 |
3 | package com.algolia.instantsearch.core.internal
4 |
5 | import kotlinx.coroutines.CancellationException
6 |
7 | /**
8 | * Cancellation due to operation abort.
9 | */
10 | internal fun AbortException(canceller: String) = CancellationException("$canceller operation abort")
11 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/loading/LoadingConnection.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.loading
2 |
3 | import com.algolia.instantsearch.core.connection.Connection
4 | import com.algolia.instantsearch.core.loading.internal.LoadingConnectionView
5 |
6 | public fun LoadingViewModel.connectView(view: LoadingView): Connection {
7 | return LoadingConnectionView(this, view)
8 | }
9 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/loading/LoadingView.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.loading
2 |
3 | import com.algolia.instantsearch.core.Callback
4 |
5 | public interface LoadingView {
6 |
7 | public var onReload: Callback?
8 |
9 | public fun setIsLoading(isLoading: Boolean)
10 | }
11 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/loading/LoadingViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.loading
2 |
3 | import com.algolia.instantsearch.core.internal.traceLoading
4 | import com.algolia.instantsearch.core.subscription.SubscriptionEvent
5 | import com.algolia.instantsearch.core.subscription.SubscriptionValue
6 |
7 | public open class LoadingViewModel(
8 | isLoading: Boolean = false
9 | ) {
10 | init {
11 | traceLoading(isLoading)
12 | }
13 |
14 | public val isLoading: SubscriptionValue = SubscriptionValue(isLoading)
15 | public val eventReload: SubscriptionEvent = SubscriptionEvent()
16 | }
17 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/number/DefaultNumberPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.number
2 |
3 | public object DefaultNumberPresenter : NumberPresenter {
4 |
5 | override fun invoke(number: Number?): String {
6 | return number?.toString() ?: "-"
7 | }
8 | }
9 |
10 | @Deprecated(message = "use DefaultNumberPresenter instead", replaceWith = ReplaceWith("DefaultNumberPresenter"))
11 | public typealias NumberPresenterImpl = DefaultNumberPresenter
12 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/number/NumberConnection.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.number
2 |
3 | import com.algolia.instantsearch.core.connection.Connection
4 | import com.algolia.instantsearch.core.number.internal.NumberConnectionView
5 |
6 | public fun NumberViewModel.connectView(
7 | view: NumberView,
8 | presenter: NumberPresenter = DefaultNumberPresenter
9 | ): Connection where T : Number, T : Comparable {
10 | return NumberConnectionView(this, view, presenter)
11 | }
12 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/number/NumberPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.number
2 |
3 | public typealias NumberPresenter = (T?) -> String
4 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/number/NumberView.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.number
2 |
3 | /**
4 | * The view that renders the numeric value.
5 | */
6 | public interface NumberView {
7 |
8 | public fun setText(text: String)
9 |
10 | public fun setComputation(computation: Computation)
11 | }
12 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/number/range/NumberRangeConnection.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.number.range
2 |
3 | import com.algolia.instantsearch.core.connection.Connection
4 | import com.algolia.instantsearch.core.number.range.internal.NumberRangeConnectionView
5 |
6 | public fun NumberRangeViewModel.connectView(
7 | view: NumberRangeView
8 | ): Connection where T : Number, T : Comparable {
9 | return NumberRangeConnectionView(this, view)
10 | }
11 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/number/range/NumberRangeView.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.number.range
2 |
3 | import com.algolia.instantsearch.core.Callback
4 |
5 | public interface NumberRangeView where T : Number, T : Comparable {
6 |
7 | public var onRangeChanged: Callback>?
8 |
9 | public fun setRange(range: Range?)
10 |
11 | public fun setBounds(bounds: Range?)
12 | }
13 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/number/range/Range.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.number.range
2 |
3 | public data class Range(
4 | public val min: T,
5 | public val max: T
6 | ) where T : Number, T : Comparable {
7 |
8 | public companion object {
9 |
10 | public operator fun invoke(range: ClosedRange): Range where T : Number, T : Comparable {
11 | return Range(range.start, range.endInclusive)
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/number/range/internal/Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.number.range.internal
2 |
3 | import com.algolia.instantsearch.core.number.range.Range
4 |
5 | internal fun Range.coerce(bounds: Range?): Range where T : Number, T : Comparable {
6 | return bounds?.let {
7 | val coercedMin = min.coerceIn(it.min, it.max)
8 | val coercedMax = max.coerceIn(it.min, it.max)
9 |
10 | Range(coercedMin, coercedMax)
11 | } ?: this
12 | }
13 |
14 | internal fun T.coerce(bounds: Range?): T where T : Number, T : Comparable {
15 | return bounds?.let { coerceIn(it.min, it.max) } ?: this
16 | }
17 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/relevantsort/RelevantSortConnector.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.relevantsort
2 |
3 | import com.algolia.instantsearch.core.connection.Connection
4 | import com.algolia.instantsearch.core.searcher.Searcher
5 |
6 | /**
7 | * High-level component automatically establishing the connections between relevant sort components.
8 | */
9 | public interface RelevantSortConnector : Connection {
10 |
11 | /**
12 | * The component handling relevant sort logic.
13 | */
14 | public val viewModel: RelevantSortViewModel
15 |
16 | /**
17 | * Searcher that handles your searches.
18 | */
19 | public val searcher: Searcher<*>
20 | }
21 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/relevantsort/RelevantSortPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.relevantsort
2 |
3 | /**
4 | * Defines the way we want to interact with a Relevant sort priority value.
5 | */
6 | public typealias RelevantSortPresenter = (RelevantSortPriority?) -> T
7 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/relevantsort/RelevantSortPriority.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.relevantsort
2 |
3 | import kotlin.jvm.JvmStatic
4 |
5 | /**
6 | * Represents the priority to apply to the search in the dynamically sorted index
7 | *
8 | * @param relevancyStrictness relevancy strictness value to apply to the search
9 | */
10 | public enum class RelevantSortPriority(
11 | public val relevancyStrictness: Int
12 | ) {
13 |
14 | /** Prioritize less more relevant results */
15 | Relevancy(100),
16 |
17 | /** Prioritize more less relevant results */
18 | HitsCount(0);
19 |
20 | public companion object {
21 |
22 | @JvmStatic
23 | public fun of(relevancyStrictness: Int): RelevantSortPriority = when (relevancyStrictness) {
24 | 0 -> HitsCount
25 | else -> Relevancy
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/relevantsort/RelevantSortView.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.relevantsort
2 |
3 | /**
4 | * View presenting the relevant sort priority applied to the search and a toggle control.
5 | */
6 | public interface RelevantSortView {
7 |
8 | /**
9 | * Lambda triggered by the controller when toggle interaction occurred
10 | * (for example, toggle button clicked or switch changed).
11 | */
12 | public var didToggle: (() -> Unit)?
13 |
14 | /**
15 | * Set a priority to the view.
16 | */
17 | public fun updateView(input: T)
18 | }
19 |
20 | /**
21 | * Convenience implementation of [RelevantSortView] with [RelevantSortPriority] as view update input.
22 | */
23 | public typealias RelevantSortPriorityView = RelevantSortView
24 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/searchbox/SearchBoxConnection.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.searchbox
2 |
3 | import com.algolia.instantsearch.core.connection.Connection
4 | import com.algolia.instantsearch.core.searchbox.internal.SearchBoxConnectionView
5 |
6 | public fun SearchBoxViewModel.connectView(
7 | view: SearchBoxView
8 | ): Connection {
9 | return SearchBoxConnectionView(this, view)
10 | }
11 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/searchbox/SearchBoxView.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.searchbox
2 |
3 | import com.algolia.instantsearch.core.Callback
4 |
5 | public interface SearchBoxView {
6 |
7 | public var onQueryChanged: Callback?
8 | public var onQuerySubmitted: Callback?
9 |
10 | public fun setText(text: String?, submitQuery: Boolean = false)
11 | }
12 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/searchbox/SearchBoxViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.searchbox
2 |
3 | import com.algolia.instantsearch.core.internal.traceSearchBox
4 | import com.algolia.instantsearch.core.subscription.SubscriptionEvent
5 | import com.algolia.instantsearch.core.subscription.SubscriptionValue
6 |
7 | public open class SearchBoxViewModel {
8 |
9 | public val query: SubscriptionValue = SubscriptionValue(null)
10 | public val eventSubmit: SubscriptionEvent = SubscriptionEvent()
11 |
12 | init {
13 | traceSearchBox()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/searcher/Constants.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.searcher
2 |
3 | public const val debounceLoadingInMillis: Long = 200L
4 | public const val debounceSearchInMillis: Long = 100L
5 | public const val debounceFilteringInMillis: Long = 0L
6 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/searcher/SearcherConnection.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.searcher
2 |
3 | import com.algolia.instantsearch.core.connection.Connection
4 | import com.algolia.instantsearch.core.searcher.internal.SearcherConnectionView
5 |
6 | public fun Searcher.connectView(view: (T) -> Unit, transform: (R?) -> T): Connection {
7 | return SearcherConnectionView(this, view, transform)
8 | }
9 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/selectable/SelectableItemConnection.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.selectable
2 |
3 | import com.algolia.instantsearch.core.Presenter
4 | import com.algolia.instantsearch.core.connection.Connection
5 | import com.algolia.instantsearch.core.selectable.internal.SelectableItemConnectionView
6 |
7 | public fun SelectableItemViewModel.connectView(
8 | view: SelectableItemView,
9 | presenter: Presenter
10 | ): Connection {
11 | return SelectableItemConnectionView(this, view, presenter)
12 | }
13 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/selectable/SelectableItemView.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.selectable
2 |
3 | import com.algolia.instantsearch.core.Callback
4 |
5 | public interface SelectableItemView {
6 |
7 | public var onSelectionChanged: Callback?
8 |
9 | public fun setItem(item: T)
10 | public fun setIsSelected(isSelected: Boolean)
11 | }
12 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/selectable/SelectableItemViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.selectable
2 |
3 | import com.algolia.instantsearch.core.internal.traceFilterToggle
4 | import com.algolia.instantsearch.core.subscription.SubscriptionEvent
5 | import com.algolia.instantsearch.core.subscription.SubscriptionValue
6 |
7 | public open class SelectableItemViewModel(
8 | item: T,
9 | isSelected: Boolean = false
10 | ) {
11 |
12 | init {
13 | traceFilterToggle(isSelected)
14 | }
15 |
16 | public val item: SubscriptionValue = SubscriptionValue(item)
17 | public val isSelected: SubscriptionValue = SubscriptionValue(isSelected)
18 | public val eventSelection: SubscriptionEvent = SubscriptionEvent()
19 | }
20 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/selectable/list/SelectableItem.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.selectable.list
2 |
3 | public typealias SelectableItem = Pair
4 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/selectable/list/SelectableListConnection.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.selectable.list
2 |
3 | import com.algolia.instantsearch.core.connection.Connection
4 | import com.algolia.instantsearch.core.selectable.list.internal.SelectableListConnectionView
5 |
6 | public fun SelectableListViewModel.connectView(
7 | view: SelectableListView
8 | ): Connection {
9 | return SelectableListConnectionView(this, view)
10 | }
11 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/selectable/list/SelectableListView.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.selectable.list
2 |
3 | import com.algolia.instantsearch.core.Callback
4 |
5 | public interface SelectableListView {
6 |
7 | public var onSelection: Callback?
8 |
9 | public fun setItems(items: List>)
10 | }
11 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/selectable/list/SelectionMode.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.selectable.list
2 |
3 | public enum class SelectionMode {
4 | Single,
5 | Multiple
6 | }
7 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/selectable/map/SelectableMapConnection.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.selectable.map
2 |
3 | import com.algolia.instantsearch.core.Presenter
4 | import com.algolia.instantsearch.core.connection.Connection
5 | import com.algolia.instantsearch.core.selectable.map.internal.SelectableMapConnectionView
6 |
7 | public fun SelectableMapViewModel.connectView(
8 | view: SelectableMapView,
9 | presenter: Presenter
10 | ): Connection {
11 | return SelectableMapConnectionView(this, view, presenter)
12 | }
13 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/selectable/map/SelectableMapView.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.selectable.map
2 |
3 | import com.algolia.instantsearch.core.Callback
4 |
5 | public interface SelectableMapView {
6 |
7 | public var onSelectionChange: Callback?
8 |
9 | public fun setMap(map: Map)
10 |
11 | public fun setSelected(selected: K?)
12 | }
13 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/selectable/map/SelectableMapViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.selectable.map
2 |
3 | import com.algolia.instantsearch.core.map.MapViewModel
4 | import com.algolia.instantsearch.core.subscription.SubscriptionEvent
5 | import com.algolia.instantsearch.core.subscription.SubscriptionValue
6 |
7 | public open class SelectableMapViewModel(
8 | map: Map = emptyMap(),
9 | selected: K? = null
10 | ) : MapViewModel(map) {
11 |
12 | public val selected: SubscriptionValue = SubscriptionValue(selected)
13 | public val eventSelection: SubscriptionEvent = SubscriptionEvent()
14 | }
15 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/subscription/SubscriptionEvent.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.subscription
2 |
3 | public class SubscriptionEvent : Subscription() {
4 |
5 | public fun send(event: T) {
6 | notifyAll(event)
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/subscription/SubscriptionMerge.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.subscription
2 |
3 | public fun mergeSubscription(
4 | initialValue: T,
5 | subscriptionA: SubscriptionValue,
6 | subscriptionB: SubscriptionValue,
7 | merge: (R, S) -> T
8 | ): SubscriptionValue {
9 | return SubscriptionValue(initialValue).apply {
10 | subscriptionA.subscribePast { value = merge(it, subscriptionB.value) }
11 | subscriptionB.subscribePast { value = merge(subscriptionA.value, it) }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/subscription/SubscriptionValue.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.subscription
2 |
3 | import com.algolia.instantsearch.core.subscription.internal.SubscriptionOnce
4 | import kotlin.properties.Delegates
5 |
6 | public class SubscriptionValue(initialValue: T) : Subscription() {
7 |
8 | public var value: T by Delegates.observable(initialValue) { _, _, newValue ->
9 | notifyAll(newValue)
10 | }
11 |
12 | public fun subscribePast(subscription: (T) -> Unit) {
13 | subscription(value)
14 | subscriptions += subscription
15 | }
16 |
17 | public fun subscribePastOnce(skipNull: Boolean = true, subscription: (T) -> Unit) {
18 | subscriptions += SubscriptionOnce(this, skipNull, subscription)
19 | }
20 |
21 | public fun notifySubscriptions() {
22 | notifyAll(value)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/subscription/internal/SubscriptionOnce.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.subscription.internal
2 |
3 | import com.algolia.instantsearch.core.subscription.Subscription
4 |
5 | internal class SubscriptionOnce(
6 | private val subscription: Subscription,
7 | private val skipNull: Boolean,
8 | private val call: (T) -> Unit
9 | ) : (T) -> Unit {
10 |
11 | override fun invoke(value: T) {
12 | if (value == null && skipNull) return
13 | execute(value)
14 | }
15 |
16 | private fun execute(value: T) {
17 | subscription.unsubscribe(this)
18 | call(value)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/tree/Node.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.tree
2 |
3 | public data class Node(
4 | val content: T,
5 | val children: MutableList> = mutableListOf()
6 | ) {
7 |
8 | public var isSelected: Boolean = false
9 |
10 | public constructor(
11 | content: T,
12 | isSelected: Boolean,
13 | children: MutableList> = mutableListOf()
14 | ) : this(content, children) {
15 | this.isSelected = isSelected
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/tree/Tree.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.tree
2 |
3 | public data class Tree(
4 | val children: MutableList> = mutableListOf()
5 | )
6 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/tree/TreeConnection.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.tree
2 |
3 | import com.algolia.instantsearch.core.connection.Connection
4 | import com.algolia.instantsearch.core.tree.internal.TreeConnectionView
5 |
6 | public fun TreeViewModel.connectView(
7 | view: TreeView,
8 | presenter: TreePresenter
9 | ): Connection {
10 | return TreeConnectionView(this, view, presenter)
11 | }
12 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/tree/TreePresenter.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.tree
2 |
3 | import com.algolia.instantsearch.core.Presenter
4 |
5 | public typealias TreePresenter = Presenter, O>
6 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/tree/TreeView.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.tree
2 |
3 | import com.algolia.instantsearch.core.Callback
4 |
5 | public interface TreeView {
6 |
7 | public fun setTree(tree: V)
8 |
9 | public var onSelectionChanged: Callback?
10 | }
11 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonMain/kotlin/com/algolia/instantsearch/core/tree/TreeViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.tree
2 |
3 | import com.algolia.instantsearch.core.subscription.SubscriptionValue
4 |
5 | public abstract class TreeViewModel(
6 | tree: Tree = Tree()
7 | ) {
8 |
9 | public val tree: SubscriptionValue> = SubscriptionValue(tree)
10 |
11 | /**
12 | * Computes selected levels given a hierarchical key.
13 | *
14 | * @param key a hierarchy level
15 | */
16 | public abstract fun computeSelections(key: K)
17 | }
18 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonTest/kotlin/map/TestMapViewModel.kt:
--------------------------------------------------------------------------------
1 | package map
2 |
3 | import com.algolia.instantsearch.core.map.MapViewModel
4 | import shouldEqual
5 | import kotlin.test.Test
6 |
7 | class TestMapViewModel {
8 |
9 | private val id = "id"
10 | private val map = mapOf(id to "value")
11 |
12 | @Test
13 | fun removeShouldCallOnMapComputed() {
14 | val viewModel = MapViewModel(map)
15 |
16 | viewModel.event.subscribe { viewModel.map.value = it }
17 | viewModel.remove(id)
18 | viewModel.map.value shouldEqual mapOf()
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonTest/kotlin/number/TestNumberPresenter.kt:
--------------------------------------------------------------------------------
1 | package number
2 |
3 | import com.algolia.instantsearch.core.number.DefaultNumberPresenter
4 | import shouldEqual
5 | import kotlin.test.Test
6 |
7 | class TestNumberPresenter {
8 |
9 | @Test
10 | fun isNull() {
11 | DefaultNumberPresenter(null) shouldEqual "-"
12 | }
13 |
14 | @Test
15 | fun other() {
16 | DefaultNumberPresenter(0) shouldEqual "0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonTest/kotlin/relevantsort/TestRelevantSortViewModel.kt:
--------------------------------------------------------------------------------
1 | package relevantsort
2 |
3 | import com.algolia.instantsearch.core.relevantsort.RelevantSortPriority
4 | import com.algolia.instantsearch.core.relevantsort.RelevantSortViewModel
5 | import org.junit.Test
6 | import shouldEqual
7 |
8 | class TestRelevantSortViewModel {
9 |
10 | @Test
11 | fun testToggle() {
12 | val viewModel = RelevantSortViewModel(RelevantSortPriority.HitsCount)
13 | viewModel.toggle()
14 | viewModel.priority.value shouldEqual RelevantSortPriority.Relevancy
15 | viewModel.toggle()
16 | viewModel.priority.value shouldEqual RelevantSortPriority.HitsCount
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonTest/kotlin/searchbox/TestSearchBoxViewModel.kt:
--------------------------------------------------------------------------------
1 | package searchbox
2 |
3 | import com.algolia.instantsearch.core.searchbox.SearchBoxViewModel
4 | import shouldEqual
5 | import kotlin.test.Test
6 |
7 | class TestSearchBoxViewModel {
8 |
9 | @Test
10 | fun setQueryShouldCallSubscription() {
11 | val viewModel = SearchBoxViewModel()
12 | var expected: String? = null
13 | val value = "hello"
14 |
15 | viewModel.query.subscribe { expected = it }
16 | viewModel.query.value = value
17 | expected shouldEqual value
18 | }
19 |
20 | @Test
21 | fun sendEventShouldCallSubscription() {
22 | val viewModel = SearchBoxViewModel()
23 | var expected: String? = null
24 | val value = "hello"
25 |
26 | viewModel.eventSubmit.subscribe { expected = it }
27 | viewModel.eventSubmit.send(value)
28 | expected shouldEqual value
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonTest/kotlin/selectable/TestSelectableItemViewModel.kt:
--------------------------------------------------------------------------------
1 | package selectable
2 |
3 | import com.algolia.instantsearch.core.selectable.SelectableItemViewModel
4 | import shouldBeFalse
5 | import shouldBeTrue
6 | import kotlin.test.Test
7 |
8 | class TestSelectableItemViewModel {
9 |
10 | private val valueA = "valueA"
11 |
12 | @Test
13 | fun selection() {
14 | SelectableItemViewModel(valueA).apply {
15 | eventSelection.subscribe { isSelected.value = it }
16 | isSelected.value.shouldBeFalse()
17 | eventSelection.send(true)
18 | isSelected.value.shouldBeTrue()
19 | eventSelection.send(false)
20 | isSelected.value.shouldBeFalse()
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonTest/kotlin/selectable/map/TestSelectableMapViewModel.kt:
--------------------------------------------------------------------------------
1 | package selectable.map
2 |
3 | import com.algolia.instantsearch.core.selectable.map.SelectableMapViewModel
4 | import shouldBeNull
5 | import shouldEqual
6 | import kotlin.test.Test
7 |
8 | class TestSelectableMapViewModel {
9 |
10 | private val items = mapOf(0 to "A", 1 to "B")
11 |
12 | @Test
13 | fun noSelectedByDefault() {
14 | SelectableMapViewModel(items).apply {
15 | selected.value.shouldBeNull()
16 | }
17 | }
18 |
19 | @Test
20 | fun computeSelected() {
21 | SelectableMapViewModel(items).apply {
22 | eventSelection.subscribe { selected.value = it }
23 | eventSelection.send(1)
24 | selected.value shouldEqual 1
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/instantsearch-core/src/commonTest/kotlin/subscription/TestSubscriptionEvent.kt:
--------------------------------------------------------------------------------
1 | package subscription
2 |
3 | import com.algolia.instantsearch.core.subscription.SubscriptionEvent
4 | import shouldEqual
5 | import kotlin.test.Test
6 |
7 | class TestSubscriptionEvent {
8 |
9 | private val value = "value"
10 |
11 | @Test
12 | fun sendValueShouldCallSubscription() {
13 | var expected: String? = null
14 | val item: SubscriptionEvent = SubscriptionEvent()
15 |
16 | item.subscribe { expected = it }
17 | item.send(value)
18 | expected shouldEqual value
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/instantsearch-core/src/jvmMain/kotlin/com/algolia/instantsearch/core/internal/Collections.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.core.internal
2 |
3 | import java.util.concurrent.CopyOnWriteArraySet
4 |
5 | internal actual fun frozenCopyOnWriteSet(collection: Collection?): MutableSet {
6 | return if (collection == null) {
7 | CopyOnWriteArraySet()
8 | } else {
9 | CopyOnWriteArraySet(collection)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/instantsearch-insights/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=InstantSearch Insights
2 | POM_ARTIFACT_ID=instantsearch-insights
3 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/androidMain/kotlin/com/algolia/instantsearch/insights/internal/extension/Platform.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.extension
2 |
3 | import android.content.Context
4 | import android.content.SharedPreferences
5 | import com.algolia.search.model.IndexName
6 |
7 | internal var SharedPreferences.events: Set by SharedPreferencesDelegate.StringSet(setOf())
8 |
9 | internal fun Context.sharedPreferences(name: String, mode: Int = Context.MODE_PRIVATE): SharedPreferences {
10 | return getSharedPreferences(name, mode)
11 | }
12 |
13 | internal fun Context.insightsSharedPreferences(indexName: IndexName) = sharedPreferences("Algolia Insights-$indexName")
14 |
15 | /**
16 | * Get Insights Settings Shared Preferences.
17 | */
18 | internal fun Context.insightsSettingsPreferences(): SharedPreferences {
19 | return getSharedPreferences("InsightsEvents", Context.MODE_PRIVATE)
20 | }
21 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/androidMain/kotlin/com/algolia/instantsearch/insights/internal/extension/Time.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.extension
2 |
3 | internal actual val currentTimeMillis: Long
4 | get() = System.currentTimeMillis()
5 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/androidMain/kotlin/com/algolia/instantsearch/insights/internal/extension/UUID.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.extension
2 |
3 | import java.util.UUID
4 |
5 | internal actual fun randomUUID() = UUID.randomUUID().toString()
6 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/androidMain/kotlin/com/algolia/instantsearch/insights/internal/logging/InsightsLogger.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.logging
2 |
3 | import android.util.Log
4 | import com.algolia.search.model.IndexName
5 |
6 | internal actual object InsightsLogger {
7 |
8 | private const val TAG = "Algolia Insights"
9 | actual var enabled: MutableMap = mutableMapOf()
10 |
11 | actual fun log(indexName: IndexName, message: String) {
12 | if (enabled[indexName] == true) {
13 | Log.d(TAG, "Index=$indexName: $message")
14 | }
15 | }
16 |
17 | actual fun log(message: String) {
18 | Log.d(TAG, message)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/androidUnitTest/kotlin/com/algolia/instantsearch/insights/MockDistantRepository.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights
2 |
3 | import com.algolia.instantsearch.insights.internal.data.distant.InsightsDistantRepository
4 | import com.algolia.instantsearch.insights.internal.event.EventResponse
5 | import com.algolia.search.model.APIKey
6 | import com.algolia.search.model.ApplicationID
7 | import com.algolia.search.model.insights.InsightsEvent
8 |
9 | internal class MockDistantRepository : InsightsDistantRepository {
10 |
11 | var code = 200
12 |
13 | override suspend fun send(event: InsightsEvent): EventResponse {
14 | return EventResponse(event, code)
15 | }
16 |
17 | override val apiKey = APIKey("apiKey")
18 | override val applicationID = ApplicationID("applicationID")
19 | }
20 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/androidUnitTest/kotlin/com/algolia/instantsearch/insights/MockLocalRepository.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights
2 |
3 | import com.algolia.instantsearch.insights.internal.data.local.InsightsLocalRepository
4 | import com.algolia.search.model.insights.InsightsEvent
5 |
6 | internal class MockLocalRepository(
7 | private val events: MutableList,
8 | ) : InsightsLocalRepository {
9 |
10 | override fun append(event: InsightsEvent) {
11 | events.add(event)
12 | }
13 |
14 | override fun overwrite(events: List) {
15 | clear()
16 | this.events += events
17 | }
18 |
19 | override fun read(): List {
20 | return events
21 | }
22 |
23 | override fun count(): Int {
24 | return events.size
25 | }
26 |
27 | override fun clear() {
28 | events.clear()
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/androidUnitTest/kotlin/com/algolia/instantsearch/insights/util/WorkerManager.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.util
2 |
3 | import android.app.Application
4 | import androidx.test.core.app.ApplicationProvider
5 | import androidx.work.Configuration
6 | import androidx.work.testing.SynchronousExecutor
7 | import androidx.work.testing.WorkManagerTestInitHelper
8 |
9 | internal fun setupWorkManager() {
10 | val context = ApplicationProvider.getApplicationContext()
11 | val config = Configuration.Builder()
12 | .setExecutor(SynchronousExecutor())
13 | .build()
14 |
15 | WorkManagerTestInitHelper.initializeTestWorkManager(context, config)
16 | }
17 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/commonMain/kotlin/com/algolia/instantsearch/insights/exception/InsightsException.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.exception
2 |
3 | /**
4 | * InstantSearch Insights exceptions.
5 | */
6 | public sealed class InsightsException(override val message: String? = null) : Exception(message) {
7 |
8 | /**
9 | * Will be thrown when you try to access an index through the Insights.shared
10 | * method without having registered the index through the Insights.register method first.
11 | */
12 | public class IndexNotRegistered : InsightsException("You need to call Insights.register before Insights.shared")
13 |
14 | /**
15 | * Will be thrown when you call Insights.viewed without Insights.userToken first.
16 | */
17 | public class NoUserToken : InsightsException("You need to set Insights.userToken first.")
18 | // TODO: Remove exception once default userToken
19 | }
20 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/commonMain/kotlin/com/algolia/instantsearch/insights/internal/InsightsMap.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal
2 |
3 | import com.algolia.search.model.IndexName
4 |
5 | /**
6 | * Map storing all registered Insights instances.
7 | */
8 | internal object InsightsMap : MutableMap by mutableMapOf()
9 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/commonMain/kotlin/com/algolia/instantsearch/insights/internal/cache/InsightsCache.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.cache
2 |
3 | import com.algolia.search.model.insights.InsightsEvent
4 |
5 | internal interface InsightsCache {
6 |
7 | fun save(event: InsightsEvent)
8 |
9 | fun size(): Int
10 | }
11 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/commonMain/kotlin/com/algolia/instantsearch/insights/internal/cache/InsightsEventCache.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.cache
2 |
3 | import com.algolia.instantsearch.insights.internal.data.local.InsightsLocalRepository
4 | import com.algolia.search.model.insights.InsightsEvent
5 |
6 | internal class InsightsEventCache(
7 | private val localRepository: InsightsLocalRepository,
8 | ) : InsightsCache {
9 |
10 | override fun save(event: InsightsEvent) {
11 | localRepository.append(event)
12 | }
13 |
14 | override fun size(): Int {
15 | return localRepository.count()
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/commonMain/kotlin/com/algolia/instantsearch/insights/internal/data/distant/InsightsDistantRepository.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.data.distant
2 |
3 | import com.algolia.instantsearch.insights.internal.event.EventResponse
4 | import com.algolia.search.configuration.Credentials
5 | import com.algolia.search.model.insights.InsightsEvent
6 |
7 | internal interface InsightsDistantRepository : Credentials {
8 |
9 | suspend fun send(event: InsightsEvent): EventResponse
10 | }
11 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/commonMain/kotlin/com/algolia/instantsearch/insights/internal/data/local/InsightsLocalRepository.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.data.local
2 |
3 | import com.algolia.search.model.insights.InsightsEvent
4 |
5 | internal interface InsightsLocalRepository {
6 |
7 | fun append(event: InsightsEvent)
8 |
9 | fun overwrite(events: List)
10 |
11 | fun read(): List
12 |
13 | fun count(): Int
14 |
15 | fun clear()
16 | }
17 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/commonMain/kotlin/com/algolia/instantsearch/insights/internal/data/local/mapper/InsightsEventDOMapper.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.data.local.mapper
2 |
3 | import com.algolia.instantsearch.insights.internal.data.local.model.InsightsEventDO
4 | import com.algolia.instantsearch.insights.internal.extension.JsonNonStrict
5 |
6 | internal object InsightsEventDOMapper : Mapper {
7 |
8 | override fun map(input: InsightsEventDO): String {
9 | return JsonNonStrict.encodeToString(InsightsEventDO.serializer(), input)
10 | }
11 |
12 | override fun unmap(input: String): InsightsEventDO {
13 | return JsonNonStrict.decodeFromString(InsightsEventDO.serializer(), input)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/commonMain/kotlin/com/algolia/instantsearch/insights/internal/data/local/mapper/Mapper.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.data.local.mapper
2 |
3 | internal interface Mapper {
4 |
5 | fun map(input: T): R
6 |
7 | fun unmap(input: R): T
8 | }
9 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/commonMain/kotlin/com/algolia/instantsearch/insights/internal/data/settings/InsightsSettings.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.data.settings
2 |
3 | internal interface InsightsSettings {
4 |
5 | var workId: String?
6 |
7 | var userToken: String?
8 | }
9 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/commonMain/kotlin/com/algolia/instantsearch/insights/internal/event/EventResponse.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.event
2 |
3 | import com.algolia.search.model.insights.InsightsEvent
4 |
5 | internal data class EventResponse(
6 | val event: InsightsEvent,
7 | val code: Int,
8 | )
9 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/commonMain/kotlin/com/algolia/instantsearch/insights/internal/extension/InsightsEvent.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.extension
2 |
3 | import com.algolia.search.model.insights.InsightsEvent
4 |
5 | internal fun InsightsEvent.copy(timestamp: Long?): InsightsEvent {
6 | return when (this) {
7 | is InsightsEvent.View -> copy(timestamp = timestamp)
8 | is InsightsEvent.Click -> copy(timestamp = timestamp)
9 | is InsightsEvent.Conversion -> copy(timestamp = timestamp)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/commonMain/kotlin/com/algolia/instantsearch/insights/internal/extension/Json.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.extension
2 |
3 | import kotlinx.serialization.json.Json
4 | import kotlinx.serialization.json.JsonElement
5 | import kotlinx.serialization.json.decodeFromJsonElement
6 |
7 | internal val JsonNonStrict = Json {
8 | ignoreUnknownKeys = true
9 | isLenient = true
10 | allowSpecialFloatingPointValues = true
11 | encodeDefaults = true
12 | }
13 |
14 | internal fun JsonElement?.deserializeString(): String {
15 | return if (this != null) JsonNonStrict.decodeFromJsonElement(this) else error("null json element")
16 | }
17 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/commonMain/kotlin/com/algolia/instantsearch/insights/internal/extension/Map.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.extension
2 |
3 | internal fun MutableMap.put(entry: Pair) {
4 | put(entry.first, entry.second)
5 | }
6 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/commonMain/kotlin/com/algolia/instantsearch/insights/internal/extension/Time.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.extension
2 |
3 | internal expect val currentTimeMillis: Long
4 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/commonMain/kotlin/com/algolia/instantsearch/insights/internal/extension/UUID.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.extension
2 |
3 | internal expect fun randomUUID(): String
4 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/commonMain/kotlin/com/algolia/instantsearch/insights/internal/logging/InsightsLogger.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.logging
2 |
3 | import com.algolia.search.model.IndexName
4 |
5 | internal expect object InsightsLogger {
6 |
7 | var enabled: MutableMap
8 |
9 | fun log(indexName: IndexName, message: String)
10 |
11 | fun log(message: String)
12 | }
13 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/commonMain/kotlin/com/algolia/instantsearch/insights/internal/uploader/InsightsUploader.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.uploader
2 |
3 | import com.algolia.instantsearch.insights.internal.event.EventResponse
4 | import com.algolia.search.configuration.Credentials
5 |
6 | internal interface InsightsUploader : Credentials {
7 |
8 | suspend fun uploadAll(): List
9 | }
10 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/commonMain/kotlin/com/algolia/instantsearch/insights/internal/worker/InsightsManager.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.worker
2 |
3 | internal interface InsightsManager {
4 |
5 | fun setInterval(intervalInMinutes: Long) = Unit
6 |
7 | fun startPeriodicUpload()
8 |
9 | fun startOneTimeUpload()
10 | }
11 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/jvmMain/kotlin/com/algolia/instantsearch/insights/internal/extension/Time.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.extension
2 |
3 | internal actual val currentTimeMillis: Long
4 | get() = System.currentTimeMillis()
5 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/jvmMain/kotlin/com/algolia/instantsearch/insights/internal/extension/UUID.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.extension
2 |
3 | import java.util.UUID
4 |
5 | internal actual fun randomUUID() = UUID.randomUUID().toString()
6 |
--------------------------------------------------------------------------------
/instantsearch-insights/src/jvmMain/kotlin/com/algolia/instantsearch/insights/internal/logging/InsightsLogger.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.insights.internal.logging
2 |
3 | import com.algolia.instantsearch.insights.Insights
4 | import com.algolia.search.model.IndexName
5 | import org.slf4j.LoggerFactory
6 |
7 | internal actual object InsightsLogger {
8 |
9 | private val logger = LoggerFactory.getLogger(Insights::class.java)
10 | actual var enabled: MutableMap = mutableMapOf()
11 |
12 | actual fun log(indexName: IndexName, message: String) {
13 | if (enabled[indexName] == true) {
14 | logger.debug("Index=$indexName: $message")
15 | }
16 | }
17 |
18 | actual fun log(message: String) {
19 | logger.debug(message)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/instantsearch-utils/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=InstantSearch Utilities
2 | POM_ARTIFACT_ID=instantsearch-utils
3 |
--------------------------------------------------------------------------------
/instantsearch-utils/src/androidMain/kotlin/com/algolia/instantsearch/platform/Env.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.platform
2 |
3 | import android.os.Build
4 |
5 | internal actual fun osVersion(): String = "Android (${Build.VERSION.SDK_INT})"
6 |
--------------------------------------------------------------------------------
/instantsearch-utils/src/commonJvm/kotlin/com/algolia/instantsearch/encode/Gzip.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.encode
2 |
3 | import java.io.ByteArrayOutputStream
4 | import java.util.zip.GZIPOutputStream
5 |
6 | /**
7 | * Applies GZIP compression to a [ByteArray]
8 | */
9 | public actual fun ByteArray.gzip(): ByteArray {
10 | return ByteArrayOutputStream().use { bos ->
11 | GZIPOutputStream(bos).buffered().use { gzip -> gzip.write(this) }
12 | bos.toByteArray()
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/instantsearch-utils/src/commonJvm/kotlin/com/algolia/instantsearch/util/uuid.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.util
2 |
3 | import java.util.*
4 |
5 | public actual fun randomUuid(): String {
6 | return UUID.randomUUID().toString()
7 | }
8 |
--------------------------------------------------------------------------------
/instantsearch-utils/src/commonMain/kotlin/com/algolia/instantsearch/encode/Gzip.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.encode
2 |
3 | import com.algolia.instantsearch.InternalInstantSearch
4 |
5 | /**
6 | * Applies GZIP compression to a [ByteArray]
7 | */
8 | @InternalInstantSearch
9 | public expect fun ByteArray.gzip(): ByteArray
10 |
--------------------------------------------------------------------------------
/instantsearch-utils/src/commonMain/kotlin/com/algolia/instantsearch/platform/Env.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.platform
2 |
3 | /** Get current os version */
4 | internal expect fun osVersion(): String
5 |
--------------------------------------------------------------------------------
/instantsearch-utils/src/commonMain/kotlin/com/algolia/instantsearch/util/agent.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.util
2 |
3 | import com.algolia.instantsearch.BuildConfig
4 | import com.algolia.instantsearch.InternalInstantSearch
5 | import com.algolia.instantsearch.platform.osVersion
6 |
7 | @InternalInstantSearch
8 | public fun algoliaAgent(libName: String): String = "$libName (${BuildConfig.version}); ${osVersion()}"
9 |
--------------------------------------------------------------------------------
/instantsearch-utils/src/commonMain/kotlin/com/algolia/instantsearch/util/uuid.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.util
2 |
3 | public expect fun randomUuid(): String
4 |
--------------------------------------------------------------------------------
/instantsearch-utils/src/commonMain/templates/BuildConfig.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch
2 |
3 | @InternalInstantSearch
4 | public object BuildConfig {
5 | public const val version: String = "$projectVersion"
6 | }
7 |
--------------------------------------------------------------------------------
/instantsearch-utils/src/jvmMain/kotlin/com/algolia/instantsearch/platform/Env.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.platform
2 |
3 | /** Get current os version */
4 | internal actual fun osVersion(): String = "JVM (${System.getProperty("java.version")})"
5 |
--------------------------------------------------------------------------------
/instantsearch/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=InstantSearch
2 | POM_ARTIFACT_ID=instantsearch
3 |
--------------------------------------------------------------------------------
/instantsearch/src/androidMain/kotlin/com/algolia/instantsearch/android/ViewGroup.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.android
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 |
7 | public fun ViewGroup.inflate(layoutId: Int, attachToRoot: Boolean = false): View {
8 | return LayoutInflater.from(context).inflate(layoutId, this, attachToRoot)
9 | }
10 |
11 | public inline fun ViewGroup.inflate(layoutId: Int, attachToRoot: Boolean = false): T {
12 | return inflate(layoutId, attachToRoot) as T
13 | }
14 |
--------------------------------------------------------------------------------
/instantsearch/src/androidMain/kotlin/com/algolia/instantsearch/android/filter/clear/DefaultFilterClearView.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.android.filter.clear
2 |
3 | import android.view.View
4 | import com.algolia.instantsearch.core.Callback
5 | import com.algolia.instantsearch.filter.clear.FilterClearView
6 |
7 | public class DefaultFilterClearView(
8 | public val view: View,
9 | ) : FilterClearView {
10 |
11 | override var onClear: Callback? = null
12 |
13 | init {
14 | view.setOnClickListener { onClear?.invoke(Unit) }
15 | }
16 | }
17 |
18 | @Deprecated(message = "use DefaultFilterClearView instead", replaceWith = ReplaceWith("DefaultFilterClearView"))
19 | public typealias FilterClearViewImpl = DefaultFilterClearView
20 |
--------------------------------------------------------------------------------
/instantsearch/src/androidMain/kotlin/com/algolia/instantsearch/android/filter/facet/FacetListViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.android.filter.facet
2 |
3 | import android.view.View
4 | import android.view.ViewGroup
5 | import androidx.recyclerview.widget.RecyclerView
6 | import com.algolia.search.model.search.Facet
7 |
8 | public abstract class FacetListViewHolder(public val view: View) : RecyclerView.ViewHolder(view) {
9 |
10 | public abstract fun bind(facet: Facet, selected: Boolean, onClickListener: View.OnClickListener)
11 |
12 | public interface Factory {
13 |
14 | public fun createViewHolder(parent: ViewGroup): FacetListViewHolder
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/instantsearch/src/androidMain/kotlin/com/algolia/instantsearch/android/filter/facet/dynamic/DynamicFacetModel.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.android.filter.facet.dynamic
2 |
3 | import com.algolia.search.model.Attribute
4 | import com.algolia.search.model.search.Facet
5 |
6 | /**
7 | * Facet view model to be rendered.
8 | */
9 | public sealed class DynamicFacetModel {
10 |
11 | /**
12 | * Attribute view to be rendered.
13 | */
14 | public data class Header(val attribute: Attribute) : DynamicFacetModel()
15 |
16 | /**
17 | * Facet value view to be rendered.
18 | */
19 | public data class Item(val attribute: Attribute, val facet: Facet, val selected: Boolean) : DynamicFacetModel()
20 | }
21 |
--------------------------------------------------------------------------------
/instantsearch/src/androidMain/kotlin/com/algolia/instantsearch/android/hits/HitsArrayAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.android.hits
2 |
3 | import android.widget.ArrayAdapter
4 | import com.algolia.instantsearch.core.hits.HitsView
5 |
6 | public class HitsArrayAdapter(
7 | public val adapter: ArrayAdapter,
8 | ) : HitsView {
9 |
10 | override fun setHits(hits: List) {
11 | adapter.clear()
12 | adapter.addAll(hits)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/instantsearch/src/androidMain/kotlin/com/algolia/instantsearch/android/hits/HitsViewConnection.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.android.hits
2 |
3 | import android.os.Build
4 | import android.widget.AutoCompleteTextView
5 | import androidx.annotation.RequiresApi
6 | import com.algolia.instantsearch.android.hits.internal.HitsArrayAdapterConnection
7 | import com.algolia.instantsearch.core.Presenter
8 | import com.algolia.instantsearch.core.connection.Connection
9 | import com.algolia.instantsearch.core.searcher.Searcher
10 |
11 | @RequiresApi(Build.VERSION_CODES.Q)
12 | public fun Searcher.connectHitsArrayAdapter(
13 | adapter: HitsArrayAdapter,
14 | view: AutoCompleteTextView,
15 | presenter: Presenter>,
16 | ): Connection {
17 | return HitsArrayAdapterConnection(this, adapter, view, presenter)
18 | }
19 |
--------------------------------------------------------------------------------
/instantsearch/src/androidMain/kotlin/com/algolia/instantsearch/android/internal/Telemetry.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.android.internal
2 |
3 | import android.util.Base64
4 | import com.algolia.instantsearch.telemetry.Schema
5 | import com.algolia.instantsearch.telemetry.toByteArray
6 |
7 | /**
8 | * Encodes [Schema] byte array representation as base64 string.
9 | */
10 | internal fun Schema.encodeBase64(): String = Base64.encodeToString(toByteArray(), Base64.URL_SAFE)
11 |
--------------------------------------------------------------------------------
/instantsearch/src/androidMain/kotlin/com/algolia/instantsearch/android/item/StatsTextView.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.android.item
2 |
3 | import android.widget.TextView
4 | import com.algolia.instantsearch.stats.StatsView
5 |
6 | public class StatsTextView(
7 | public val view: TextView,
8 | ) : StatsView {
9 |
10 | override fun setText(text: String) {
11 | view.text = text
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/instantsearch/src/androidMain/kotlin/com/algolia/instantsearch/android/item/StatsTextViewSpanned.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.android.item
2 |
3 | import android.text.SpannedString
4 | import android.widget.TextView
5 | import com.algolia.instantsearch.stats.StatsView
6 |
7 | public class StatsTextViewSpanned(
8 | public val view: TextView,
9 | ) : StatsView {
10 |
11 | override fun setText(text: SpannedString) {
12 | view.text = text
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/instantsearch/src/androidMain/kotlin/com/algolia/instantsearch/android/list/Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.android.list
2 |
3 | import androidx.recyclerview.widget.RecyclerView
4 |
5 | public fun RecyclerView.autoScrollToStart(adapter: RecyclerView.Adapter<*>) {
6 | adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
7 | override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
8 | if (positionStart == 0) {
9 | scrollToPosition(0)
10 | }
11 | }
12 | })
13 | }
14 |
--------------------------------------------------------------------------------
/instantsearch/src/androidMain/kotlin/com/algolia/instantsearch/android/stats/StatsTextView.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.android.stats
2 |
3 | import com.algolia.instantsearch.android.item.StatsTextView
4 |
5 | public typealias StatsTextView = StatsTextView
6 |
--------------------------------------------------------------------------------
/instantsearch/src/androidMain/kotlin/com/algolia/instantsearch/android/stats/StatsTextViewSpanned.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.android.stats
2 |
3 | import com.algolia.instantsearch.android.item.StatsTextViewSpanned
4 |
5 | public typealias StatsTextViewSpanned = StatsTextViewSpanned
6 |
--------------------------------------------------------------------------------
/instantsearch/src/androidMain/kotlin/com/algolia/instantsearch/extension/Console.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.extension
2 |
3 | import android.util.Log
4 | import com.algolia.instantsearch.InternalInstantSearch
5 |
6 | @InternalInstantSearch
7 | public actual object Console {
8 |
9 | private const val Tag = "InstantSearch"
10 |
11 | public actual fun debug(message: String, throwable: Throwable?) {
12 | Log.d(Tag, message, throwable)
13 | }
14 |
15 | public actual fun info(message: String, throwable: Throwable?) {
16 | Log.i(Tag, message, throwable)
17 | }
18 |
19 | public actual fun warn(message: String, throwable: Throwable?) {
20 | Log.w(Tag, message, throwable)
21 | }
22 |
23 | public actual fun error(message: String, throwable: Throwable?) {
24 | Log.e(Tag, message, throwable)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/instantsearch/src/androidMain/kotlin/com/algolia/instantsearch/searcher/internal/Actual.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.searcher.internal
2 |
3 | import kotlinx.coroutines.CoroutineDispatcher
4 | import kotlinx.coroutines.Dispatchers
5 |
6 | internal actual val defaultDispatcher: CoroutineDispatcher = Dispatchers.IO
7 |
--------------------------------------------------------------------------------
/instantsearch/src/androidUnitTest/kotlin/MainCoroutineRule.kt:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.Dispatchers
2 | import kotlinx.coroutines.test.TestDispatcher
3 | import kotlinx.coroutines.test.UnconfinedTestDispatcher
4 | import kotlinx.coroutines.test.resetMain
5 | import kotlinx.coroutines.test.setMain
6 | import org.junit.rules.TestWatcher
7 | import org.junit.runner.Description
8 |
9 | class MainCoroutineRule(
10 | val testDispatcher: TestDispatcher = UnconfinedTestDispatcher(),
11 | ) : TestWatcher() {
12 | override fun starting(description: Description) {
13 | Dispatchers.setMain(testDispatcher)
14 | }
15 |
16 | override fun finished(description: Description) {
17 | Dispatchers.resetMain()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/instantsearch/src/androidUnitTest/kotlin/instrumented/InstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package instrumented
2 |
3 | import android.app.Application
4 | import androidx.test.core.app.ApplicationProvider
5 | import com.algolia.instantsearch.android.R
6 |
7 | val applicationContext: Application
8 | get() = ApplicationProvider.getApplicationContext().apply {
9 | setTheme(R.style.Theme_MaterialComponents)
10 | }
11 |
--------------------------------------------------------------------------------
/instantsearch/src/androidUnitTest/kotlin/instrumented/stats/TestStatsTextView.kt:
--------------------------------------------------------------------------------
1 | package instrumented.stats
2 |
3 | import android.os.Build
4 | import android.widget.TextView
5 | import androidx.test.ext.junit.runners.AndroidJUnit4
6 | import androidx.test.filters.SmallTest
7 | import com.algolia.instantsearch.android.stats.StatsTextView
8 | import instrumented.applicationContext
9 | import org.junit.Test
10 | import org.junit.runner.RunWith
11 | import org.robolectric.annotation.Config
12 | import shouldEqual
13 |
14 | @SmallTest
15 | @Config(sdk = [Build.VERSION_CODES.P])
16 | @RunWith(AndroidJUnit4::class)
17 | class TestStatsTextView {
18 |
19 | private fun view() = StatsTextView(TextView(applicationContext))
20 |
21 | private val text = "text"
22 |
23 | @Test
24 | fun setItemShouldSetText() {
25 | val view = view()
26 |
27 | view.setText(text)
28 | view.view.text shouldEqual text
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/instantsearch/src/commonMain/kotlin/com/algolia/instantsearch/attribute/AttributeMatchAndReplace.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.attribute
2 |
3 | import com.algolia.search.model.Attribute
4 |
5 | public class AttributeMatchAndReplace(
6 | private val match: Attribute,
7 | private val replacement: String,
8 | ) : AttributePresenter {
9 |
10 | override fun invoke(attribute: Attribute): String {
11 | return if (attribute == match) replacement else attribute.raw
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/instantsearch/src/commonMain/kotlin/com/algolia/instantsearch/attribute/AttributePresenter.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.attribute
2 |
3 | import com.algolia.instantsearch.core.Presenter
4 | import com.algolia.search.model.Attribute
5 |
6 | public typealias AttributePresenter = Presenter
7 |
--------------------------------------------------------------------------------
/instantsearch/src/commonMain/kotlin/com/algolia/instantsearch/attribute/DefaultAttributePresenter.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.attribute
2 |
3 | import com.algolia.search.model.Attribute
4 |
5 | public class DefaultAttributePresenter(
6 | private val transform: (Attribute) -> String = { attribute -> attribute.raw },
7 | ) : AttributePresenter {
8 |
9 | override fun invoke(attribute: Attribute): String {
10 | return transform(attribute)
11 | }
12 | }
13 |
14 | @Deprecated(message = "use DefaultAttributePresenter instead", replaceWith = ReplaceWith("DefaultAttributePresenter"))
15 | public typealias AttributePresenterImpl = DefaultAttributePresenter
16 |
--------------------------------------------------------------------------------
/instantsearch/src/commonMain/kotlin/com/algolia/instantsearch/customdata/QueryRuleCustomDataConnection.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("DEPRECATION")
2 |
3 | package com.algolia.instantsearch.customdata
4 |
5 | import com.algolia.instantsearch.core.connection.Connection
6 | import com.algolia.instantsearch.customdata.internal.QueryRuleCustomDataConnectionSearcherForHits
7 | import com.algolia.instantsearch.searcher.SearcherForHits
8 |
9 | /**
10 | * Create a connection between a view model and a searcher.
11 | *
12 | * @param searcher searcher to connect
13 | */
14 | public fun QueryRuleCustomDataViewModel.connectSearcher(searcher: SearcherForHits<*>): Connection {
15 | return QueryRuleCustomDataConnectionSearcherForHits(this, searcher)
16 | }
17 |
--------------------------------------------------------------------------------
/instantsearch/src/commonMain/kotlin/com/algolia/instantsearch/customdata/QueryRuleCustomDataPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.customdata
2 |
3 | /**
4 | * Defines the way we want to interact with a model.
5 | */
6 | public typealias QueryRuleCustomDataPresenter = (T?) -> Unit
7 |
--------------------------------------------------------------------------------
/instantsearch/src/commonMain/kotlin/com/algolia/instantsearch/extension/Console.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.extension
2 |
3 | import com.algolia.instantsearch.InternalInstantSearch
4 |
5 | @InternalInstantSearch
6 | public expect object Console {
7 |
8 | public fun debug(message: String, throwable: Throwable? = null)
9 |
10 | public fun info(message: String, throwable: Throwable? = null)
11 |
12 | public fun warn(message: String, throwable: Throwable? = null)
13 |
14 | public fun error(message: String, throwable: Throwable? = null)
15 | }
16 |
--------------------------------------------------------------------------------
/instantsearch/src/commonMain/kotlin/com/algolia/instantsearch/extension/KSerializer.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.extension
2 |
3 | import kotlinx.serialization.KSerializer
4 |
5 | @Suppress("UNCHECKED_CAST")
6 | internal fun KSerializer<*>.cast(): KSerializer = this as KSerializer
7 |
--------------------------------------------------------------------------------
/instantsearch/src/commonMain/kotlin/com/algolia/instantsearch/extension/Utils.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.extension
2 |
3 | internal fun tryOrNull(block: () -> T): T? {
4 | return try {
5 | block()
6 | } catch (e: Throwable) {
7 | null
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/instantsearch/src/commonMain/kotlin/com/algolia/instantsearch/filter/clear/ClearMode.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.filter.clear
2 |
3 | public enum class ClearMode {
4 | Specified,
5 | Except
6 | }
7 |
--------------------------------------------------------------------------------
/instantsearch/src/commonMain/kotlin/com/algolia/instantsearch/filter/clear/FilterClearView.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.filter.clear
2 |
3 | import com.algolia.instantsearch.core.Callback
4 |
5 | public interface FilterClearView {
6 |
7 | public var onClear: Callback?
8 | }
9 |
--------------------------------------------------------------------------------
/instantsearch/src/commonMain/kotlin/com/algolia/instantsearch/filter/clear/FilterClearViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.filter.clear
2 |
3 | import com.algolia.instantsearch.core.subscription.SubscriptionEvent
4 | import com.algolia.instantsearch.extension.traceFilterClear
5 |
6 | public open class FilterClearViewModel {
7 |
8 | init {
9 | traceFilterClear()
10 | }
11 |
12 | public val eventClear: SubscriptionEvent = SubscriptionEvent()
13 | }
14 |
--------------------------------------------------------------------------------
/instantsearch/src/commonMain/kotlin/com/algolia/instantsearch/filter/clear/internal/FilterClearConnectionView.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.filter.clear.internal
2 |
3 | import com.algolia.instantsearch.core.connection.AbstractConnection
4 | import com.algolia.instantsearch.filter.clear.FilterClearView
5 | import com.algolia.instantsearch.filter.clear.FilterClearViewModel
6 |
7 | internal data class FilterClearConnectionView(
8 | private val viewModel: FilterClearViewModel,
9 | private val view: FilterClearView,
10 | ) : AbstractConnection() {
11 |
12 | override fun connect() {
13 | super.connect()
14 | view.onClear = (viewModel.eventClear::send)
15 | }
16 |
17 | override fun disconnect() {
18 | super.disconnect()
19 | view.onClear = null
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/instantsearch/src/commonMain/kotlin/com/algolia/instantsearch/filter/current/FilterAndID.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.filter.current
2 |
3 | import com.algolia.instantsearch.filter.state.FilterGroupID
4 | import com.algolia.search.model.filter.Filter
5 |
6 | public typealias FilterAndID = Pair
7 |
8 | internal fun Map>.toFilterAndIds(): Map {
9 | return flatMap { (key, value) -> value.map { FilterAndID(key, it) to it } }.toMap()
10 | }
11 |
--------------------------------------------------------------------------------
/instantsearch/src/commonMain/kotlin/com/algolia/instantsearch/filter/current/FilterCurrentPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.algolia.instantsearch.filter.current
2 |
3 | import com.algolia.instantsearch.core.Presenter
4 | import com.algolia.search.model.filter.Filter
5 |
6 | public interface FilterCurrentPresenter : Presenter