Powiadomienia push na platformie Android

266

Chcę napisać aplikację, która odbiera powiadomienia wypychane z serwera. Znalazłem kilka metod, aby to zrobić.

  1. SMS - przechwytuj przychodzące wiadomości SMS i inicjuj pobieranie z serwera
  2. Okresowo odpytuj serwer

Każdy ma swoje ograniczenia. SMS - brak gwarancji na czas przyjazdu. Sonda może wyczerpać baterię.

Czy masz lepszą sugestię? Dziękuję bardzo.

Vinod
źródło
4
Możesz także obejrzeć prezentację Google I / O 2010 na temat powiadomień push developer.android.com/videos/index.html#v=PLM4LajwDVc
vokilam
Myślę, że możesz spojrzeć na ten post: stackoverflow.com/questions/17629942/... Dzięki Worklight możesz otrzymywać push za pośrednictwem różnych kanałów, w tym GCM.
Neeraj Krishna
1
Prezentacja Google I / O 2010 jest dostępna na youtube.com/watch?v=PLM4LajwDVc
elcuco
„Sonda może wyczerpać baterię”. Możesz zaplanować ankietę za pomocą AlarmManager, aby bateria nie była mocno rozładowana. Jest to proste i bezpłatne (nie trzeba płacić, jak w przypadku GCM) rozwiązanie.
Deepscorn,
mają ten sam problem stackoverflow.com/questions/35812304/…
albert

Odpowiedzi:

203

Oficjalną odpowiedzią Google jest system Android Cloud to Device Messaging Framework (wycofany) Google Cloud Messaging (wycofany) Firebase Cloud Messaging

Będzie działać na Androidzie> = 2.2 (na telefonach, które mają Sklep Play).

BoD
źródło
jest obecnie w fazie beta, ale możesz zarejestrować się w nadziei na aktywację.
nawias
3
Zasadniczo możesz aktywować się bardzo szybko i jest on używany do takich rzeczy jak Gmail, więc wiadomo, że działa w produkcji. Niestety brakuje ich przykładowego kodu do komunikacji z aspektem C2DM po stronie serwera. Napisałem samouczek dla tego aspektu tutaj blog.boxedice.com/2010/10/07/…
DavidM
6
Problem polega na tym, że potrzebujesz konta Google dla swoich użytkowników: co jest, moim zdaniem, ograniczeniem.
kofeina
33
Pamiętaj, że system Android Cloud to Device Messaging Framework jest przestarzały. Nowa platforma nosi nazwę Google Cloud Messaging i można ją znaleźć tutaj: developer.android.com/guide/google/gcm/index.html
Ryan Berger
10 kwietnia 2018 r. Google wycofało GCM. Interfejsy API serwera i klienta GCM zostały usunięte 29 maja 2019 r. Migruj aplikacje GCM do Firebase Cloud Messaging (FCM), który dziedziczy niezawodną i skalowalną infrastrukturę GCM oraz wiele nowych funkcji. Więcej informacji znajdziesz w przewodniku migracji.
paiego
29

(przesłanie krzyżowe z odpowiedzi, której udzieliłem na podobne pytanie - Czy system Android obsługuje powiadomienia push w czasie rzeczywistym? )

Niedawno zacząłem grać w MQTT http://mqtt.org dla Androida jako sposób na zrobienie czegoś takiego (np. Powiadomienie push, które nie jest SMS-em, ale sterowane danymi, prawie natychmiastowe dostarczanie wiadomości, nie odpytywanie itp.)

Mam wpis na blogu z podstawowymi informacjami na ten temat, na wypadek, gdyby był pomocny

http://dalelane.co.uk/blog/?p=938

(Uwaga: MQTT jest technologią IBM i powinienem zauważyć, że pracuję dla IBM.)

