Search In Apps SDK Implementation for Android

Information Required

Information provided by Google

  • client_id: <to be populated: e.x. ms-demo-app-sia>

Information you provide to Google

  • An email address for a user with administrator access to an appropriate Google Cloud account
  • The project number for a Cloud project that this user has administrator access

API Key Generation Process

  1. Provide your project number (see below) and the project admin's workspace account (email address) to Google for your project number to be added to the Trusted Partner group. Google Cloud Project Info
  2. Google will provide your account with access to the Search in Apps API, and notify you once you can proceed with the next step.
  3. Add the Search in Apps API Service to your Google Cloud account (Enable and Disable APIs)
  4. Create an API Key in Google Cloud (Setting up API keys) to be used in the Implementation Process section.

Implementation Process

Dependency

Import the "searchinapps" SDK into an Android project from GMaven. Open your project's build.gradle file, declare the Google Maven repository and add the SDK dependency:

repositories {
  google()
  ...
}

dependencies {
  implementation 'com.google.android.libraries.searchinapps:searchinapps:[version]'
  ...
}

Then build your project using Gradle.

AndroidManifest.xml configuration

Update your Android project AndroidManifest.xml file to add the following metadata:

  1. com.google.searchinapps.API_KEY: the string value of your SDK api key (see above).
  2. com.google.searchinapps.CLIENT_ID: the string value of your app's client identifier (see above).

Sample AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.samples.quickstart.searchinapps">

<application
...
>
<meta-data
android:name="com.google.searchinapps.API_KEY"
android:value="[your api key]"/>
<meta-data
android:name="com.google.searchinapps.CLIENT_ID"
android:value="[your client id]"/>
...
</application>

</manifest>

Initialization

To use "searchinapps" SDK's functions, in the target Activity or any classes that's responsible for retrieving search suggestions, create a SearchInAppsService instance (you can do it in the Activity class's onCreate function) and also pass activity or application Context into it.

Sample code

Java

package ...;

...
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.libraries.searchinapps.SearchInAppsService;
...

public class MainActivity extends AppCompatActivity {
  private SearchInAppsService service;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    ...
    service = SearchInAppsService.create(this);
    ...
  }
}

Jetpack Compose

package ...

...
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import com.google.android.libraries.searchinapps.SearchInAppsService
...

class MainActivityJetpack : AppCompatActivity() {
  private var service: SearchInAppsService? = null

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
      SearchSuggestionsUI()
    }
  }

  @Composable
  fun SearchSuggestionsUI() {
    ...
    if (service == null) {
      service = SearchInAppsService.create(LocalContext.current)
    }
    ...
  }
  ...
}

Request search suggestions UI view generator

To get the search suggestions UI view, you should get its generator first by the following steps:

  1. Let your target Activity class implement the GetSearchSuggestionsViewGeneratorCallback interface or use the anonymous inner class.
  2. Override the GetSearchSuggestionsViewGeneratorCallback interface's onSuccess(SearchSuggestionsViewGenerator) and onError(String) methods.
  3. Construct the GetSearchSuggestionsViewOptions class instance with a list of search contexts. (Optional) This object also takes a SearchSuggestionsViewOptions object which provides some options to customize the appearance of the search suggestions UI.
  4. Call SearchInAppsService's getSearchSuggestionsView(GetSearchSuggestionsViewOptions, GetSearchSuggestionsViewGeneratorCallback) function.
  5. After you get the UI generator, you can consider storing it in a ViewModel so that you don't need to ask for the generator again when the activity needs to recreate (like when the configuration has changed while the app is running).

Sample code

Java

package ...;

...
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.libraries.searchinapps.GetSearchSuggestionsViewGeneratorCallback;
import com.google.android.libraries.searchinapps.GetSearchSuggestionsViewOptions;
import com.google.android.libraries.searchinapps.SearchInAppsService;
import com.google.android.libraries.searchinapps.SearchSuggestionsViewGenerator;
import java.util.Arrays;
import java.util.List;
...

public class MainActivity extends AppCompatActivity implements GetSearchSuggestionsViewGeneratorCallback {
  private SearchInAppsService service;

  @Override
  public void onSuccess(SearchSuggestionsViewGenerator generator) {
    ...
  }

