Поиск изображений в Android при помощи Flickr

в 11:50, , рубрики: android, flickr, поиск изображений, Разработка под android

В этой публикации хотел бы поделиться опытом подбора контент-провайдера при поиске изображений и описать базовый интерфейс взаимодействия с одним из них на примере сервиса Flickr.

Так получилось, что при реализации одного из моих проектов понадобился поиск изображений по определенным ключевым словам. Поиск должен был быть реализован исходя из двух простых требований:

  • быть бесплатным;
  • быть хорошо фильтрованным, чтобы не нарваться на проблемы с Apple (такая же задача стояла и для версии приложения под iOS).

Я долго серфил интернеты в поисках подходящего контент-провайдера, предоставляющего JSON API. В итоге были отобраны 3 основных кандидата: Google Search, Flickr и Bing Image Search. Был еще один вариант подключения к старым бесплатным API от Google, которые уже лет 5 как в статусе deprecated, но все еще работают. Но этот вариант, увы, не подходил.

К сожалению, официальные Google Search API являются, во-первых, платными, во-вторых, в результате выдачи Google по некоторым запросам вылетают неприемлемые изображения. C Bing Image Search ситуация еще более плачевная, хотя доступ к API у них обходится и дешевле.

Чтобы не быть голословным, приведу сравнение выдачи всех трех провайдеров по провокационным запросам с применением максимальной фильтрации выдачи неприемлемого контента, да простят меня автолюбители.

Выдача Google

Поиск изображений в Android при помощи Flickr - 1

Выдача Bing

Поиск изображений в Android при помощи Flickr - 2

Выдача Flickr
Поиск изображений в Android при помощи Flickr - 3

У Google Search, на первый взгляд, все более или менее приемлемо, но если прокрутить страницу чуть ниже, местами встречаются очень печальные картинки. У Bing Image Search все еще гораздо более хардкорно. По понятным причинам скрины этого я приводить здесь не буду. Я проводил еще множество подобных сравнений, в том числе и по пикантным запросам, и во всех них однозначным лидером оказывался Flickr. Поэтому мой выбор пал именно на него. К тому же доступ к их API является бесплатным. Единственным жирным минусом были совсем уж скудные результаты поиска на русском языке. Забегая вперед, скажу, что решено это было на бекэнде – был поднят простенький сервис, переводящий русский текст на английский. Таким образом запрос к Flickr состоял из двух частей: запрос перевода у нашего сервера, отправка полученного результата на Flickr.

Регистрация приложения

Прежде всего нам необходимо получить API key работы с Flickr. Для этого перейдем по ссылке (только для некоммерческого использования)
После заполнения формы вам будут предоставлены Key и Secret:

Поиск изображений в Android при помощи Flickr - 4

Интерфейс запросов к Flickr

Для начала определимся, каким образом вообще выглядит запрос к API:

https://api.flickr.com/services/rest/?safe_search=safe&api_key=XXX&sort=relevance&method=flickr.interestingness.getList&per_page=50&media=photos&extras=url_sq,url_t,url_s,url_q,url_m,url_n,url_z,url_c,url_l,url_o&license=1,2,3,4,5,6&format=json

Нам будут интересны два метода: flickr.photos.search и flickr.interestingness.getList.

Метод запроса flickr.interestingness.getList возвращает нам список самых популярных изображений. Изображения не застаиваются и периодически обновляются (по моим наблюдениям раз в пол часа — час).

Метод запроса flickr.photos.search будет использоваться непосредственно для поиска по ключевым словам.

Более подробно про каждый из параметров запроса вы можете почитать у них в официальной документации.

Пример реализации

Для реализации общения с сервисом был реализован следующий класс:

public class Flickr extends Model {
    public interface FlickrModelResponseHandler {
        public void onSuccess(ArrayList<HashMap<String, String>> responseArray);
        public void onFailure(String error);
    }

    public Flickr(Context context) {
        super(context);        
        setUrl("/services/rest/");
    }

   
    public void fetch (Map<String, String> inData, Map<String, Object> options, final FlickrModelResponseHandler handler) {
        HashMap<String, String> fetchedParams = new HashMap(inData);
        fetchedParams.put("api_key", "XXX");
        fetchedParams.put("extras", "url_sq,url_t,url_s,url_q,url_m,url_n,url_z,url_c,url_l,url_o");
        fetchedParams.put("format", "json");
        fetchedParams.put("per_page", "50");
        fetchedParams.put("safe_search", "safe");
        fetchedParams.put("content_type", "1");
        fetchedParams.put("media", "photos");
        fetchedParams.put("sort", "relevance");
        fetchedParams.put("license", "1,2,3,4,5,6");
        super.fetch(fetchedParams, new HashMap<String, Object>() {{
            put("host", "https://api.flickr.com");            
        }}, new ModelResponseHandler() {
            @Override
            public void onSuccess(Map<String, Object> responseDict) {
                ArrayList<HashMap<String, String>> photos = ((HashMap<String, ArrayList<HashMap<String, String>>>)(responseDict.get("photos"))).get("photo");
                if (handler != null)
                    handler.onSuccess(photos);
            }

            @Override
            public void onFailure(String error) {
                if (handler != null)
                    handler.onFailure(error);
            }
        });
    }


    @Override
    protected JSONObject deserialize(String responseString) {
        Pattern p = Pattern.compile(".*?\((.*)\)$");
        Matcher m = p.matcher(responseString);
        String json = null;
        if (m.matches()) {
            json = m.group(1);
        }

        JSONObject response = null;
        try {
            response = new JSONObject(json);
        } catch (JSONException e) {
            e.printStackTrace();
        }

        return response;
    }
}

Как видно из кода выше, класс унаследован от класса Model, в котором в свою очередь реализованы примитивные методы общения с сервером (GET, POST, PUT, DELETE запросы). Метод fetch реализует http-метод GET и принимает в качестве первого аргумента параметры запроса, в качестве второго – различные флаги обработки данных и т.д., в качестве третьего – callback интерфейс.

Метод deserialize, как понятно из названия, десериализует полученные данные. Так как Flickr возвращает JSON, обернутый вот в такую бяку: jsonFlickrApi(), то перед десериализацией JSON-объекта регуляркой отбрасываем все лишнее. По остальному, я думаю, тут все понятно.

Теперь реализуем метод, в который непосредственно будет передаваться строка запроса.

    private void searchImages(String query) {        
        HashMap<String, String> params = new HashMap<>();
        flickr = new Flickr(ImageSearchActivity.this);
        if (query != null) {
            params.put("method", "flickr.photos.search");
            params.put("text", query);
        } else {
            params.put("method", "flickr.interestingness.getList");
        }

        flickr.fetch(params, null, new Flickr.FlickrModelResponseHandler() {
            @Override
            public void onSuccess(ArrayList<HashMap<String, String>> responseArray) {
                adapter.setArray(responseArray);
            }

            @Override
            public void onFailure(String error) {
                adapter.setArray(new ArrayList<HashMap<String, String>>());
            }
        });
    }

Тут, если query равен null, то делаем запрос методом flickr.interestingness.getList, иначе — flickr.photos.search c текстом query.

И, напоследок, результат работы.

Осторожно, 10-ти метровая gif-ка

Поиск изображений в Android при помощи Flickr - 5

Автор: biomax

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js