dalelane
źródło
Cześć Dale, przeczytałem twój wpis na blogu o MQTT i zdecydowanie wydaje się on pasować do rachunku za prawie natychmiastowe powiadomienia na telefonach komórkowych. Ale nie udało mi się znaleźć żadnych informacji na temat tego, jak to właściwie robi. Czy utrzymuje gniazdo zawsze otwarte? Jak powiadamia serwer o zmianie adresu IP? Byłbym wdzięczny, gdybyś mógł rzucić na to trochę światła. Na zdrowie Naren
Naren
2
Utrzymuje otwarte połączenie. W kolejnym poście ( dalelane.co.uk/blog/?p=1009 ) mówiłem więcej o konsekwencjach utrzymywania otwartego połączenia - widziałeś to? Jeśli połączenie zostanie zerwane, zarówno serwer, jak i klient mogą zostać powiadomieni. Następnie decyzja o tym, jak odpowiedzieć, zależy od warstwy aplikacji (np. Ponownie połączyć). Więcej informacji znajduje się w dokumentach, o których mowa w poście (np. IA92: www-01.ibm.com/support/docview.wss?rs=171&uid=swg24006006 pdf na tej stronie i Javadoc na zipie na tej stronie)
dalelane,
18

Moje zrozumienie / doświadczenie z powiadomieniami push systemu Android to:

  1. C2DM GCM - jeśli docelowa platforma Android to 2.2+, wybierz ją. Tylko jeden haczyk, użytkownicy urządzeń muszą być zawsze zalogowani za pomocą konta Google, aby otrzymywać wiadomości.

  2. MQTT - podejście oparte na pub / sub, wymaga aktywnego połączenia z urządzeniem, może rozładować baterię, jeśli nie zostanie rozsądnie wdrożone.

  3. Diakon - może nie być dobry na dłuższą metę ze względu na ograniczone wsparcie społeczności.

Edycja : Dodano 25 listopada 2013 r

GCM - Google mówi ...

W przypadku urządzeń starszych niż 3.0 wymaga to od użytkowników skonfigurowania konta Google na urządzeniach mobilnych. Konto Google nie jest wymagane na urządzeniach z Androidem 4.0.4 lub nowszym. *

Lalit
źródło
Konto Google nie jest wymagane w wersji 4.0.4 lub nowszej, ale wymaga zainstalowania aplikacji Google Play. Zastanawiam się, jak to zainstalować bez posiadania konta Google.
Jan
1
@ Jan: Aplikacja Google Play jest instalowana z urządzeniem. Użytkownik nie musi instalować.
Dexter,
@Dexter, nie wszystkie urządzenia z Androidem mają domyślnie zainstalowaną Google Play. Większość z nich ma zainstalowaną domyślnie, zwłaszcza urządzenia zakupione od renomowanych dostawców telefonów, ale urządzenia, które mają tylko flashowany system operacyjny Android, nie zawsze mają Google Play. (Na przykład wiele emulatorów Androida, takich jak nowe urządzenia Genymotion, domyślnie nie ma Google Play.)
Spencer D
Więc ... Czy MQTT jest najlepszą opcją dla urządzeń z Androidem, które nie mają zainstalowanej Google Play?
Yeung
17

Android Cloud to Device Messaging Framework

Ważne: C2DM zostało oficjalnie wycofane 26 czerwca 2012 r. Oznacza to, że C2DM przestał akceptować nowych użytkowników i żądania przydziału. Żadne nowe funkcje nie zostaną dodane do C2DM. Aplikacje korzystające z C2DM będą jednak nadal działać. Zachęcamy istniejących programistów C2DM do migracji do nowej wersji C2DM, zwanej Google Cloud Messaging dla Androida (GCM). Więcej informacji można znaleźć w dokumencie migracji C2DM-to-GCM. Programiści muszą korzystać z GCM do nowego programowania.

Prosimy sprawdzić następujący link:

http://developer.android.com/guide/google/gcm/index.html

chiranjib
źródło
17

Tutaj napisałem kilka kroków, jak uzyskać RegID i powiadomienie, zaczynając od zera

  1. Utwórz / zarejestruj aplikację w Google Cloud
  2. Skonfiguruj Cloud SDK z programowaniem
  3. Skonfiguruj projekt dla GCM
  4. Uzyskaj identyfikator rejestracji urządzenia
  5. Wyślij powiadomienia push
  6. Otrzymuj powiadomienia push

Kompletny samouczek można znaleźć w poniższym linku URL

Pierwsze kroki z powiadomieniem push w systemie Android: najnowsza usługa Google Cloud Messaging (GCM) - samouczek krok po kroku

wprowadź opis zdjęcia tutaj

Wycinek kodu, aby uzyskać identyfikator rejestracji (token urządzenia dla powiadomienia wypychanego).

Skonfiguruj projekt dla GCM


Zaktualizuj plik AndroidManifest

Aby włączyć GCM w naszym projekcie, musimy dodać kilka uprawnień do naszego pliku manifestu Przejdź do AndroidManifest.xml i dodaj poniższy kod Dodaj zezwolenie

<uses-permission android:name="android.permission.INTERNET”/>
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

<uses-permission android:name="android.permission.VIBRATE" />

<uses-permission android:name=“.permission.RECEIVE" />
<uses-permission android:name=“<your_package_name_here>.permission.C2D_MESSAGE" />
<permission android:name=“<your_package_name_here>.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />

Dodaj deklarację GCM Broadcast Receiver

dodaj deklarację GCM Broadcast Receiver do tagu aplikacji

<application
        <receiver
            android:name=".GcmBroadcastReceiver"
            android:permission="com.google.android.c2dm.permission.SEND" ]]>
            <intent-filter]]>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <category android:name="" />
            </intent-filter]]>

        </receiver]]>
     
<application/>

Dodaj deklarację GCM Servie

<application
     <service android:name=".GcmIntentService" />
<application/>

Uzyskaj identyfikator rejestracji (token urządzenia dla powiadomienia push)

Teraz przejdź do swojej aktywności związanej z uruchomieniem / powitaniem

Dodaj stałe i zmienne klas

private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
public static final String EXTRA_MESSAGE = "message";
public static final String PROPERTY_REG_ID = "registration_id";
private static final String PROPERTY_APP_VERSION = "appVersion";
private final static String TAG = "LaunchActivity";
protected String SENDER_ID = "Your_sender_id";
private GoogleCloudMessaging gcm =null;
private String regid = null;
private Context context= null;

Zaktualizuj metody OnCreate i OnResume

@Override
protected void onCreate(Bundle savedInstanceState)
{
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_launch);
     context = getApplicationContext();
         if (checkPlayServices()) 
     {
            gcm = GoogleCloudMessaging.getInstance(this);
            regid = getRegistrationId(context);

            if (regid.isEmpty())
            {
                registerInBackground();
            }
            else
            {
            Log.d(TAG, "No valid Google Play Services APK found.");
            }
      }
 }

@Override protected void onResume()
{
       super.onResume();       checkPlayServices();
}


# Implement GCM Required methods (Add below methods in LaunchActivity)

private boolean checkPlayServices() {
        int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
        if (resultCode != ConnectionResult.SUCCESS) {
            if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
                GooglePlayServicesUtil.getErrorDialog(resultCode, this,
                        PLAY_SERVICES_RESOLUTION_REQUEST).show();
            } else {
                Log.d(TAG, "This device is not supported - Google Play Services.");
                finish();
            }
            return false;
        }
        return true;
 }

private String getRegistrationId(Context context) 
{
   final SharedPreferences prefs = getGCMPreferences(context);
   String registrationId = prefs.getString(PROPERTY_REG_ID, "");
   if (registrationId.isEmpty()) {
       Log.d(TAG, "Registration ID not found.");
       return "";
   }
   int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE);
   int currentVersion = getAppVersion(context);
   if (registeredVersion != currentVersion) {
        Log.d(TAG, "App version changed.");
        return "";
    }
    return registrationId;
}

private SharedPreferences getGCMPreferences(Context context) 
{
    return getSharedPreferences(LaunchActivity.class.getSimpleName(),
                Context.MODE_PRIVATE);
}

private static int getAppVersion(Context context) 
{
     try 
     {
         PackageInfo packageInfo = context.getPackageManager()
                    .getPackageInfo(context.getPackageName(), 0);
            return packageInfo.versionCode;
      } 
      catch (NameNotFoundException e) 
      {
            throw new RuntimeException("Could not get package name: " + e);
      }
}


