We are writing a widget using the Yandex.Metrica API

    Not so long ago, Yandex.Metrica announced an open API , with which you can access almost all the functions of the Metrica from its own program.
    Today I want to talk a little about the use of this API and how to create a simple widget for Android devices on its basis.


    API Basics


    Yandex.Metrica API is built on the principle of REST. All that can be controlled through the API is represented by a resource: counters, goals, filters, etc. Operations on resources (reading, deleting, changing) are performed by means of HTTP requests to I.Metrica API servers, each type of operation has its own HTTP method:
    • GET: reading content
    • POST: adding a resource
    • PUT: resource change
    • DELETE: delete a resource

    For example, to get a list of counters, you need to refer to the counters: resource GET /counters. To obtain information about a counter, refer to the resource by its identifier: GET /counter/{id}. To delete, refer to the same resource, but using the DELETE: method DELETE /counter/{id}.

    The input for REST calls and the results can be encoded in two formats: XML and JSON. In the future, we will use JSON, as a more compact and convenient format for displaying on structures used in programming languages.

    Naturally, working through the API is possible only with counters, which are accessed from your Y. Metrika account. OAuth authentication is used to identify the account owner. It is arranged very simply. A developer who wants to use the Yandex.Metrica API registers his application (see instructions ) and receives the application identifier. After registering the application, you can get a debugging OAuth token for it and immediately start working with the Metrica API. A debugging token will allow working with counters available from the developer account that registered the application. The token must be transmitted in each HTTP request, either as an additional parameter in the URL ( ), or as a header in the HTTP request ....&oauth_token=Authorization: OAuth

    In order for the application to work with arbitrary counters, and not just those available to the developer, a separate OAuth token is needed for each application user. How can I get it:
    1. The safest way is when a user is redirected by an application to a special Yandex page, logs in to it, and (if he has not done so already) gives the application access to his counters. After that, the user is redirected back to the application, and the URL to which the redirection goes contains the parameter with the OAuth token for this user. The application should read this parameter and remember the token. Details of this procedure are described in the manual .
    2. Applications can also receive a token by directly requesting a username and password from the user, passing them to OAuth to the Yandex server, and receiving the token in response. This method is less secure: login and password are requested and transmitted explicitly.

    Thanks to the use of REST, the simplest operation with the API (read requests) is possible directly from the browser, without any coding. For example, this request will display information about the counter in the demo account of the Ya.Metrica API. Such a request will produce a report on the attendance of the counter.

    Despite the simplicity of the REST interface, for the full use of the API in a program written in a high-level language, you need to make quite a lot of gestures: generating request URLs, sending HTTP requests and receiving results, generating and parsing JSON, error handling, etc. To simplify my life, I created a ready-made library for working with the API I. Metrics in Java: Metrika4j. The library code is distributed under the Apache License, you can freely modify and supplement it to your requirements. Next, I will tell you how to use this library to create a widget for Android devices that displays site traffic.

    Metrika4j


    Metrika4j allows you to work with Yandex.Metrica, operating on familiar Java concepts (classes, function calls, types) and not thinking about low-level work with HTTP and JSON. The central interface of the library is MetrikaApi . It has methods for working with the main entities of the Metric: counters and reports. Work with additional entities (goals, filters, etc.) is made in separate mini-APIs obtained from the main class MetrikaApi.

    The structure of the methods roughly corresponds to the structure of REST calls to the I. Metrica API. The arguments passed to the REST call correspond to the arguments passed to the API methods (except for the reporting API, which we will discuss separately).
    Metrika4j supports out of the box two libraries for working with JSON: Jackson JSON processorand org.json . For Jackson, 100% functionality is supported, for org.json - the minimum sufficient to work with reports: reading a list of counters and receiving reports. The org.json library is built into the Andriod API, so its use is convenient for Android applications. If necessary, the developer can use any other JSON library by implementing the JsonMapper and JsonObject interfaces with it .

    Using Metrika4j

    First you need to create an instance MetrikaApiusing ApiFactory . When creating, you must pass the user OAuth token:

    // Создаем экземпляр API, использующий демо-токен API Метрики и Jackson JSON processor
    MetrikaApi api = ApiFactory.createMetrikaAPI(
        "05dd3dd84ff948fdae2bc4fb91f13e22", new JacksonMapper());


    Further, using the created instance, you can perform any operations:

    // Получаем список счетчиков в текущем аккаунте
     Counter[] myCounters = api.getCounters();
    // Создание счетчика
    Counter newCounter = new Counter();
    newCounter.setSite("mysite.ru");
    newCounter.setName("Мой сайт");
    Counter createdCounter = api.createCounter(newCounter);
    // В createdCounter содержится новый счетчик, загруженный из Метрики, со всем значениями полей,
    // проставленными Метрикой, например Id
    System.out.println(createdCounter.getId());
    // Удаление счетчика
    api.deleteCounter(createdCounter.id);
    


    Work with reports

    Almost all reports existing in the Metric are available through the API. The set of available reports is contained in the Reports class . To build the selected report, you need to call a method MetrikaApi.makeReportBuilder(Reports report, int counterId)that will return a special object - the ReportBuilder report builder . In the report builder, you need to set the required report parameters (time interval, sorting, etc.). When all parameters are set, call the method ReportBuilder.build(), which will send an HTTP request to the Metrics API and return a report.

    // Создаем построитель отчета "популярное содержимое" для счетчика с id=2138128
    ReportBuilder builder = api.makeReportBuilder(Reports.contentPopular, 2138128);
    // Задаём параметры отчета (отчет за неделю) и строим отчет
    Report report = builder.withDateFrom(MetrikaDate.yesterday())
        .withDateTo(MetrikaDate.today())
        .build();
    


    The report is returned as a Report object , which is a table with the results, plus some additional information (totals, the interval of dates to which the report relates, etc.). Each row of the result table is a ReportItem object , data from which can be obtained using one of the methods getXXX(String fieldName)(similar to getting values ​​from ResultSetwhen using JDBC). Field names and returned additional data must be specified for each report in the documentation for the Yandex.Metrica API.

    // Вытаскиваем результаты из отчета
    ReportItem[] items = report.getData();
    for (ReportItem item : items) {
          System.out.printf("pageViews: %4d, url: %s", item.getInt("page_views"), item.getString("url"))
              .println();
    }
    


    A more detailed description of working with Metrika4j can be found in Javadoc .

    Widget for Android


    Having at its disposal such powerful tools as the Yandex.Metrica and Metrika4j APIs, you can solve any problems, up to creating alternative user interfaces to Yandex.Metrica. But within the framework of this article we will confine ourselves to a more modest goal: we will create a widget for Android that will show the current traffic to the site. The source code of the widget is available on GitHub in the MetrikaWidget project . Like the Metrika4j code, it is freely distributed with a minimum of licensing restrictions.

    OAuth authorization

    Let's start with the creation MetrikaApiand authorization. The application works with only one account, so the instance MetrikaApican be made Singleton. Its code is contained in the Globals class .

    private static MetrikaApi api;
    public static synchronized MetrikaApi getApi(Context context) {
        if (api == null) {
            // Получаем сохраненный OAuth token из SharedPreferences
            String token = getOAuthToken(context); 
            if (token == null) {
                throw new AuthException();
            } else {
                // Используем библиотеку org.json, встроенную в Android
                api = ApiFactory.createMetrikaAPI(token, new OrgJsonMapper());
            }
        }
        return api;
    }
    


    If the application is launched for the first time and OAuth token is not yet in SharedPreferences, you need to get it. To do this, go to the token request URL:

    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://oauth.yandex.ru/authorize?response_type=token&client_id=1359488e196b4bfa92615d0885b106d4"));
    startActivity(intent);
    


    For the user, it will look like the opened page "The application is requesting access to your data on Yandex." Assume that the user successfully logged in and allowed access. Then the fun begins: how to transfer the token from the web interface back to our application? To do this, you need to register one of the activities in the application as a handler for a protocol specific to our application (specific so that our handler does not overlap with other applications). We AndroidManifest.xmlindicate the following:



    We registered AuthTokenActivity as the “metwd” protocol handler for the “oauthtoken” pseudo-host. Now it’s enough to specify the Callback URI metwd://oauthtokenin the settings of the registered application , and after successful OAuth authorization, the user will be called AuthTokenActivity. In this activity, you need to parse the received string and save the token in SharedPreferences:

    String fragment = getIntent().getData().getFragment();
    String[] parts = fragment.split("\\&");
    for (String part : parts) {
        if (part.startsWith("access_token=")) {
            String token =  part.substring(param.length(), part.length());
            // Запоминаем полученный токен в preferences
            getSharedPreferences(Globals.PREF_FILE, 0)
                 .edit()
                 .putString(Globals.PREF_TOKEN, token)
                 .commit();
        }
    }
    


    Data retrieval

    The data retrieval code for the widget is extremely simple:

    MetrikaApi api = Globals.getApi(context);
    // Собственно запрос к Metrika API
    Report report = api.makeReportBuilder(Reports.trafficSummary, counterId)
            .withDateFrom(new MetrikaDate())
            .withDateTo(new MetrikaDate())
            .build();
    return new Result(report.getTotals().getInt("visits"));
    


    The nuance is that this code cannot be executed "head-on". Metrica API servers respond fairly quickly, but the HTTP request itself can go to the server and back through slow and unreliable mobile communication channels. As a result, the widget awaiting a response will “freeze” from the point of view of Android OS, and a window will be displayed suggesting that the suspended application crash. This is clearly not what we need. Therefore, all requests to the Metrics API are executed asynchronously using the AsyncTask class . Here is the simplified code for loading the counter list (the full version is in the WidgetSetupActivity class ):

    private class CountersLoadTask extends AsyncTask {
        private ProgressDialog progressDialog;
        protected void onPreExecute() {
            progressDialog = ProgressDialog.show(...);
        }
        protected void onPostExecute(Counter[] counters) {
            progressDialog.dismiss();
            counterList.addAll(Arrays.asList(counters));
            // Уведомляем адаптер, чтобы он перерисовал список счетчиков
            listAdapter.notifyDataSetChanged(); 
        }
        protected Counter[] doInBackground(Void... voids) {
            return Globals.getApi(WidgetSetupActivity.this).getCounters();
        }
    }
    


    For widgets, working with data is a bit more complicated. To begin with, the widget provider that receives the "time to update the widget" events is, in Android terminology, the broadcast receiver . The broadcast receiver's life cycle is short: it processes the event, after which it immediately dies. If you start a thread from the event handler, then receiver-a may die before the thread finishes its work: sadness. The Android developer guide strongly recommends using services for such cases. So let's do it: the widget provider ( MetrikaWidgetProvider ) receives events and passes them for processing to the UpdateService . UpdateService, in turn, uses asynchronous data loading throughAsyncTask (otherwise, we again get the “Application Not Responding” window for a long time waiting for a response from the API).

    Widget display

    Working with widgets in Android is a separate big topic that goes beyond the scope of this article and is well covered both in the Android developer guide and in additional articles. Therefore, I will tell you briefly, but for details I will refer to the source code of the classes MetrikaWidgetProviderand UpdateService.

    A widget can be in three states: “data received”, “data update” and “no communication”. The widget is updated by timer or by clicking on the widget.
    • The data received is the regular state of the widget in which it displays the number of visits on the site for today. In this state, the widget displays the standard bar chart - the "rainbow", which is visible in the header of the Yandex.Metrica interface
    • Data update is an intermediate state made solely for the convenience of the user, so that he receives visual feedback from a click on the widget. The widget goes into it before sending a request to the API Metric, and exits after the request is completed. In this state, an inverted “rainbow” is displayed.
    • No connection - a state indicating that the current data could not be obtained. Displayed as a “rainbow” with reduced saturation, almost gray.


    If the widget can be in the “no connection” state, it would be logical to update it when this connection appears so that the user immediately sees the actual data. To do this, the provider widget subscribes to system events associated with a change in the status of the network interface:

    
        ...
    


    Upon receipt of such an event, a check is made to see if a connection has appeared and an update is called:

    if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
        // Изменилось состояние подключения к Internet
        NetworkInfo info = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
        if (info != null && info.isConnected() && !info.isRoaming()) {
            // Если появилась связь, обновляем виджеты, которые могли быть в состоянии "offline"
            ...
        }
    }
    


    Widget installation


    A widget can be compiled from the source code located in the MetrikaWidget project .

    You are not an Android developer, but want to use a widget? No problem - you can download the finished widget. Release from Android Market or debug build with GitHub .

    image

    After installation, you will also see the “Y. Metrika” application, which is actually a mini-instruction for the widget.

    Also popular now: