Ова е гостински текст на Горјан Јовановски првично објавен на неговиот блог на Medium. Горјан е креатор на апликацијата Мој Воздух (AirCare), активист за заштита на природата, TED-говорник и поранешeн Team Lead во Booking.com. Денес Горјан е ИТ консултант, тренер на успешни тимови и јавен говорник.
Го сакам мојот Android. Можеби го сакам и премногу, па кога купив iPhone и Apple Watch за мојот стартап да може да развива повеќе апликации на нив, не можев да се префрлам. Apple Watch е одличен, но навикнат на отвореноста на Android, па ми недостасуваат „power“-функционалности на iPhone.
Накратко, ми се допаѓа Apple Watch, но не ми се допаѓаше што не можам да го користам со Android.
Секако има неколку самостојни апликации, но добивањето на нотификации е функционалноста која ми е потребна. Мојата мисија беше да го остварам ова. И ми успеа! По три дена обиди и тестови, на мојот Apple часовник пристигаат нотификациите од мојот Pixel телефон. Сакате да дознаете како, или можеби самите да се обидете? Продолжете да читате!
Дисклејмер: За овој туторијал треба да познавате технологија, да знаете основи на развој на апликации, да имате Apple Developer Account и да можете да развивате на Xcode.
Потребни алатки
- Android: Android Studio, Firebase account
- Server: PHP сервер по ваш избор (Јас користам Digital Ocean), Composer, Firebase PHP SDK, Simple DOM Parser (не е неопходен)
- Apple: Xcode, Mac, Apple Developer account (платен)
Потребно време: 1 час, ако сте запознаени со развој на апликации и сервер
Концепт
Идејата е едноставна: По WatchOS 6.0, може да развиваме самостојни апликации за Apple Watch за кои не е потребен iPhone за да работат.
- Ќе креираме Android апликација која ќе има Notification Listener Service, и за секоја нотификација ќе ги испраќаме title и body до нашиот PHP сервер.
- На нашиот сервер ќе креираме куса PHP скрипта која очекува повици од нашата Android апликација. Го користи името на пакетот од апликацијата што ја испраќа нотификацијата, за да ја пронајде нејзината слика од Play Store. Потоа со помош на Firebase SDK ќе ја препрати нотификацијата на одреден „topic“, т.е. канал, кој нашиот часовник го следи.
- Ќе креираме самостојна WatchOS апликација со интегриран Firebase Messaging, која слуша на специфични топици (topic) на кои нашиот сервер испраќа пораки. За ова да функционира часовникот треба да биде поврзан на WiFi/4G мрежа.
Штом некоја нотификација пристига на Android, го повикуваме серверот, кој го повикува Firebase, и оттука нашата порака пристига на WatchOS апликацијата.
Прв чекор: Android апликација
Ако имате искуство со развој на апликации за Android ова ќе биде наједноставен дел од овој туторијал. Креирате basic activity, потоа креирате NotificationListenerService.
Внимавајте да ја замените baseURL променливата со линк до вашиот .php фајл на серверот откако ќе го креирате во чекор 2.
public class MyService extends NotificationListenerService{
LinkedList<String> sentBodies;
@Override
public void onCreate() {
super.onCreate();
sentBodies = new LinkedList<>();
System.out.println("********* SERVICE STARTED ***********");
}
//Use this function if you want the app's name
public String getAppName(String packageName){
final PackageManager pm = getApplicationContext().getPackageManager();
ApplicationInfo ai;
try {
ai = pm.getApplicationInfo(packageName, 0);
} catch (final PackageManager.NameNotFoundException e) {
ai = null;
}
return (String) (ai != null ? pm.getApplicationLabel(ai) : "(unknown)");
}
public void sendNotification(String title, String body, String packageName) throws Exception{
RequestQueue queue = Volley.newRequestQueue(this);
title = URLEncoder.encode(title, "utf-8");
body = URLEncoder.encode(body, "utf-8");
String baseURL = ""; // <--- Insert URL to your server's script here
String fullURL = baseURL + "?title="+title+"&body="+body+"&packageName="+packageName;
StringRequest stringRequest = new StringRequest(Request.Method.GET, fullURL,
response -> {Log.v("moj", "Success, server pinged.");}, error -> {Log.v("moj", "Failure, could not ping server: " + error.getMessage());});
queue.add(stringRequest);
}
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
Log.v("ANB","***** Notification received *****");
String pack,title,text;
Bundle extras;
if(sbn.isOngoing()){
return;
}
try {
pack = sbn.getPackageName();
extras = sbn.getNotification().extras;
title = extras.getString("android.title");
text = extras.getCharSequence("android.text").toString();
String notificationTitle = title;
String notificationBody = text;
if(sentBodies.contains(notificationBody)){
return;
}
sentBodies.add(notificationBody);
sendNotification(notificationTitle, notificationBody, pack);
}catch (Exception e)
{
Log.v("ANB","***** notification error *****");
Log.v("ANB", Objects.requireNonNull(e.getMessage()));
}
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
Дефинирајте „listener“ во Android Manifest во <application> тагот на следниот начин:
<!-- rest of manifest -->
<service android:name=".MyService"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
<!-- rest of manifest -->
Конечно стартувајте го сервисот во Main Activity.
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(MainActivity.this,MyService.class));
//Optional, add a button to your layout that will open the Noficiation Access settings, where you can start or stop the listener
Button startButton = (Button) findViewById(R.id.startButton);
startButton.setOnClickListener(v -> {
Intent intentS = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
startActivity(intentS);
});
}
//Optional, use this function to check if the app has access to notifications, thus the listener is running
public boolean hasNotificationAccess(){
ComponentName cn = new ComponentName(this, MyService.class);
String flat = Settings.Secure.getString(this.getContentResolver(), "enabled_notification_listeners");
return flat != null && flat.contains(cn.flattenToString());
}
}
Важни забелешки:
- Не заборавајте да додадете дозвола за пристап на интернет во AndroidManifest.xml.
- Не заборавајте да додадете Volley како зависност во build.gradle.
- Пристапот до нотификации е специјална дозвола која треба да се овозможи „рачно“. Може да го направите преку „intent call“ на копчето кое го додадов во MainActivity, или рачно од нагодувањето на телефонот.
- „Notification listener“ е специјален тип на сервис кој e стартуван и прекинат од системот, во зависност од пристапот на нотификации. Затоа единствен начин да ја запрете апликацијата да испраќа нотификации е да го прекинете пристапот до нотификации, или да додадете дополнително конфигурирање на апликацијата кое сервисот може да го прочита и да постапи во согласност со тоа.
Со ова е завршен Android делот на туторијалот. Ако се е како што треба може да „изградите“ (build) и да ја стартувате апликацијата на Android телефон.
Чекор 2: Серверска страна
Сега да ја креираме PHP скриптата која ќе ги добива нотификациите од нашата Android апликација и ќе ги препраќа нотификациите преку Firebase до нашата Apple апликација.
Composer
Креирајте composer.json фајл и додадете ja Firebase PHP SDK зависноста во него, потоа со composer install инсталирајте го SDK во „vendor“ фолдерот.
{
"require": {
"kreait/firebase-php": "^5.9"
}
}
Firebase account
Откако го инсталиравме Firebase SDK, време е да го регистрирате Firebase профилот. Штом ќе го креирате профилот (активирање на Analytics не е потребно), одберете ја „запчаник“-иконата веднаш до Project Overview, потоа одберете Settings и од Service Accounts одберете Generate new private key.
Преземете го JSON-фајлот, и зачувајте го на серверот во фолдер кој не е јавно достапен.
PHP код
На крај, ќе ја креираме watchNotification.php скриптата во истиот фолдер со „vendor“-фолдерот. Внимателно прочитајте ги коментарите во кодот, особено ако сакате да ја испратите и иконата на апликацијата која ја испраќа нотификацијата. Потоа треба да ја додадете и Simple DОМ библиотеката. Не заборавајте да ја промените патеката до вашиот Firebase Service Account JSON фајл во 57-от ред.
<?php
require_once(__DIR__. "/vendor/autoload.php"); //Set correct path if needed
//require_once(__DIR__. "/simple_html_dom/simple_html_dom.php"); //Optional, if you want the getImageURL function to work
use Kreait\Firebase\Factory;
use Kreait\Firebase\ServiceAccount;
use Kreait\Firebase\Messaging\CloudMessage;
use Kreait\Firebase\Messaging\Notification;
use Kreait\Firebase;
$useImage = false; //Change this to true to send an image of the app that sent the notification. If enabled, uncomment the second 'require_once' statement above and install the Simple DOM parser library
// This function scrapes the HTML of the app's Play Store page and gets the image URL of it's icon.
// This code is bound to break, so it will need to be updated when the design of the Play page changes.
// In order not to SPAM the Play servers for every notification, create a 'packageIcons.json' file
// in the same folder as this script, add it read/write rights for the web user. The script will cache
// all images of apps there, so the Play page for every app won't be scraped if it has already been before.
function getImageURL($packageName){
$json = json_decode(file_get_contents("packageIcons.json"));
$link = null;
if(isset($json->{$packageName})){
$link = $json->{$packageName};
}
else{
$html = file_get_html("https://play.google.com/store/apps/details?id=".$packageName);
$bannerImage = $html->find('.xSyT2c img');
$imgUrl = $bannerImage[0]->src;
$json->{$packageName} = $imgUrl;
file_put_contents("packageIcons.json", json_encode($json));
$link = $imgUrl;
}
if(!is_null($link)){
$link = str_replace("s180", "s360", $link); //Get a higher resolution of the image
}
return $link;
}
function getNotification($notificationTitle, $notificationBody, $imageURL){
global $useImage;
if($useImage && !is_null($imageURL)){
return Notification::create($notificationTitle, $notificationBody, $imageURL);
}
return Notification::create($notificationTitle, $notificationBody);
}
function sendNotification($messaging, $notificationTitle, $notificationBody, $imageURL, $packageName){
$message = CloudMessage::withTarget('topic', "watch")
->withAndroidConfig(['priority' => 'high', 'collapseKey' => 'anbNotifications'])
->withApnsConfig(['headers' => ['apns-collapse-id' => 'anbNotifications.' . $packageName]])
->withNotification(getNotification($notificationTitle, $notificationBody, $imageURL))
->withDefaultSounds();
$messaging->send($message);
}
function sendPushNotifications($title, $body, $packageName){
$firebase = (new Factory)->withServiceAccount('firebase.json'); // <-- Change this to the path to your Fireabse JSON Service Account file. Keep this file outside of the public access folders.
$messaging = $firebase->createMessaging();
$image = getImageURL($packageName);
sendNotification($messaging, $title, $body, $image, $packageName);
}
if(isset($_GET['title']) && isset($_GET['body'])){
sendPushNotifications($_GET['title'], $_GET['body'], $_GET['packageName']);
}
Со ова завршивме два од трите дела потребни за ова да функционира. Уште сте тука? Ајде, речиси завршивме, остана уште последниот дел!
Чекор 3: WatchOS апликација
Време е за последниот дел. Ова е покомплексен дел, но можете да го завршите.
Ископирајте го кодот од примерот
Одличните луѓе во Google имаат креирана апликација пример која го прави токму она што нас ни е потребно. Во основа ќе го ископираме нивниот код од ова git repo.
Уредување на Podfile
Ќе забележите дека подфајлот од примерот користи локална зависност. Овие се постари, а нас ни требаат најновите. Затоа ќе го уредиме подфајлот да изгледа вака, и ќе ги додадеме точните имиња:
source 'https://cdn.cocoapods.org/'
use_frameworks!
target 'ANB WatchKit Extension' do #Make sure to put the right target name here, this is the EXTENSION target
platform :watchos, '7.0'
pod 'Firebase/Messaging'
end
target 'ServiceExtension' do
platform :watchos, '7.0'
pod 'Firebase/Messaging'
end
Потоа само извршете ја командата pod install и отворете го новиот .xcworkspaceфајл кој Pod го генерираше за нас. Отсега користете го овој фајл наместо .xcodeproj.
Регистрирај bundle IDs
Креирајте сопствен ID за секој „target“ во кодот кој го копирате од Google GitHub репозиториумот, внимавајќи да го замените com.google.firebase.extensions.dev со ваш Bundle.
Ако Xcode не го направи ова автоматски, регистрирајте ги сите „ID“-а од Apple Developer профилот и активирајте „Push“-нотификации https://developer.apple.com/account/resources/identifiers/list.
Регистрирајте ја апликацијата во Firebase
Сега со вашиот „bunde ID“ вратете се назад во Firebase профилот и додадете iOS апликација. Важно: регистрирајте ја апликација која завршува со .WatchKitApp, не другите bundle ID-ња што ги креиравме.
За време на регистрацијата ќе го добиете GoogleService-Info.plist фајлот. Преземете го фајлот и поставете го во root-фолдерот (локацијата каде што се наоѓа .xcworkspace) и проверете дали „target membership“ е селектиран за секој „target“.
Генерирајте и сетирајте APN-клуч
На Firebase му е потребен Apple Push Notification клуч за авторизација за да може да ги испраќа нотификациите на часовникот. Од девелопер конзолата генерирајте клуч за авторизација, селектирајте Apple Push Notifications service (APNs) како тип.
Преземете го клучот. На Firebase Project Setting страницата одберете Cloud Messaging и во iOS app configuration полето качете го APN-клучот.
Стартувајте ја апликацијата
По ова поврзете ги вашиот телефон и часовник со Xcode и стартувајте ја апликацијата на вашиот часовник. Следете што се случува во известувањата и проверете дали Firebase може да го пронајде фајлот за конфигурација, дали се поврзува на сервисот, дали генерира токен и се „претплатува“ на „watch“. Ако се е во ред, „излезот“ ќе ги содржи овие податоци.
Тест нотификација
На Firebase конзолата, од листата линкови на левата страна пронајдете го Cloud Messaging и одберете Send your first message. Внесете наслов и содржина (body) на пораката, во следниот дел одберете „Topic“ и внесете „watch“.
На крај одберете Review и екранот ќе изгледа вака. Може да го занемарите предупредувањето, само значи дека не сме активирале аналитика за проектот.
Кога ќе ја објавите пораката, тест нотификацијата треба да пристигне на вашиот часовник. Сега само треба да провериме дека сите делови се активни и вашиот Android телефон ќе започне да испраќа нотификации до вашиот Apple Watch.
Финални забелешки
Бидејќи целата комуникација се прави преку интернет, часовникот мора да биде поврзан. Ако немате 4G часовник тогаш користете апликации како Automate за автоматски да креирате хотспот на Android кога излегувате од дома/канцеларија, за часовникот да може да се поврзе и да добива нотификации.
Ова беше луда авантура за да го поврземе Android да комуницира со нашиот Apple Watch, но може да се изведе. Најголем дел од проектот може да се поедностави и да се автоматизира, но се сомневам дека Apple ќе дозволи официјална апликација за ова. Затоа, барем засега мора чепкаме и да хакираме вака.
Ова секако може да работи и во обратната насока, од Apple Watch да се поврземе на Android телефон преку Firebase data-пораки (можеби одговор на нотификации преку часовник), но ова е проект за некоја следна прилика…
[…] post Како направив да ми пристигаат Android нотификации на Apple W… appeared first on […]