private void registerInBackground() 
{     new AsyncTask() {
     Override
     protected Object doInBackground(Object... params) 
     {
          String msg = "";
          try 
          {
               if (gcm == null) 
               {
                        gcm = GoogleCloudMessaging.getInstance(context);
               }
               regid = gcm.register(SENDER_ID);               Log.d(TAG, "########################################");
               Log.d(TAG, "Current Device's Registration ID is: "+msg);     
          } 
          catch (IOException ex) 
          {
              msg = "Error :" + ex.getMessage();
          }
          return null;
     }     protected void onPostExecute(Object result) 
     { //to do here };
  }.execute(null, null, null);
}

Uwaga : przechowuj REGISTRATION_KEY, ważne jest, aby wysłać wiadomość PN do GCM, zachowaj również w moim, będzie to unikatowe dla wszystkich urządzeń, używając tego tylko GCM wyśle ​​powiadomienie push.

Otrzymuj powiadomienia push

Dodaj GCM Broadcast Receiver Class

Jak już zadeklarowaliśmy „GcmBroadcastReceiver.java” w naszym pliku manifestu, stwórzmy w ten sposób ten kod klasy aktualizacji odbiornika klasy

public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) 
    {        ComponentName comp = new ComponentName(context.getPackageName(),
                GcmIntentService.class.getName());        startWakefulService(context, (intent.setComponent(comp)));
        setResultCode(Activity.RESULT_OK);
        Toast.makeText(context, wow!! received new push notification", Toast.LENGTH_LONG).show();
    }
}

Dodaj klasę usługi GCM

Jak już zadeklarowaliśmy „GcmBroadcastReceiver.java” w naszym pliku manifestu, stwórzmy w ten sposób ten kod klasy aktualizacji odbiornika klasy

public class GcmIntentService extends IntentService
{     public static final int NOTIFICATION_ID = 1;     private NotificationManager mNotificationManager;     private final static String TAG = "GcmIntentService";     public GcmIntentService() {
     super("GcmIntentService");     
     }     @Override
     protected void onHandleIntent(Intent intent) {
          Bundle extras = intent.getExtras();
          Log.d(TAG, "Notification Data Json :" + extras.getString("message"));

          GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
          String messageType = gcm.getMessageType(intent);          if (!extras.isEmpty()) {          if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR
               .equals(messageType)) {
               sendNotification("Send error: " + extras.toString());
          } else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED
          .equals(messageType)) {
          sendNotification("Deleted messages on server: "
          + extras.toString());          // If it's a regular GCM message, do some work.
          } else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE
          .equals(messageType)) {
          // This loop represents the service doing some work.
          for (int i = 0; i < 5; i++) {
               Log.d(TAG," Working... " + (i + 1) + "/5 @ "
               + SystemClock.elapsedRealtime());               try {
                    Thread.sleep(5000);
               } catch (InterruptedException e) {
               }
             }
             Log.i(TAG, "Completed work @ " + SystemClock.elapsedRealtime());
             sendNotification(extras.getString("message"));
           }
        }        // Release the wake lock provided by the WakefulBroadcastReceiver.
        GcmBroadcastReceiver.completeWakefulIntent(intent);
     }     // Put the message into a notification and post it.
     // This is just one simple example of what you might choose to do with
     // a GCM message.
     private void sendNotification(String msg) {          mNotificationManager = (NotificationManager) this
          .getSystemService(Context.NOTIFICATION_SERVICE);
          PendingIntent contentIntent = PendingIntent.getActivity(this, 0,          new Intent(this, LaunchActivity.class), 0);

          NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(          this)
          .setSmallIcon(R.drawable.icon)
          .setContentTitle("Ocutag Snap")
          .setStyle(new NotificationCompat.BigTextStyle().bigText(msg))
          .setContentText(msg)
          .setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE);

          mBuilder.setContentIntent(contentIntent);          mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
     }
}
swiftBoy
źródło
@Rohit sprawdziłeś link po ukończeniu samouczka. proszę również dać mi znać, czego brakuje. Zaktualizuję odpowiedź.
swiftBoy
11

