Custom data provider
This example does not contain a UI. Results will be logged in Android Studio.
Note
This example is a part of the Search SDK for Android sample app. You can find the values for all referenced resources in the res
directory. For example, see res/values/strings.xml
for R.string.*
references used in this example. The dependencies can be found here.The examples use View binding.See setup documention if necessary.
package com.mapbox.search.sample.api;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.mapbox.geojson.Point;
import com.mapbox.search.common.AsyncOperationTask;
import com.mapbox.search.common.CompletionCallback;
import com.mapbox.search.ResponseInfo;
import com.mapbox.search.SearchEngine;
import com.mapbox.search.SearchEngineSettings;
import com.mapbox.search.SearchOptions;
import com.mapbox.search.SearchSelectionCallback;
import com.mapbox.search.common.concurrent.SearchSdkMainThreadWorker;
import com.mapbox.search.record.FavoriteRecord;
import com.mapbox.search.record.IndexableDataProvider;
import com.mapbox.search.record.IndexableDataProviderEngine;
import com.mapbox.search.record.IndexableRecord;
import com.mapbox.search.result.SearchResult;
import com.mapbox.search.result.SearchResultType;
import com.mapbox.search.result.SearchSuggestion;
import com.mapbox.search.sample.R;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Executor;
import kotlin.Unit;
public class CustomIndexableDataProviderJavaExample extends AppCompatActivity {
private SearchEngine searchEngine;
private AsyncOperationTask registerProviderTask;
@Nullable
private AsyncOperationTask searchRequestTask = null;
private final InMemoryDataProvider<IndexableRecord> customDataProvider = new InMemoryDataProvider<>(
Arrays.asList(
createRecord("Let it be", Point.fromLngLat(27.575321258282806, 53.89025545661358)),
createRecord("Laŭka", Point.fromLngLat(27.574862357961212, 53.88998973246244)),
createRecord("Underdog", Point.fromLngLat(27.57573285942709, 53.89020312748444))
)
);
private final SearchSelectionCallback searchCallback = new SearchSelectionCallback() {
@Override
public void onSuggestions(@NonNull List<SearchSuggestion> suggestions, @NonNull ResponseInfo responseInfo) {
if (suggestions.isEmpty()) {
Log.i("SearchApiExample", "No suggestions found");
} else {
Log.i("SearchApiExample", "Search suggestions: " + suggestions + ".\nSelecting first suggestion...");
searchRequestTask = searchEngine.select(suggestions.get(0), this);
}
}
@Override
public void onResult(@NonNull SearchSuggestion suggestion, @NonNull SearchResult result, @NonNull ResponseInfo responseInfo) {
Log.i("SearchApiExample", "Search result: " + result);
}
@Override
public void onResults(@NonNull SearchSuggestion suggestion, @NonNull List<SearchResult> results, @NonNull ResponseInfo responseInfo) {
Log.i("SearchApiExample", "Category search results: " + results);
}
@Override
public void onError(@NonNull Exception e) {
Log.i("SearchApiExample", "Search error", e);
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
searchEngine = SearchEngine.createSearchEngineWithBuiltInDataProviders(
new SearchEngineSettings(getString(R.string.mapbox_access_token))
);
Log.i("SearchApiExample", "Start CustomDataProvider registering...");
registerProviderTask = searchEngine.registerDataProvider(
customDataProvider,
new CompletionCallback<Unit>() {
@Override
public void onComplete(Unit result) {
Log.i("SearchApiExample", "CustomDataProvider is registered");
searchRequestTask = searchEngine.search(
"Underdog",
new SearchOptions.Builder()
.proximity(Point.fromLngLat(27.574862357961212, 53.88998973246244))
.build(),
searchCallback
);
}
@Override
public void onError(@NonNull Exception e) {
Log.i("SearchApiExample", "Error during registering", e);
}
}
);
}
@Override
protected void onDestroy() {
registerProviderTask.cancel();
if (searchRequestTask != null) {
searchRequestTask.cancel();
}
searchEngine.unregisterDataProvider(
customDataProvider,
new CompletionCallback<Unit>() {
@Override
public void onComplete(Unit result) {
Log.i("SearchApiExample", "CustomDataProvider is unregistered");
}
@Override
public void onError(@NonNull Exception e) {
Log.i("SearchApiExample", "Error during unregistering", e);
}
}
);
super.onDestroy();
}
private IndexableRecord createRecord(String name, Point coordinate) {
return new FavoriteRecord(
UUID.randomUUID().toString(),
name,
null,
null,
null,
Collections.emptyList(),
null,
coordinate,
SearchResultType.POI,
null
);
}
private static class InMemoryDataProvider<R extends IndexableRecord> implements IndexableDataProvider<R> {
private final List<IndexableDataProviderEngine> dataProviderEngines = new ArrayList<>();
private final Map<String, R> records = new LinkedHashMap<>();
private final Executor mainThreadExecutor = SearchSdkMainThreadWorker.INSTANCE.getMainExecutor();
InMemoryDataProvider(List<R> records) {
for (R record : records) {
this.records.put(record.getId(), record);
}
}
@NonNull
@Override
public String getDataProviderName() {
return "SAMPLE_APP_CUSTOM_DATA_PROVIDER";
}
@Override
public int getPriority() {
return 200;
}
@NonNull
@Override
public AsyncOperationTask registerIndexableDataProviderEngine(
@NonNull IndexableDataProviderEngine dataProviderEngine,
@NonNull Executor executor,
@NonNull CompletionCallback<Unit> callback
) {
dataProviderEngine.upsertAll(records.values());
dataProviderEngines.add(dataProviderEngine);
executor.execute(() -> callback.onComplete(Unit.INSTANCE));
return AsyncOperationTask.getCompleted();
}
@NonNull
@Override
public AsyncOperationTask registerIndexableDataProviderEngine(
@NonNull IndexableDataProviderEngine dataProviderEngine,
@NonNull CompletionCallback<Unit> callback
) {
return registerIndexableDataProviderEngine(dataProviderEngine, mainThreadExecutor, callback);
}
@NonNull
@Override
public AsyncOperationTask unregisterIndexableDataProviderEngine(
@NonNull IndexableDataProviderEngine dataProviderEngine,
@NonNull Executor executor,
@NonNull CompletionCallback<Boolean> callback
) {
boolean isRemoved = dataProviderEngines.remove(dataProviderEngine);
if (isRemoved) {
dataProviderEngine.clear();
}
executor.execute(() -> callback.onComplete(isRemoved));
return AsyncOperationTask.getCompleted();
}
@NonNull
@Override
public AsyncOperationTask unregisterIndexableDataProviderEngine(
@NonNull IndexableDataProviderEngine dataProviderEngine,
@NonNull CompletionCallback<Boolean> callback
) {
return unregisterIndexableDataProviderEngine(dataProviderEngine, mainThreadExecutor, callback);
}
@NonNull
@Override
public AsyncOperationTask get(
@NonNull String id,
@NonNull Executor executor,
@NonNull CompletionCallback<? super R> callback
) {
executor.execute(() -> callback.onComplete(records.get(id)));
return AsyncOperationTask.getCompleted();
}
@NonNull
@Override
public AsyncOperationTask get(@NonNull String id, @NonNull CompletionCallback<? super R> callback) {
return get(id, mainThreadExecutor, callback);
}
@NonNull
@Override
public AsyncOperationTask getAll(@NonNull Executor executor, @NonNull CompletionCallback<List<R>> callback) {
executor.execute(() -> callback.onComplete(new ArrayList<>(records.values())));
return AsyncOperationTask.getCompleted();
}
@NonNull
@Override
public AsyncOperationTask getAll(@NonNull CompletionCallback<List<R>> callback) {
return getAll(mainThreadExecutor, callback);
}
@NonNull
@Override
public AsyncOperationTask contains(
@NonNull String id,
@NonNull Executor executor,
@NonNull CompletionCallback<Boolean> callback
) {
executor.execute(() -> callback.onComplete(records.get(id) != null));
return AsyncOperationTask.getCompleted();
}
@NonNull
@Override
public AsyncOperationTask contains(@NonNull String id, @NonNull CompletionCallback<Boolean> callback) {
return contains(id, mainThreadExecutor, callback);
}
@NonNull
@Override
public AsyncOperationTask upsert(
@NonNull R record,
@NonNull Executor executor,
@NonNull CompletionCallback<Unit> callback
) {
for (IndexableDataProviderEngine engine : dataProviderEngines) {
engine.upsert(record);
}
records.put(record.getId(), record);
executor.execute(() -> callback.onComplete(Unit.INSTANCE));
return AsyncOperationTask.getCompleted();
}
@NonNull
@Override
public AsyncOperationTask upsert(@NonNull R record, @NonNull CompletionCallback<Unit> callback) {
return upsert(record, mainThreadExecutor, callback);
}
@NonNull
@Override
public AsyncOperationTask upsertAll(
@NonNull List<? extends R> records,
@NonNull Executor executor,
@NonNull CompletionCallback<Unit> callback
) {
for (IndexableDataProviderEngine engine : dataProviderEngines) {
engine.upsertAll(records);
}
for (R record : records) {
this.records.put(record.getId(), record);
}
executor.execute(() -> callback.onComplete(Unit.INSTANCE));
return AsyncOperationTask.getCompleted();
}
@NonNull
@Override
public AsyncOperationTask upsertAll(@NonNull List<? extends R> records, @NonNull CompletionCallback<Unit> callback) {
return upsertAll(records, mainThreadExecutor, callback);
}
@NonNull
@Override
public AsyncOperationTask remove(
@NonNull String id,
@NonNull Executor executor,
@NonNull CompletionCallback<Boolean> callback
) {
for (IndexableDataProviderEngine engine : dataProviderEngines) {
engine.remove(id);
}
boolean isRemoved = records.remove(id) != null;
executor.execute(() -> callback.onComplete(isRemoved));
return AsyncOperationTask.getCompleted();
}
@NonNull
@Override
public AsyncOperationTask remove(@NonNull String id, @NonNull CompletionCallback<Boolean> callback) {
return remove(id, mainThreadExecutor, callback);
}
@NonNull
@Override
public AsyncOperationTask clear(@NonNull Executor executor, @NonNull CompletionCallback<Unit> callback) {
for (IndexableDataProviderEngine engine : dataProviderEngines) {
engine.clear();
}
records.clear();
executor.execute(() -> callback.onComplete(Unit.INSTANCE));
return AsyncOperationTask.getCompleted();
}
@NonNull
@Override
public AsyncOperationTask clear(@NonNull CompletionCallback<Unit> callback) {
return clear(mainThreadExecutor, callback);
}
}
}
package com.mapbox.search.sample.api
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.mapbox.geojson.Point
import com.mapbox.search.ResponseInfo
import com.mapbox.search.SearchEngine
import com.mapbox.search.SearchEngineSettings
import com.mapbox.search.SearchOptions
import com.mapbox.search.SearchSelectionCallback
import com.mapbox.search.common.AsyncOperationTask
import com.mapbox.search.common.CompletionCallback
import com.mapbox.search.record.FavoriteRecord
import com.mapbox.search.record.IndexableDataProvider
import com.mapbox.search.record.IndexableDataProviderEngine
import com.mapbox.search.record.IndexableRecord
import com.mapbox.search.result.SearchResult
import com.mapbox.search.result.SearchResultType
import com.mapbox.search.result.SearchSuggestion
import com.mapbox.search.sample.R
import java.util.ArrayList
import java.util.UUID
import java.util.concurrent.Executor
class CustomIndexableDataProviderKotlinExample : AppCompatActivity() {
private lateinit var searchEngine: SearchEngine
private lateinit var registerProviderTask: AsyncOperationTask
private var searchRequestTask: AsyncOperationTask? = null
private val customDataProvider = InMemoryDataProvider(
records = listOf(
createRecord("Let it be", Point.fromLngLat(27.575321258282806, 53.89025545661358)),
createRecord("Laŭka", Point.fromLngLat(27.574862357961212, 53.88998973246244)),
createRecord("Underdog", Point.fromLngLat(27.57573285942709, 53.89020312748444)),
)
)
private val searchCallback = object : SearchSelectionCallback {
override fun onSuggestions(suggestions: List<SearchSuggestion>, responseInfo: ResponseInfo) {
if (suggestions.isEmpty()) {
Log.i("SearchApiExample", "No suggestions found")
} else {
Log.i("SearchApiExample", "Search suggestions: $suggestions.\nSelecting first suggestion...")
searchRequestTask = searchEngine.select(suggestions.first(), this)
}
}
override fun onResult(
suggestion: SearchSuggestion,
result: SearchResult,
responseInfo: ResponseInfo
) {
Log.i("SearchApiExample", "Search result: $result")
}
override fun onResults(
suggestion: SearchSuggestion,
results: List<SearchResult>,
responseInfo: ResponseInfo
) {
Log.i("SearchApiExample", "Category search results: $results")
}
override fun onError(e: Exception) {
Log.i("SearchApiExample", "Search error", e)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
searchEngine = SearchEngine.createSearchEngineWithBuiltInDataProviders(
SearchEngineSettings(getString(R.string.mapbox_access_token))
)
Log.i("SearchApiExample", "Start CustomDataProvider registering...")
registerProviderTask = searchEngine.registerDataProvider(
dataProvider = customDataProvider,
callback = object : CompletionCallback<Unit> {
override fun onComplete(result: Unit) {
Log.i("SearchApiExample", "CustomDataProvider is registered")
searchRequestTask = searchEngine.search(
"Underdog",
SearchOptions(
proximity = Point.fromLngLat(27.574862357961212, 53.88998973246244),
),
searchCallback
)
}
override fun onError(e: Exception) {
Log.i("SearchApiExample", "Error during registering", e)
}
}
)
}
override fun onDestroy() {
registerProviderTask.cancel()
searchRequestTask?.cancel()
searchEngine.unregisterDataProvider(
dataProvider = customDataProvider,
callback = object : CompletionCallback<Unit> {
override fun onComplete(result: Unit) {
Log.i("SearchApiExample", "CustomDataProvider is unregistered")
}
override fun onError(e: Exception) {
Log.i("SearchApiExample", "Error during unregistering", e)
}
}
)
super.onDestroy()
}
private fun createRecord(name: String, coordinate: Point): IndexableRecord {
return FavoriteRecord(
UUID.randomUUID().toString(),
name,
null,
null,
null,
emptyList(),
null,
coordinate,
SearchResultType.POI,
null,
)
}
private class InMemoryDataProvider<R : IndexableRecord>(records: List<R>) : IndexableDataProvider<R> {
private val dataProviderEngines: MutableList<IndexableDataProviderEngine> = mutableListOf()
private val records: MutableMap<String, R> = mutableMapOf()
override val dataProviderName: String = "SAMPLE_APP_CUSTOM_DATA_PROVIDER"
override val priority: Int = 200
init {
this.records.putAll(records.map { it.id to it })
}
override fun registerIndexableDataProviderEngine(
dataProviderEngine: IndexableDataProviderEngine,
executor: Executor,
callback: CompletionCallback<Unit>
): AsyncOperationTask {
dataProviderEngine.upsertAll(records.values.toList())
dataProviderEngines.add(dataProviderEngine)
executor.execute {
callback.onComplete(Unit)
}
return AsyncOperationTask.COMPLETED
}
override fun unregisterIndexableDataProviderEngine(
dataProviderEngine: IndexableDataProviderEngine,
executor: Executor,
callback: CompletionCallback<Boolean>
): AsyncOperationTask {
val isRemoved = dataProviderEngines.remove(dataProviderEngine)
if (isRemoved) {
dataProviderEngine.clear()
}
executor.execute {
callback.onComplete(isRemoved)
}
return AsyncOperationTask.COMPLETED
}
override operator fun get(
id: String,
executor: Executor,
callback: CompletionCallback<in R?>
): AsyncOperationTask {
executor.execute {
callback.onComplete(records[id])
}
return AsyncOperationTask.COMPLETED
}
override fun getAll(executor: Executor, callback: CompletionCallback<List<R>>): AsyncOperationTask {
executor.execute {
callback.onComplete(ArrayList(records.values))
}
return AsyncOperationTask.COMPLETED
}
override fun contains(
id: String,
executor: Executor,
callback: CompletionCallback<Boolean>
): AsyncOperationTask {
executor.execute {
callback.onComplete(records[id] != null)
}
return AsyncOperationTask.COMPLETED
}
override fun upsert(record: R, executor: Executor, callback: CompletionCallback<Unit>): AsyncOperationTask {
dataProviderEngines.forEach {
it.upsert(record)
}
records[record.id] = record
executor.execute {
callback.onComplete(Unit)
}
return AsyncOperationTask.COMPLETED
}
override fun upsertAll(
records: List<R>,
executor: Executor,
callback: CompletionCallback<Unit>
): AsyncOperationTask {
dataProviderEngines.forEach {
it.upsertAll(records)
}
for (record in records) {
this.records[record.id] = record
}
executor.execute {
callback.onComplete(Unit)
}
return AsyncOperationTask.COMPLETED
}
override fun remove(id: String, executor: Executor, callback: CompletionCallback<Boolean>): AsyncOperationTask {
dataProviderEngines.forEach {
it.remove(id)
}
val isRemoved = records.remove(id) != null
executor.execute {
callback.onComplete(isRemoved)
}
return AsyncOperationTask.COMPLETED
}
override fun clear(executor: Executor, callback: CompletionCallback<Unit>): AsyncOperationTask {
dataProviderEngines.forEach {
it.clear()
}
records.clear()
executor.execute {
callback.onComplete(Unit)
}
return AsyncOperationTask.COMPLETED
}
}
}
Was this example helpful?