|
1 | 1 | package com.mapbox.search.autofill
|
2 | 2 |
|
| 3 | +import android.app.Application |
| 4 | +import com.mapbox.android.core.location.LocationEngine |
3 | 5 | import com.mapbox.bindgen.Expected
|
4 |
| -import com.mapbox.bindgen.ExpectedFactory.createError |
5 |
| -import com.mapbox.bindgen.ExpectedFactory.createValue |
6 | 6 | import com.mapbox.geojson.Point
|
7 |
| -import com.mapbox.search.base.BaseResponseInfo |
| 7 | +import com.mapbox.search.base.SearchRequestContextProvider |
| 8 | +import com.mapbox.search.base.core.CoreApiType |
| 9 | +import com.mapbox.search.base.core.CoreEngineOptions |
| 10 | +import com.mapbox.search.base.core.CoreSearchEngine |
8 | 11 | import com.mapbox.search.base.core.createCoreReverseGeoOptions
|
9 | 12 | import com.mapbox.search.base.core.createCoreSearchOptions
|
| 13 | +import com.mapbox.search.base.engine.TwoStepsToOneStepSearchEngineAdapter |
| 14 | +import com.mapbox.search.base.location.LocationEngineAdapter |
| 15 | +import com.mapbox.search.base.location.WrapperLocationProvider |
| 16 | +import com.mapbox.search.base.record.IndexableRecordResolver |
| 17 | +import com.mapbox.search.base.record.SearchHistoryService |
10 | 18 | import com.mapbox.search.base.result.BaseSearchResult
|
11 |
| -import com.mapbox.search.base.result.BaseSearchSuggestion |
12 |
| -import com.mapbox.search.base.result.BaseSearchSuggestionType |
13 |
| -import kotlinx.coroutines.Deferred |
14 |
| -import kotlinx.coroutines.async |
15 |
| -import kotlinx.coroutines.coroutineScope |
16 |
| - |
17 |
| -internal sealed class SearchSelectionResponse { |
18 |
| - |
19 |
| - data class Suggestions( |
20 |
| - val suggestions: List<BaseSearchSuggestion>, |
21 |
| - val responseInfo: BaseResponseInfo, |
22 |
| - ) : SearchSelectionResponse() |
23 |
| - |
24 |
| - data class Result( |
25 |
| - val suggestion: BaseSearchSuggestion, |
26 |
| - val result: BaseSearchResult, |
27 |
| - val responseInfo: BaseResponseInfo, |
28 |
| - ) : SearchSelectionResponse() |
29 |
| - |
30 |
| - data class CategoryResult( |
31 |
| - val suggestion: BaseSearchSuggestion, |
32 |
| - val results: List<BaseSearchResult>, |
33 |
| - val responseInfo: BaseResponseInfo, |
34 |
| - ) : SearchSelectionResponse() |
35 |
| -} |
| 19 | +import com.mapbox.search.base.result.SearchResultFactory |
| 20 | +import com.mapbox.search.base.utils.UserAgentProvider |
| 21 | +import java.util.concurrent.ExecutorService |
| 22 | +import java.util.concurrent.Executors |
36 | 23 |
|
37 | 24 | /**
|
38 | 25 | * Temporary implementation of the [AddressAutofill] based on the two-step search.
|
39 | 26 | */
|
40 |
| -internal class AddressAutofillImpl(private val searchEngine: AutofillSearchEngine) : AddressAutofill { |
| 27 | +internal class AddressAutofillImpl(private val searchEngine: TwoStepsToOneStepSearchEngineAdapter) : AddressAutofill { |
41 | 28 |
|
42 |
| - override suspend fun suggestions(point: Point, options: AddressAutofillOptions): Expected<Exception, List<AddressAutofillSuggestion>> { |
| 29 | + override suspend fun suggestions( |
| 30 | + point: Point, |
| 31 | + options: AddressAutofillOptions |
| 32 | + ): Expected<Exception, List<AddressAutofillSuggestion>> { |
43 | 33 | val coreOptions = createCoreReverseGeoOptions(
|
44 | 34 | point = point,
|
45 | 35 | countries = options.countries?.map { it.code },
|
46 | 36 | language = options.language?.let { listOf(it.code) },
|
47 | 37 | )
|
48 | 38 |
|
49 |
| - return searchEngine.search(coreOptions).mapValue { (results, _) -> |
| 39 | + return searchEngine.reverseGeocoding(coreOptions).mapValue { (results, _) -> |
50 | 40 | results.toAddressAutofillSuggestions()
|
51 | 41 | }
|
52 | 42 | }
|
53 | 43 |
|
54 |
| - override suspend fun suggestions(query: Query, options: AddressAutofillOptions): Expected<Exception, List<AddressAutofillSuggestion>> { |
55 |
| - val response = searchEngine.search( |
56 |
| - query = query.query, |
57 |
| - options = createCoreSearchOptions( |
58 |
| - countries = options.countries?.map { it.code }, |
59 |
| - language = options.language?.let { listOf(it.code) }, |
60 |
| - limit = 10, |
61 |
| - ignoreUR = true, |
62 |
| - ) |
| 44 | + override suspend fun suggestions( |
| 45 | + query: Query, |
| 46 | + options: AddressAutofillOptions |
| 47 | + ): Expected<Exception, List<AddressAutofillSuggestion>> { |
| 48 | + val coreOptions = createCoreSearchOptions( |
| 49 | + countries = options.countries?.map { it.code }, |
| 50 | + language = options.language?.let { listOf(it.code) }, |
| 51 | + limit = 10, |
| 52 | + ignoreUR = true, |
63 | 53 | )
|
64 |
| - |
65 |
| - return if (response.isValue) { |
66 |
| - forwardGeocoding(requireNotNull(response.value).first) |
67 |
| - } else { |
68 |
| - createError(requireNotNull(response.error)) |
| 54 | + return searchEngine.searchResolveImmediately(query.query, coreOptions).mapValue { |
| 55 | + it.toAddressAutofillSuggestions() |
69 | 56 | }
|
70 | 57 | }
|
71 | 58 |
|
72 |
| - private suspend fun forwardGeocoding(suggestions: List<BaseSearchSuggestion>): Expected<Exception, List<AddressAutofillSuggestion>> { |
73 |
| - return when { |
74 |
| - suggestions.isEmpty() -> { |
75 |
| - createValue(emptyList()) |
76 |
| - } |
77 |
| - suggestions.all { it.isBatchResolveSupported } -> { |
78 |
| - searchEngine.select(suggestions).mapValue { (_, results, _) -> |
79 |
| - results.toAddressAutofillSuggestions() |
80 |
| - } |
81 |
| - } |
82 |
| - else -> { |
83 |
| - coroutineScope { |
84 |
| - val deferred: List<Deferred<Expected<Exception, SearchSelectionResponse>>> = suggestions |
85 |
| - // Filtering in order to avoid infinite recursion |
86 |
| - // because of some specific suggestions like "Did you mean recursion?" |
87 |
| - .filter { it.type !is BaseSearchSuggestionType.Query } |
88 |
| - .map { suggestion -> |
89 |
| - async { |
90 |
| - searchEngine.select(suggestion) |
91 |
| - } |
92 |
| - } |
| 59 | + internal companion object { |
93 | 60 |
|
94 |
| - val selectionResponses = deferred.map { it.await() } |
| 61 | + private val DEFAULT_EXECUTOR: ExecutorService = Executors.newSingleThreadExecutor { runnable -> |
| 62 | + Thread(runnable, "AddressAutofill executor") |
| 63 | + } |
95 | 64 |
|
96 |
| - val responses: List<Expected<Exception, List<AddressAutofillSuggestion>>> = selectionResponses |
97 |
| - .map { result -> |
98 |
| - if (result.isValue) { |
99 |
| - when (val response = requireNotNull(result.value)) { |
100 |
| - is SearchSelectionResponse.Suggestions -> { |
101 |
| - forwardGeocoding(response.suggestions) |
102 |
| - } |
103 |
| - is SearchSelectionResponse.Result -> { |
104 |
| - val autofillSuggestion = response.result.toAddressAutofillSuggestion() |
105 |
| - if (autofillSuggestion != null) { |
106 |
| - createValue(listOf(autofillSuggestion)) |
107 |
| - } else { |
108 |
| - createValue(emptyList()) |
109 |
| - } |
110 |
| - } |
111 |
| - is SearchSelectionResponse.CategoryResult -> { |
112 |
| - createValue(response.results.toAddressAutofillSuggestions()) |
113 |
| - } |
114 |
| - } |
115 |
| - } else { |
116 |
| - createError(requireNotNull(result.error)) |
117 |
| - } |
118 |
| - } |
| 65 | + fun create( |
| 66 | + accessToken: String, |
| 67 | + app: Application, |
| 68 | + locationEngine: LocationEngine, |
| 69 | + ): AddressAutofillImpl { |
| 70 | + val coreEngine = CoreSearchEngine( |
| 71 | + CoreEngineOptions( |
| 72 | + accessToken, |
| 73 | + null, |
| 74 | + CoreApiType.AUTOFILL, |
| 75 | + UserAgentProvider.userAgent, |
| 76 | + null |
| 77 | + ), |
| 78 | + WrapperLocationProvider( |
| 79 | + LocationEngineAdapter(app, locationEngine), |
| 80 | + null |
| 81 | + ), |
| 82 | + ) |
119 | 83 |
|
120 |
| - // If at least one response completed successfully, return it. |
121 |
| - if (responses.isNotEmpty() && responses.all { it.isError }) { |
122 |
| - responses.first() |
123 |
| - } else { |
124 |
| - responses.asSequence() |
125 |
| - .mapNotNull { it.value } |
126 |
| - .flatten() |
127 |
| - .toList() |
128 |
| - .let { |
129 |
| - createValue(it) |
130 |
| - } |
131 |
| - } |
132 |
| - } |
133 |
| - } |
134 |
| - } |
135 |
| - } |
| 84 | + val engine = TwoStepsToOneStepSearchEngineAdapter( |
| 85 | + apiType = CoreApiType.AUTOFILL, |
| 86 | + coreEngine = coreEngine, |
| 87 | + requestContextProvider = SearchRequestContextProvider(app), |
| 88 | + historyService = SearchHistoryService.STUB, |
| 89 | + searchResultFactory = SearchResultFactory(IndexableRecordResolver.EMPTY), |
| 90 | + engineExecutorService = DEFAULT_EXECUTOR |
| 91 | + ) |
136 | 92 |
|
137 |
| - private companion object { |
| 93 | + return AddressAutofillImpl(engine) |
| 94 | + } |
138 | 95 |
|
139 |
| - fun List<BaseSearchResult>.toAddressAutofillSuggestions() = mapNotNull { it.toAddressAutofillSuggestion() } |
| 96 | + private fun List<BaseSearchResult>.toAddressAutofillSuggestions() = mapNotNull { it.toAddressAutofillSuggestion() } |
140 | 97 |
|
141 |
| - fun BaseSearchResult.toAddressAutofillSuggestion(): AddressAutofillSuggestion? { |
| 98 | + private fun BaseSearchResult.toAddressAutofillSuggestion(): AddressAutofillSuggestion? { |
142 | 99 | // Filtering incomplete results
|
143 | 100 | val autofillAddress = AddressComponents.fromCoreSdkAddress(address, metadata) ?: return null
|
144 |
| - val validCoordinate = coordinate |
145 | 101 |
|
146 | 102 | return AddressAutofillSuggestion(
|
147 | 103 | name = name,
|
148 | 104 | formattedAddress = fullAddress ?: autofillAddress.formattedAddress(),
|
149 | 105 | address = autofillAddress,
|
150 |
| - coordinate = validCoordinate, |
| 106 | + coordinate = coordinate, |
151 | 107 | )
|
152 | 108 | }
|
153 | 109 | }
|
|
0 commit comments