From c3743cde259bcda76c75f438fb98c89c81f7f6e2 Mon Sep 17 00:00:00 2001 From: Jannis Mattheis Date: Sat, 12 May 2018 20:59:59 +0200 Subject: [PATCH] Add main activity and push service --- .../src/main/java/de/gotify/MainActivity.java | 23 +++ .../main/java/de/gotify/MainApplication.java | 52 ++++++ .../src/main/java/de/gotify/PushService.java | 172 ++++++++++++++++++ 3 files changed, 247 insertions(+) create mode 100644 android/app/src/main/java/de/gotify/MainActivity.java create mode 100644 android/app/src/main/java/de/gotify/MainApplication.java create mode 100644 android/app/src/main/java/de/gotify/PushService.java diff --git a/android/app/src/main/java/de/gotify/MainActivity.java b/android/app/src/main/java/de/gotify/MainActivity.java new file mode 100644 index 0000000..65f6240 --- /dev/null +++ b/android/app/src/main/java/de/gotify/MainActivity.java @@ -0,0 +1,23 @@ +package de.gotify; + +import android.content.Intent; + +import com.facebook.react.ReactActivity; + +public class MainActivity extends ReactActivity { + + @Override + protected void onStart() { + super.onStart(); + startService(new Intent(this, PushService.class)); + } + + /** + * Returns the name of the main component registered from JavaScript. + * This is used to schedule rendering of the component. + */ + @Override + protected String getMainComponentName() { + return "gotify"; + } +} diff --git a/android/app/src/main/java/de/gotify/MainApplication.java b/android/app/src/main/java/de/gotify/MainApplication.java new file mode 100644 index 0000000..9d2f2db --- /dev/null +++ b/android/app/src/main/java/de/gotify/MainApplication.java @@ -0,0 +1,52 @@ +package de.gotify; + +import android.app.Application; + +import com.facebook.react.ReactApplication; + +import in.sriraman.sharedpreferences.RNSharedPreferencesReactPackage; +import com.learnium.RNDeviceInfo.RNDeviceInfo; +import com.oblador.vectoricons.VectorIconsPackage; +import com.facebook.react.ReactNativeHost; +import com.facebook.react.ReactPackage; +import com.facebook.react.shell.MainReactPackage; +import com.facebook.soloader.SoLoader; + +import java.util.Arrays; +import java.util.List; + +public class MainApplication extends Application implements ReactApplication { + + private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { + @Override + public boolean getUseDeveloperSupport() { + return BuildConfig.DEBUG; + } + + @Override + protected List getPackages() { + return Arrays.asList( + new MainReactPackage(), + new RNSharedPreferencesReactPackage(), + new RNDeviceInfo(), + new VectorIconsPackage() + ); + } + + @Override + protected String getJSMainModuleName() { + return "index"; + } + }; + + @Override + public ReactNativeHost getReactNativeHost() { + return mReactNativeHost; + } + + @Override + public void onCreate() { + super.onCreate(); + SoLoader.init(this, /* native exopackage */ false); + } +} diff --git a/android/app/src/main/java/de/gotify/PushService.java b/android/app/src/main/java/de/gotify/PushService.java new file mode 100644 index 0000000..b58a940 --- /dev/null +++ b/android/app/src/main/java/de/gotify/PushService.java @@ -0,0 +1,172 @@ +package de.gotify; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Handler; +import android.os.IBinder; +import android.support.v4.app.NotificationCompat; +import android.util.Log; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Nullable; + +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.WebSocket; +import okhttp3.WebSocketListener; + +public class PushService extends Service { + private final OkHttpClient client = new OkHttpClient.Builder().readTimeout(0, TimeUnit.MILLISECONDS).build(); + public Handler handler = null; + private WebSocket socket = null; + private Gson gson = null; + + private SharedPreferences.OnSharedPreferenceChangeListener listener = new SharedPreferences.OnSharedPreferenceChangeListener() { + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + if (socket != null) { + socket.close(1000, "client logout"); + socket = null; + } + start(); + } + }; + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + super.onStartCommand(intent, flags, startId); + return START_REDELIVER_INTENT; + } + + @Override + public void onCreate() { + handler = new Handler(); + gson = new Gson(); + start(); + // https://github.com/sriraman/react-native-shared-preferences/issues/12 for why wit_player_shared_preferences + final SharedPreferences preferences = this.getSharedPreferences("wit_player_shared_preferences", Context.MODE_PRIVATE); + preferences.registerOnSharedPreferenceChangeListener(listener); + } + + private void foregroundNotification(String message) { + Intent notificationIntent = new Intent(this, MainActivity.class); + + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); + + Notification notification = new NotificationCompat.Builder(this, "GOTIFY_CHANNEL") + .setSmallIcon(R.mipmap.ic_launcher) + .setContentTitle("Gotify") + .setOngoing(true) + .setPriority(Notification.PRIORITY_MIN) + .setContentText(message) + .setContentIntent(pendingIntent).build(); + + startForeground(1337, notification); + } + + private void start() { + final SharedPreferences preferences = this.getSharedPreferences("wit_player_shared_preferences", Context.MODE_PRIVATE); + String url = preferences.getString("@global:url", null); + String token = preferences.getString("@global:token", null); + + + if (url == null || token == null) { + foregroundNotification("login required"); + return; + } + + HttpUrl httpUrl = HttpUrl.parse(url).newBuilder().addPathSegment("stream").addQueryParameter("token", token).build(); + + final Request request = new Request.Builder() + .url(httpUrl) + .get() + .build(); + foregroundNotification("Initializing WebSocket"); + + socket = client.newWebSocket(request, new WebSocketListener() { + + @Override + public void onOpen(WebSocket webSocket, Response response) { + foregroundNotification("Listening to " + request.url().host()); + } + + @Override + public void onMessage(WebSocket webSocket, String text) { + Log.i("gotify-ws", "Received" + text); + + Map hashMap = gson.fromJson(text, new TypeToken>() { + }.getType()); + + showNotification(Integer.parseInt(hashMap.get("id")), hashMap.get("title"), hashMap.get("message")); + } + + @Override + public void onClosed(WebSocket webSocket, int code, String reason) { + foregroundNotification("Stopped"); + showNotification(-4, "WS Conn Closed", "The websocket connection closed, this normmaly means the token was invalidated. A re-login is required"); + } + + @Override + public void onFailure(WebSocket webSocket, Throwable t, @Nullable Response response) { + foregroundNotification("Error: " + t.getMessage()); + + if (response != null && response.code() >= 400 && response.code() <= 499) { + showNotification(-2, "WS Bad Request", "Could not connect to WS: " + response.message()); + preferences.edit().remove("@global:token").apply(); + return; + } + + showNotification(-3, "WS Conn Failed", "The websocket connection failed, trying again in a minute: " + t.getMessage()); + + handler.postDelayed(new Runnable() { + @Override + public void run() { + start(); + } + }, TimeUnit.MINUTES.toMillis(1)); + } + }); + } + + private void showNotification(int id, String title, String message) { + Intent intent = new Intent(this, MainActivity.class); + PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + + NotificationCompat.Builder b = new NotificationCompat.Builder(this, "GOTIFY_CHANNEL"); + + + b.setAutoCancel(true) + .setDefaults(Notification.DEFAULT_ALL) + .setWhen(System.currentTimeMillis()) + .setSmallIcon(android.R.mipmap.sym_def_app_icon) + .setTicker("Gotify - " + title) + .setContentTitle(title) + .setContentText(message) + .setStyle(new NotificationCompat.BigTextStyle().bigText(message)) + .setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_SOUND) + .setContentIntent(contentIntent); + + + NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE); + + notificationManager.notify(id, b.build()); + } + +}