  @Override
  public void onError(String errorMessage) {
    ...
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    ...
    service = SearchInAppsService.create(this);
    // Right now only the first element of this list will be used.
  List<String> searchContext = Arrays.asList(new String[]{"This is a test query, for example."});
    // Uses the default SearchSuggestionsViewOptions.
    service.getSearchSuggestionsView(
      new GetSearchSuggestionsViewOptions().setTextContext(searchContext),this);
    ...
  }
}

Jetpack Compose

package ...

...
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import com.google.android.libraries.searchinapps.GetSearchSuggestionsViewGeneratorCallback
import com.google.android.libraries.searchinapps.GetSearchSuggestionsViewOptions
import com.google.android.libraries.searchinapps.SearchInAppsService
import com.google.android.libraries.searchinapps.SearchSuggestionsViewGenerator
...

class MainActivity : AppCompatActivity() {
  private var service: SearchInAppsService? = null

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
      SearchSuggestionsUI()
    }
  }

  @Composable
  fun SearchSuggestionsUI() {
    ...
    if (service == null) {
      service = SearchInAppsService.create(LocalContext.current)
    }

    val callback =
              object : GetSearchSuggestionsViewGeneratorCallback {
                override fun onSuccess(generator: SearchSuggestionsViewGenerator) {
                  ...
                }

              override fun onError(errorMessage: String) {
                ...
              }
            }
  var searchContexts: List<String> = listOf<String>("Query")
  // Uses the default SearchSuggestionsViewOptions.
  var options: GetSearchSuggestionsViewOptions =
            GetSearchSuggestionsViewOptions()
              .setTextContext(searchContexts)
  service.getSearchSuggestionsView(options, callback)
  ...
}
}

Request location-based search suggestions

The getSearchSuggestionsView entry point also supports location-based search suggestions. Build a GetSearchSuggestionsViewOptions object with a list of LocationContext objects following these steps. Then, pass the GetSearchSuggestionsViewOptions to the getSearchSuggestionsView function, following the steps in the previous section.

  1. (Required) Build the LocationContext object with the GeographicalRestrictions object. The GeographicalRestrictions object takes a CircularArea object which contains the latitude and longitude of the center of the area and the radius of the area.
  2. (Optional) Build the TimeRestrictions object with the TimeSegment object. The TimeSegment object takes a string which represents the time segment in the ISO 8601 format.
  3. (Optional) Build the GeoTypeRestrictions object with the requested geo types. Provided types are used to adjust the ranking and filtering of results returned by the service. The supported values are: "restaurant", "cafe", "bar", "park", "zoo", "museum", "attraction", "atm", "bank", "hair_salon", "real_estate_agency", "bicycle_sharing_location", "car_rental_agency", "shopping_center", "grocery_store" and "hotel".

Sample code

Java

package ...;

...
import com.google.android.libraries.searchinapps.GetSearchSuggestionsViewGeneratorCallback;
import com.google.android.libraries.searchinapps.GetSearchSuggestionsViewOptions;
import com.google.android.libraries.searchinapps.LocationContext;
import com.google.android.libraries.searchinapps.LocationContext.CircularArea;
import com.google.android.libraries.searchinapps.LocationContext.GeoTypeRestrictions;
import com.google.android.libraries.searchinapps.LocationContext.GeographicalRestrictions;
import com.google.android.libraries.searchinapps.LocationContext.LatLng;
import com.google.android.libraries.searchinapps.LocationContext.TimeRestrictions;
import com.google.android.libraries.searchinapps.LocationContext.TimeSegment;
import com.google.android.libraries.searchinapps.SearchInAppsService;
import com.google.android.libraries.searchinapps.SearchSuggestionsViewGenerator;
...
LocationContext locationContext = new LocationContext(
    new GeographicalRestrictions(
        new CircularArea(
            new LatLng(
                40.7414728,
                -74.0059622),
                1000)))
    // Optional. If set, the returned suggestions are categories of which
    // there are several businesses in the requested area that are open at
    // the given time.
    .setTimeRestrictions(
      new TimeRestrictions(
        new TimeSegment("2024-05-18T19:20:30.45+01:00")))
    // Optional. If set, the returned suggestions are subset of the
    // requested categories.
    .setGeoTypeRestrictions(
      new GeoTypeRestrictions("restaurant", "parks"));