Podjęto nowy wysiłek typu open source, aby opracować bibliotekę Java do powiadomień wypychanych w systemie Android w oparciu o serwer internetowy Meteor. Możesz to sprawdzić na blogu projektu Deacon , gdzie znajdziesz linki do Meteora i repozytorium GitHub projektu. Potrzebujemy programistów, więc proszę, powiedz nam!

mtbkrdave
źródło
9

Możesz użyć Xtify ( http://developer.xtify.com ) - mają one usługę powiadomień push, która działa z ich pakietem SDK. jest darmowy i do tej pory działał dla mnie naprawdę dobrze.

Piotr
źródło
3
Otrzymałem odpowiedź od ich wiceprezesa, która powiedziała, że ​​nie ma żadnych planów pobierania opłat za usługę push. To całkiem niesamowity zestaw SDK.
Crowe T. Robot
8

lub....

3) Utrzymuj połączenie z serwerem, wysyłaj połączenia co kilka minut, a serwer może natychmiast przekazywać wiadomości. Tak działa Gmail, Google Talk itp.

Isaac Waller
źródło
5
Myślę, że spowoduje to niestety znaczne zużycie energii akumulatora. JEŻELI wybierzesz tę trasę, pamiętaj o ograniczeniu czasu.
haseman
Nie, tak się nie stanie, ponieważ bezczynne połączenia TCP / IP prawie nie pobierają mocy przez modem komórkowy.
Isaac Waller
W rzeczywistości zajmie to dużo czasu, więc masz rację. Bardzo pomaga też wysyłanie podtrzymujących życie z długimi przerwami.
Isaac Waller
Jakie są „długie odstępy” w tym zakresie? Wiem, że push Gmaila działa w ten sposób, ale nie wiem, jakich interwałów używają.
tobsen,
Znalazłem ten obraz o wydajności baterii Pull vs Push: labs.ericsson.com/files/battery.jpg Wziąłem go z interfejsu API Ericsson Push tutaj: labs.ericsson.com/apis/mobile-java-push/documentation
Menda
6

Polecam korzystanie z GCM - Google Cloud Messaging dla Androida To nic nie kosztuje, a dla prostych zastosowań powinno być bardzo łatwe.

Wymaga to jednak utrzymywania serwera strony trzeciej w celu wysyłania powiadomień w Twoim imieniu. Jeśli chcesz tego uniknąć, istnieje kilka bardzo dobrych rozwiązań przemysłowych dla usługi powiadomień push Android:

  • Urban Airship - bezpłatne powiadomienia do 1 mln miesięcznie, następnie naliczana jest opłata za 1000 powiadomień
  • PushApps - bezpłatne dla 1M powiadomień miesięcznie i nieograniczone powiadomienia dla 19,99 miesięcznie
  • PushWoosh - bezpłatny dla urządzeń 1M, plany premium kosztują od 39 EURO

Diclaimer - pracuję w PushApps, a także używam ich produktów w moich aplikacjach od ponad roku.

Orr
źródło
6

Od 18.05.2016 Firebase to zunifikowana platforma Google dla programistów mobilnych, w tym powiadomienia push.

Freek Nortier
źródło
4

Obawiam się, że znalazłeś obie możliwe metody. Google przynajmniej na początku zamierzał wdrożyć interfejs GChat, którego można użyć do wdrożenia push / pull. Niestety ta biblioteka została wycięta przez system Android 1.0.

Haseman
źródło
1
Obiecali przywrócić to, gdy problemy bezpieczeństwa zostaną rozwiązane ... jeśli tak się stanie.
Jeremy Logan,
3

Nie wiem, czy to nadal jest przydatne. Osiągnąłem coś takiego z biblioteką Java na http://www.pushlets.com/

Althoug robi to w usłudze, nie uniemożliwi androidowi wyłączenia go i zabicia wątku słuchacza.

n3utrino
źródło
2

C2DM: użytkownicy aplikacji muszą mieć konto Gmail.

