Searching Flickr

Let’s begin with the Flickr side of things. To search Flickr, you call the flickr.photos.search method. Here is what a GET request to search for the text cat looks like:

    https://api.flickr.com/services/rest/?method=flickr.photos.search
    &api_key=xxx&format=json&nojsoncallback=1&extras=url_s&safe_search=1&text=cat

The method is set to flickr.photos.search. The text parameter is set to whatever string you are searching for (cat, in this case). Setting safesearch to 1 filters potentially offensive results from the search data sent back.

Some of the parameter-value pairs, such as format=json, are constant across both the flickr.photos.search and flickr.interestingness.getList request URLs. You are going to abstract these shared parameter-value pairs out into an interceptor. An interceptor does what you might expect – it intercepts a request or response and allows you to manipulate the contents or take some action before the request or response completes.

Create a new Interceptor class named PhotoInterceptor in your api folder. Override intercept(chain) to access a request, add the shared parameter-value pairs to it, and overwrite the original URL with the newly built URL. (Do not neglect to include your API key, which you created in Chapter 24, in place of yourApiKeyHere. You can copy it from api/FlickrApi.kt.)

Listing 26.1  Adding an interceptor to insert URL constants (api/PhotoInterceptor.kt)

private const val API_KEY = "yourApiKeyHere"

class PhotoInterceptor : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val originalRequest: Request = chain.request()

        val newUrl: HttpUrl = originalRequest.url().newBuilder()
                .addQueryParameter("api_key", API_KEY)
                .addQueryParameter("format", "json")
                .addQueryParameter("nojsoncallback", "1")
                .addQueryParameter("extras", "url_s")
                .addQueryParameter("safesearch", "1")
                .build()

        val newRequest: Request = originalRequest.newBuilder()
                .url(newUrl)
                .build()

        return chain.proceed(newRequest)
    }
}

Android Studio presents you with multiple options when importing Request and Response. Select the okhttp3 options in both cases.

Here, you call chain.request() to access the original request. originalRequest.url() pulls the original URL from the request, and you use HttpUrl.Builder to add the query parameters to it. HttpUrl.Builder creates a new Request based on the original request and overwrites the original URL with the new one. Finally, you call chain.proceed(newRequest) to produce a Response. If you did not call chain.proceed(…), the network request would not happen.

Now open FlickrFetchr.kt and add the interceptor to your Retrofit configuration.

Listing 26.2  Adding an interceptor to your Retrofit configuration (FlickrFetchr.kt)

class FlickrFetchr {

    private val flickrApi: FlickrApi

    init {
        val client = OkHttpClient.Builder()
            .addInterceptor(PhotoInterceptor())
            .build()

        val retrofit: Retrofit = Retrofit.Builder()
            .baseUrl("https://api.flickr.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .build()

        flickrApi = retrofit.create(FlickrApi::class.java)
    }
    ...
}

You create an OkHttpClient instance and add PhotoInterceptor as an interceptor. Then you set the newly configured client on your Retrofit instance, which replaces the default client that was already in place. Now Retrofit will use the client you provided and, in turn, apply PhotoInterceptor.intercept(…) to any requests that are made.

You no longer need the flickr.interestingness.getList URL specified in FlickrApi. Clean that up and, instead, add a searchPhotos() function to define a search request to your Retrofit API configuration.

Listing 26.3  Adding a search function to FlickrApi (api/FlickrApi.kt)

interface FlickrApi {

    @GET("services/rest/?method=flickr.interestingness.getList" +
            "&api_key=yourApiKeyHere" +
            "&format=json" +
            "&nojsoncallback=1" +
            "&extras=url_s")
    @GET("services/rest?method=flickr.interestingness.getList")
    fun fetchPhotos(): Call<FlickrResponse>

    @GET
    fun fetchUrlBytes(@Url url: String): Call<ResponseBody>

    @GET("services/rest?method=flickr.photos.search")
    fun searchPhotos(@Query("text") query: String): Call<FlickrResponse>
}

The @Query annotation allows you to dynamically append a query parameter appended to the URL. Here you append a query parameter named text. The value assigned to text depends on the argument passed into searchPhotos(String). For example, calling searchPhotos("robot") would add text=robot to the URL.

Add a search function to FlickrFetchr to wrap the newly added FlickrApi.searchPhotos(String) function. Pull the code that executes a Call object asynchronously and wraps the result in LiveData into a helper function.

Listing 26.4  Adding a search function to FlickrFetchr (FlickrFetchr.kt)

class FlickrFetchr {

  private val flickrApi: FlickrApi

  init {
      ...
  }

  fun fetchPhotos(): LiveData<List<GalleryItem>> {
      return fetchPhotoMetadata(flickrApi.fetchPhotos())
  }

  fun searchPhotos(query: String): LiveData<List<GalleryItem>> {
      return fetchPhotoMetadata(flickrApi.searchPhotos(query))
  }

  fun fetchPhotos(): LiveData<List<GalleryItem>> {
  private fun fetchPhotoMetadata(flickrRequest: Call<FlickrResponse>)
          : LiveData<List<GalleryItem>> {
      val responseLiveData: MutableLiveData<List<GalleryItem>> = MutableLiveData()
      val flickrRequest: Call<FlickrResponse> = flickrApi.fetchPhotos()

      flickrRequest.enqueue(object : Callback<FlickrResponse> {
          ...
      })

      return responseLiveData
  }
  ...
}

Finally, update PhotoGalleryViewModel to kick off a Flickr search. For now, hardcode the search term to be “planets.” Hardcoding the query allows you to test out your new search code even though you have not yet provided a way to enter a query through the UI.

Listing 26.5  Kicking off a search request (PhotoGalleryViewModel.kt)

class PhotoGalleryViewModel : ViewModel() {

    val galleryItemLiveData: LiveData<List<GalleryItem>>

    init {
        galleryItemLiveData = FlickrFetchr().fetchPhotos()searchPhotos("planets")
    }
}

While the search request URL differs from the one you used to request interesting photos, the format of the JSON data returned remains the same. This is good news, because it means you can use the same Gson configuration and model mapping code you already wrote.

Run PhotoGallery to ensure your search query works correctly. Hopefully, you will see a cool photo or two of Earth. (If you do not get results obviously related to planets, it does not mean your query is not working. Try a different search term – such as “bicycle” or “llama” – and run your app again to confirm that you are indeed seeing search results.)

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.235.243.45