// Uses the default SearchSuggestionsViewOptions.
service = SearchInAppsService.create(this);
// Uses the default SearchSuggestionsViewOptions.
service.getSearchSuggestionsView(
  new GetSearchSuggestionsViewOptions().setLocationContext(Arrays.asList(locationContext)), this);

Jetpack Compose

package ...

...
import com.google.android.libraries.searchinapps.GetSearchSuggestionsViewGeneratorCallback
import com.google.android.libraries.searchinapps.GetSearchSuggestionsViewOptions
import com.google.android.libraries.searchinapps.LocationContext
import com.google.android.libraries.searchinapps.LocationContext.CircularArea
import com.google.android.libraries.searchinapps.LocationContext.GeoTypeRestrictions
import com.google.android.libraries.searchinapps.LocationContext.GeographicalRestrictions
import com.google.android.libraries.searchinapps.LocationContext.LatLng
import com.google.android.libraries.searchinapps.LocationContext.TimeRestrictions
import com.google.android.libraries.searchinapps.LocationContext.TimeSegment
import com.google.android.libraries.searchinapps.SearchInAppsService
import com.google.android.libraries.searchinapps.SearchSuggestionsViewGenerator
...

var locationContext: LocationContext = LocationContext(
  GeographicalRestrictions(
    CircularArea(
      LatLng(40.7414728, -74.0059622),
      1000)))
  // Optional. If set, there are several businesses in the requested area that
  // are open at the given time.
  .setTimeRestrictions(
    TimeRestrictions(
      TimeSegment("2024-05-18T19:20:30.45+01:00")))
  // Optional. If set, the returned suggestions are subset of the requested
  // categories.
  .setGeoTypeRestrictions(
    GeoTypeRestrictions("restaurant", "parks"))

// Uses the default SearchSuggestionsViewOptions.
var options: GetSearchSuggestionsViewOptions =
          GetSearchSuggestionsViewOptions()
            .setLocationContext(listOf<LocationContext>(locationContext))
service.getSearchSuggestionsView(options, callback)
...

Similar to request search suggestions, to get the trending searches UI view, you should get its generator first by the following steps:

  1. Let your target Activity class implement the GetSearchSuggestionsViewGeneratorCallback interface or use the anonymous inner class.
  2. Override the GetSearchSuggestionsViewGeneratorCallback interface's onSuccess(SearchSuggestionsViewGenerator) and onError(String) methods.
  3. Construct the GetTrendingSearchesViewOptions class instance with the maximum number of trending searches specified. (Optional) This object also takes a SearchSuggestionsViewOptions object which provides some options to customize the appearance of the search suggestions UI.
  4. Call SearchInAppsService's getTrendingSearchesView(GetTrendingSearchesViewOptions, GetSearchSuggestionsViewGeneratorCallback) function.
  5. After you get the UI generator, you can consider storing it in a ViewModel so that you don't need to ask for the generator again when the activity needs to to be recreated (like when the configuration has changed while the app is running).

Sample code

Java

package ...;

...
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.libraries.searchinapps.GetSearchSuggestionsViewGeneratorCallback;
import com.google.android.libraries.searchinapps.GetTrendingSearchesViewOptions;
import com.google.android.libraries.searchinapps.SearchInAppsService;
import com.google.android.libraries.searchinapps.SearchSuggestionsViewGenerator;
...

public class MainActivity extends AppCompatActivity implements GetSearchSuggestionsViewGeneratorCallback {
  private SearchInAppsService service;

  @Override
  public void onSuccess(SearchSuggestionsViewGenerator generator) {
    ...
  }

  @Override
  public void onError(String errorMessage) {
    ...
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    ...
    service = SearchInAppsService.create(this);
    // Uses the default SearchSuggestionsViewOptions.
    service.getTrendingSearchesView(
      new GetTrendingSearchesViewOptions().setMaxNumTrends(3), this);
    ...
  }
}

Jetpack Compose

package ...

...
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import com.google.android.libraries.searchinapps.GetSearchSuggestionsViewGeneratorCallback
import com.google.android.libraries.searchinapps.GetSearchSuggestionsViewOptions
import com.google.android.libraries.searchinapps.SearchInAppsService
import com.google.android.libraries.searchinapps.SearchSuggestionsViewGenerator
...

