Встраиваем локальные уведомления

    Что самое ужасное в случае удаления приложения? Правильно, потеря связи с аудиторией и невозможность проинформировать о том, что у приложения сменился адрес прописки и имя пакета. Задачу решают Push уведомления, но это довольно хлопотно и не всегда удобно. А иногда и дорого.



    Поэтому мы напишем свои, простые как барабан и надежные как танк — локальные push уведомления. Код получился универсальным, и в принципе, его можно использовать как альтернативу обычным «пушам», например для новостной рассылки о том, что вышла новая версия приложения или для клянчанья рейтинга, да для чего угодно.



    Начнем с конфига. Это обычный текстовый файл в json-формате.

    {
        "notifications":
            [
            {
                "id": 1 ,
                "title": "Sorry we are deleted from GPlay" ,
                "text": "Please click for download new app" ,
                "version": 105 ,
                "action": "market://search?q=freemp" ,
                "locale": "ru_Ru"
            }
            ] 
    } 
    


    где
    — id: unique number of message
    — title: title of message
    — text: text of message
    — version: if not set message for all (optional)
    — action: default action (optional)
    — locale: optional

    Данный файл размещаем, например на гитхабе и добавляем на него ссылку:

    public static final String MESSAGEURL = «github.com/recoilme/freemp/blob/master/message.json?raw=true»;

    Затем где нибудь в районе onCreate основного активити вызываем:
    new UpdateUtils(activity);

    Готово. С настройкой покончено. Теперь как это работает.

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

    Существуют два вида фильтров:
    version — если задать, например 105 — то будет показываться только в 105 версии приложения. Можно, например, написать для пользователей версии 105, что вышла версия 106)
    locale — локаль текста, если например необходимо сделать сообщение на русском, для русскоязычных пользователей

    Параметр action — опциональный, это Uri, который будет вызван при клике на уведомлении. Можно, например, направить в маркет ну или в другое активити (в том числе свое).

    Собственно это все. Довольно просто, и в то же время позволит нивелировать ущерб от возможного удаления. Ну или наладить диалог с пользователями.

    Код, в действии можно посмотреть тут: https://github.com/recoilme/freemp
    Но собственно пока это один файл. Возможно библиотека будет развиваться, добавлю возможность скачивания обновления или что то вроде того. Пока же код комфортно поместится и тут:

    package org.freemp.android;
    
    import android.app.Activity;
    import android.app.Notification;
    import android.app.NotificationManager;
    import android.app.PendingIntent;
    import android.content.Context;
    import android.content.Intent;
    import android.content.pm.PackageManager;
    import android.net.Uri;
    import android.os.AsyncTask;
    import android.preference.PreferenceManager;
    import android.support.v4.app.NotificationCompat;
    import android.text.TextUtils;
    import org.apache.http.HttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.impl.client.DefaultHttpClient;
    import org.json.JSONArray;
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import java.io.BufferedReader;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.util.Locale;
    
    /**
     * Created by recoil on 06.08.14.
     * example file:
    
     {
     "notifications": [{ "id":1, "title":"Sorry we are deleted from GPlay", "text":"Please click for download new app",
     "version":105 , "action":"market://search?q=freemp", "locale":"ru_Ru"}]
     }
    
      - id: unique number of message
      - title: title of message
      - text: text of message
      - version: if not set message for all (may be not set)
      - action: default action (may be not set)
      - locale: may be not set
    
     */
    
    public class UpdateUtils {
    
        public static final String MESSAGEURL = "https://github.com/recoilme/freemp/blob/master/message.json?raw=true";
        private Context context;
        private Activity activity;
        private int versionCode;
        private String locale;
    
        public UpdateUtils(Activity activity) {
            this.activity = activity;
            context = activity.getApplicationContext();
            new Update().execute();
        }
    
        private class Update extends AsyncTask<Void,Void,String> {
    
            @Override
            protected String doInBackground(Void... params) {
                try {
                    versionCode = context.getPackageManager()
                            .getPackageInfo(context.getPackageName(), 0).versionCode;
                } catch (PackageManager.NameNotFoundException e) {
                    e.printStackTrace();
                }
                locale = Locale.getDefault().toString();
                String response = "";
                DefaultHttpClient client = new DefaultHttpClient();
                HttpGet httpGet = new HttpGet(MESSAGEURL);
                try {
                    HttpResponse httpResponse = client.execute(httpGet);
                    InputStream content = httpResponse.getEntity().getContent();
                    BufferedReader buffer = new BufferedReader(new InputStreamReader(content));
                    String s = "";
                    while ((s = buffer.readLine()) != null) {
                        response += s;
                    }
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
                return response;
            }
    
            @Override
            protected void onPostExecute(String result) {
                if (!TextUtils.equals("",result)) {
                    JSONObject jsonResult = null;
                    try {
                        jsonResult = new JSONObject(result);
                    }
                    catch (JSONException e) {
                        e.printStackTrace();
                        return;
                    }
                    //process notifications if exists
                    JSONArray notifications = jsonResult.optJSONArray("notifications");
                    if (notifications==null) {
                        return;
                    }
    
                    if (context==null) {
                        return;
                    }
                    //string with showed messages
                    String showedMessages = PreferenceManager.getDefaultSharedPreferences(context).getString(MESSAGEURL,"");
                    for (int i=0;i<notifications.length();i++) {
                        JSONObject jsonNotification = notifications.optJSONObject(i);
    
                        if (jsonNotification==null) break;
    
                        final int version = jsonNotification.optInt("version",-1);
                        if (version>0 && version!=versionCode) {
                            continue;
                        }
    
                        final String localeTarget = jsonNotification.optString("locale","all");
                        if (!TextUtils.equals("all",localeTarget) && !TextUtils.equals(localeTarget,locale)) {
                            continue;
                        }
    
                        final int id = jsonNotification.optInt("id");
                        if (showedMessages.contains(id+";")) {
                            continue;
                        }
                        else {
                            showedMessages+=id+";";
                            PreferenceManager.getDefaultSharedPreferences(context).edit().putString(MESSAGEURL,showedMessages).commit();
    
                            Intent intent = null;
                            if (!TextUtils.equals("",jsonNotification.optString("action",""))) {
                                // if has action add it
                                intent = new Intent(Intent.ACTION_VIEW, Uri.parse(
                                        jsonNotification.optString("action","")));
                                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    
                            }
                            else {
                                // if no - just inform
                                intent = new Intent(activity,activity.getClass());
                            }
                            PendingIntent pIntent = PendingIntent.getActivity(context, id, intent, 0);
    
                            // if you don't use support library, change NotificationCompat on Notification
                            Notification noti = new NotificationCompat.Builder(context)
                                    .setContentTitle(jsonNotification.optString("title",""))
                                    .setContentText(jsonNotification.optString("text",""))
                                    .setSmallIcon(R.drawable.icon)//change this on your icon
                                    .setContentIntent(pIntent).build();
                            NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
                            // hide the notification after its selected
                            noti.flags |= Notification.FLAG_AUTO_CANCEL;
    
                            notificationManager.notify(id, noti);
                            break;
                        }
                    }
    
                }
            }
    
        }
    }
    
    


    Буду рад если никому не пригодится использовать ее в тех целях, для которых она создавалась)
    FreeAmp
    Company
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 14

      +5
      Это не «push» — это обычный «pull»
      0
      Спасибо за пример )
      Я, чтобы решить эту проблему сделал «ручное» обновление и WebView при старте
      Скриншот

      Обязательно сделаю, как у вас
        0
        Для такого «небогатого» контента вполне можно было обойтись textview
        public TextView getTextView(Context context,String txt){
                TextView textView = new TextView(activity);
                textView.setMovementMethod(LinkMovementMethod.getInstance());
                textView.setText(Html.fromHtml(txt));
                if (itsTablet) {
                    textView.setTextAppearance(activity, android.R.style.TextAppearance_Large);
                }
                else {
                    textView.setTextAppearance(activity, android.R.style.TextAppearance_Medium);
                }
                textView.setPadding(UtilsScreen.dpToPx(8),0,UtilsScreen.dpToPx(8),0);
                textView.setAutoLinkMask(Linkify.ALL);
                textView.setLineSpacing(0, 1.4f);
                ColorStateList cl = null;
                try {
                    XmlResourceParser xpp = context.getResources().getXml(R.color.textview_link_color_selector);
                    cl = ColorStateList.createFromXml(context.getResources(), xpp);
                    textView.setLinkTextColor(cl);
                } catch (Exception e) {
                    textView.setLinkTextColor(Color.parseColor("#6fb304"));
                }
        
                return textView;
            }
        
          0
          Ну это он пока небогатый )
          Спасибо за хороший пост, удачи вашему плееру.
          –1
          после слова «теперь» не нужна запятая
          +3
          Вы серьезно? У вас же здесь самая настоящая утечка при сворачивании приложения, переходе к следующему экрану или повороте экрана.
            0
            Я поправил на weakreference, простите мою лень^_^
            Заодно добавив возможность автообновлений немношк
            0
            «Что самое ужасное в случае удаления приложения? Правильно, потеря связи с аудиторией и невозможность проинформировать о...»

            Извините, я правильно понимаю, что пользователь удаляет приложение для разрыва связи с разработкой?
              +2
              Скорее всего имеется ввиду удаление приложения с Маркета.
              +1
              КДПВ — конечно доставило. Подумал что автор нашел способ посылать push пользователям которые удалили приложения с устройства.
                0
                А если нужно создать свой собственный сервис для push-уведомлений, единственный выход — поднимать свой собственный сокет-сервер (например, веб-сокет), а в приложении запускать сервис для фонового подключения к сервису, верно?
                  +2
                  если не использовать инфраструктуру гугла, то да, все свое придется писать
                  0
                  Из-за таких вот гениальных идей разработчиков у меня батарейка садится.
                  Что будет когда каждые 5 минут 20 приложений проверят, не удалили ли их из маркета?

                  Пользуйтесь пушами, не срите в корень sd-карты не висите сервисом в бекраунде с ежесекундными апдейтами.
                  И да прибудет с вами Сила.

                  Only users with full accounts can post comments. Log in, please.