MQTT: gdy połączenie osiągnie 1024, przestanie działać, ponieważ użył „wybierz model” linuxa.

Istnieje bezpłatna usługa push i API dla Androida, możesz spróbować: http://push-notification.org

Jacky
źródło
2

Darmowa i łatwa metoda:

Jeśli docelowa baza użytkowników nie jest duża (mniej niż 1000) i chcesz rozpocząć bezpłatną usługę, Airbop jest najlepszy i najwygodniejszy.

Witryna Airbop Korzysta z usługi Google Cloud Messaging za pośrednictwem interfejsu API i zapewnia dobrą wydajność. Użyłem go do dwóch moich projektów i jego wdrożenie było łatwe.

Usługi takie jak Urbanship są doskonałe, ale zapewniają cały stos wdrażania, a nie tylko powiadomienia push.

Jeśli Twoim celem jest tylko usługa push, Airbop będzie działał dobrze.

Nie korzystałem z Pushwoosh , ale to także świetny wybór. Umożliwia push do 1 000 000 urządzeń za darmo

adimoh
źródło
1

Sugerowałbym używanie zarówno SMS, jak i HTTP. Jeśli użytkownik nie jest zalogowany, wyślij SMS na telefon, aby powiadomić go, że czeka wiadomość.

Tak działa ta usługa Ericsson Labs: https://labs.ericsson.com/apis/mobile-java-push/

Jeśli sam to zaimplementujesz, trudną częścią jest usunięcie przychodzącej wiadomości SMS, tak aby użytkownik jej nie widział. A może ok, jeśli zobaczą to w twoim przypadku.

Wygląda na to, że to działa: Usuwanie wiadomości SMS za pomocą BroadCastReceiver - Android

Tak, pisanie takiego kodu może być niebezpieczne i możesz zrujnować czyjeś życie, ponieważ Twoja aplikacja usunęła SMS-a, którego nie powinna mieć.

Sarel Botha
źródło
1
lol: „Tak, pisanie takiego kodu może być niebezpieczne i możesz zniszczyć czyjeś życie, ponieważ Twoja aplikacja usunęła SMS-a, którego nie powinna mieć.” głosuj za to. lol.
Kumar Ravi
Link do Mobile Java Push jest martwy, a sama usługa wydaje się przestarzała.
naXa
1

Możesz używać Google Cloud Messaging lub GCM , jest bezpłatny i łatwy w użyciu. Możesz także korzystać z zewnętrznych serwerów push, takich jak PushWoosh, co zapewnia większą elastyczność

Mohsen Afshin
źródło
1

Istnieje wiele serwerów innych firm, takich jak Urban Airship , Xtify, Mainline , ... które pozwalają na wysyłanie nie tylko na Androida, ale także na iOS, Windows Phone ...

Panchine Lac
źródło
1

Firebase Cloud Messaging (FCM) to nowa wersja GCM. FCM to wieloplatformowe rozwiązanie do przesyłania wiadomości, które umożliwia bezpieczne i bezpłatne wysyłanie wiadomości. Dziedziczy centralną infrastrukturę GCM w celu niezawodnego dostarczania wiadomości w Androidzie, iOS, Internecie (javascript), Unity i C ++.

Z dniem 10 kwietnia 2018 r. Google odrzucił GCM. Interfejsy API serwera i klienta GCM są przestarzałe i zostaną usunięte 11 kwietnia 2019 r. Google zaleca migrację aplikacji GCM do Firebase Cloud Messaging (FCM), która dziedziczy niezawodną i skalowalną infrastrukturę GCM.

Ratunek

Fahed Hermoza
źródło
0

Możesz użyć Popychacza

Jest to usługa hostowana, która sprawia, że ​​bardzo łatwe jest dodawanie danych i funkcji w czasie rzeczywistym do aplikacji internetowych i mobilnych.
Pusher oferuje biblioteki do integracji ze wszystkimi głównymi środowiskami wykonawczymi i strukturami.

PHP, Ruby, Python, Java, .NET, Go and Nodena serwerze
JavaScript, Objective-C (iOS) and Java (Android)na kliencie.

Arash Hatami
źródło