class MainActivity : AppCompatActivity() {
  private var service: SearchInAppsService? = null

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
      SearchSuggestionsUI()
    }
  }

  @Composable
  fun SearchSuggestionsUI() {
    ...
    if (service == null) {
      service = SearchInAppsService.create(LocalContext.current)
    }

    val callback =
            object : GetSearchSuggestionsViewGeneratorCallback {
              override fun onSuccess(generator: SearchSuggestionsViewGenerator) {
                ...
              }

              override fun onError(errorMessage: String) {
                ...
              }
            }
    // Uses the default SearchSuggestionsViewOptions.
    var options: GetTrendingSearchesViewOptions =
            GetTrendingSearchesViewOptions()
              .setMaxNumTrends(3)
    service.getTrendingSearchesView(options, callback)
    ...
  }
}

Add search suggestions UI view

For both getSearchSuggestionsView and getTrendingSearchesView, the final output is a SearchSuggestionsViewGenerator object. You can use this object's populateView(Context) function to generate the UI view and updateView(View) to update the existing view (especially for Jetpack Compose apps).

For Jetpack Compose apps, you should use AndroidView to consume the generated classic view.

Sample code

Java

package ...;

...
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.libraries.searchinapps.GetSearchSuggestionsViewGeneratorCallback;
import com.google.android.libraries.searchinapps.GetSearchSuggestionsViewOptions;
import com.google.android.libraries.searchinapps.SearchInAppsService;
import com.google.android.libraries.searchinapps.SearchSuggestionsViewGenerator;
import java.util.Arrays;
import java.util.List;
...

public class MainActivity extends AppCompatActivity implements GetSearchSuggestionsViewGeneratorCallback {
  private SearchInAppsService service;

  @Override
  public void onSuccess(SearchSuggestionsViewGenerator generator) {
    ViewGroup container = findViewById(R.id.[container_id]);
    container.removeAllViews();
    container.addView(generator.populateView(this));
  }

  @Override
  public void onError(String errorMessage) {
    ...
  }

@Override
protected void onCreate(Bundle savedInstanceState) {
  ...
  service = SearchInAppsService.create(this);
  List<String> searchContext = Arrays.asList(new String[]{"Query"});
  // Uses the default SearchSuggestionsViewOptions.
  service.getSearchSuggestionsView(
    new GetSearchSuggestionsViewOptions().setTextContext(searchContext),this);
  ...
}
}

Jetpack Compose

package ...

...
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import com.google.android.libraries.searchinapps.GetSearchSuggestionsViewGeneratorCallback
import com.google.android.libraries.searchinapps.GetSearchSuggestionsViewOptions
import com.google.android.libraries.searchinapps.SearchInAppsService
import com.google.android.libraries.searchinapps.SearchSuggestionsViewGenerator
...

class MainActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent { SearchSuggestionsUI() }
  }

  @Composable
  fun SearchSuggestionsUI() {
    ...
    var service: SearchInAppsService = SearchInAppsService.create(LocalContext.current)
    var viewGenerator = mutableStateOf<SearchSuggestionsViewGenerator?>(null)
    val callback =
      object : GetSearchSuggestionsViewGeneratorCallback {
        override fun onSuccess(generator: SearchSuggestionsViewGenerator) {
          viewGenerator.value = generator
        }

      override fun onError(errorMessage: String) {}
    }
  var searchContexts: List<String> = listOf<String>("Query")
  // Uses the default SearchSuggestionsViewOptions.
  var options: GetSearchSuggestionsViewOptions =
    GetSearchSuggestionsViewOptions().setTextContext(searchContexts)
  service.getSearchSuggestionsView(options, callback)
  ChipGroupUI(viewGenerator)
  ...
}

  @Composable
  fun ChipGroupUI(viewGenerator: MutableState<SearchSuggestionsViewGenerator?>) {
    viewGenerator.value?.let { viewGenerator ->
      var context = LocalContext.current
      AndroidView(
        factory = { context -> viewGenerator.populateView(context) },
        update = { view -> viewGenerator.updateView(view, context) },
      )
    }
  }
}

Show suggestions search results

After clicking a search suggestion chip, the corresponding Google Search results will be displayed in an Android Custom Tab. You can customize the appearance (like menu, colors) of your CustomTabs by providing a CustomTabsIntent.Builder object to the SearchSuggestionsViewOptions object.

Sample apps code

You can find this SDK's complete sample apps code in this GitHub repository.