Эх сурвалжийг харах

Work a bit more on notifications

Mario Danic 8 жил өмнө
parent
commit
047bd3cc8e

+ 2 - 2
build.gradle

@@ -178,8 +178,8 @@ dependencies {
     compile name: 'touch-image-view'
     compile 'com.android.support:multidex:1.0.1'
 
-    compile 'com.github.nextcloud:android-library:notifications-SNAPSHOT'
-    compile 'com.github.nextcloud:android-library:1.0.14'
+
+    compile 'com.github.nextcloud:android-library:notifications-push-SNAPSHOT'
     compile "com.android.support:support-v4:${supportLibraryVersion}"
     compile "com.android.support:design:${supportLibraryVersion}"
     compile 'com.jakewharton:disklrucache:2.0.2'

+ 2 - 0
src/main/res/values/setup.xml

@@ -122,6 +122,8 @@
     <!-- Files becomes Home -->
     <bool name="use_home">false</bool>
 
+    <!-- Push server url -->
+    <string name="push_server_url" translatable="true"></string>
 </resources>
 
 

+ 70 - 9
src/modified/java/com/owncloud/android/services/firebase/NCFirebaseInstanceIDService.java

@@ -19,28 +19,89 @@
  */
 package com.owncloud.android.services.firebase;
 
-import android.util.Log;
+import android.accounts.Account;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.content.Context;
+import android.text.TextUtils;
 
 import com.google.firebase.iid.FirebaseInstanceId;
 import com.google.firebase.iid.FirebaseInstanceIdService;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.lib.common.OwnCloudAccount;
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
+import com.owncloud.android.lib.common.operations.RemoteOperation;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.notifications.RegisterAccountDeviceForNotificationsOperation;
+import com.owncloud.android.lib.resources.notifications.RegisterAccountDeviceForProxyOperation;
+import com.owncloud.android.lib.resources.notifications.models.PushResponse;
+import com.owncloud.android.utils.PushUtils;
+
+import java.io.IOException;
+import java.security.PublicKey;
 
 public class NCFirebaseInstanceIDService extends FirebaseInstanceIdService {
     private static final String TAG = "NCFirebaseInstanceID";
 
     @Override
     public void onTokenRefresh() {
+        //You can implement this method to store the token on your server
+        if (!TextUtils.isEmpty(getResources().getString(R.string.push_server_url))) {
+            PushUtils.generateRsa2048KeyPair();
+        }
 
-        //Getting registration token
-        String refreshedToken = FirebaseInstanceId.getInstance().getToken();
-
-        //Displaying token on logcat
-        Log.d(TAG, "Refreshed token: " + refreshedToken);
-
+        sendRegistrationToServer(FirebaseInstanceId.getInstance().getToken());
     }
 
     private void sendRegistrationToServer(String token) {
-        //You can implement this method to store the token on your server
-        //Not required for current project
+        String pushTokenHash = PushUtils.generateSHA512Hash(token);
+        PublicKey devicePublicKey = (PublicKey) PushUtils.readKeyFromFile(true);
+        if (devicePublicKey != null) {
+            String publicKey = devicePublicKey.toString();
+
+            Context context = MainApp.getAppContext();
+            for (Account account : AccountUtils.getAccounts(context)) {
+                try {
+                    OwnCloudAccount ocAccount = new OwnCloudAccount(account, context);
+                    OwnCloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton().
+                            getClientFor(ocAccount, context);
+
+                    RemoteOperation registerAccountDeviceForNotificationsOperation =
+                            new RegisterAccountDeviceForNotificationsOperation(pushTokenHash,
+                        publicKey, context.getResources().getString(R.string.push_server_url));
+
+                    RemoteOperationResult remoteOperationResult = registerAccountDeviceForNotificationsOperation.
+                            execute(mClient);
+
+                    if (remoteOperationResult.isSuccess()) {
+                        PushResponse pushResponse = remoteOperationResult.getPushResponseData();
+
+                        RemoteOperation registerAccountDeviceForProxyOperation = new
+                                RegisterAccountDeviceForProxyOperation(
+                                context.getResources().getString(R.string.push_server_url),
+                                token, pushResponse.getDeviceIdentifier(), pushResponse.getSignature(),
+                                pushResponse.getPublicKey());
+
+                        remoteOperationResult = registerAccountDeviceForProxyOperation.execute(mClient);
+                    }
+                } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) {
+                    Log_OC.d(TAG, "Failed to find an account");
+                } catch (AuthenticatorException e) {
+                    Log_OC.d(TAG, "Failed via AuthenticatorException");
+                } catch (IOException e) {
+                    Log_OC.d(TAG, "Failed via IOException");
+                } catch (OperationCanceledException e) {
+                    Log_OC.d(TAG, "Failed via OperationCanceledException");
+                }
+
+            }
+
+        }
+
     }
 
 }

+ 4 - 2
src/modified/java/com/owncloud/android/services/firebase/NCFirebaseMessagingService.java

@@ -31,17 +31,18 @@ import android.util.Log;
 import com.google.firebase.messaging.FirebaseMessagingService;
 import com.google.firebase.messaging.RemoteMessage;
 import com.owncloud.android.R;
-import com.owncloud.android.lib.resources.notifications.models.Notification;
+import com.owncloud.android.ui.activity.NotificationsActivity;
 
 public class NCFirebaseMessagingService extends FirebaseMessagingService {
     private static final String TAG = "NCFirebaseMessaging";
 
     @Override
     public void onMessageReceived(RemoteMessage remoteMessage) {
+        super.onMessageReceived(remoteMessage);
         Log.d(TAG, "From: " + remoteMessage.getFrom());
         Log.d(TAG, "Notification Message Body: " + remoteMessage.getNotification().getBody());
 
-        sendNotification(remoteMessage.getNotification().ge);
+        sendNotification(remoteMessage.getNotification().getTitle());
     }
 
     private void sendNotification(String contentTitle) {
@@ -55,6 +56,7 @@ public class NCFirebaseMessagingService extends FirebaseMessagingService {
                 .setSmallIcon(R.mipmap.ic_launcher)
                 .setContentTitle(contentTitle)
                 .setSound(defaultSoundUri)
+                .setAutoCancel(true)
                 .setContentIntent(pendingIntent);
 
         NotificationManager notificationManager =

+ 170 - 0
src/modified/java/com/owncloud/android/utils/PushUtils.java

@@ -0,0 +1,170 @@
+/**
+ * Nextcloud Android client application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017 Mario Danic
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.utils;
+
+import android.util.Base64;
+import android.util.Log;
+
+import com.owncloud.android.MainApp;
+import com.owncloud.android.lib.common.utils.Log_OC;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+public class PushUtils {
+
+    private static final String TAG = "PushUtils";
+    private static final String KEYPAIR_FOLDER = "nc-keypair";
+    private static final String KEYPAIR_FILE_NAME = "push_key";
+    private static final String KEYPAIR_PUB_EXTENSION = ".pub";
+
+    public static String generateSHA512Hash(String pushToken) {
+        MessageDigest messageDigest = null;
+        try {
+            messageDigest = MessageDigest.getInstance("SHA-512");
+            byte[] byteData = messageDigest.digest(pushToken.getBytes("UTF-8"));
+            String base64 = Base64.encodeToString(byteData, Base64.NO_WRAP);
+            return base64;
+        } catch (NoSuchAlgorithmException e) {
+            Log_OC.d(TAG, "SHA-512 algorithm not supported");
+        } catch (UnsupportedEncodingException e) {
+            Log_OC.d(TAG, "Unsupported encoding");
+        }
+
+        return "";
+    }
+
+    public static int generateRsa2048KeyPair() {
+        String keyPath = MainApp.getStoragePath() + File.separator + MainApp.getDataFolder() + File.separator
+                + KEYPAIR_FOLDER;;
+
+        String privateKeyPath = keyPath + File.separator + KEYPAIR_FILE_NAME;
+        String publicKeyPath = keyPath + File.separator + KEYPAIR_FILE_NAME + KEYPAIR_PUB_EXTENSION;
+        File keyPathFile = new File(keyPath);
+        if (!new File(privateKeyPath).exists() && !new File(publicKeyPath).exists()) {
+            try {
+                Log.d("MARIO", keyPathFile.getAbsolutePath());
+                keyPathFile.createNewFile();
+                KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+                keyGen.initialize(2048);
+
+                KeyPair pair = keyGen.generateKeyPair();
+                int statusPrivate = saveKeyToFile(pair.getPrivate(), privateKeyPath);
+                int statusPublic = saveKeyToFile(pair.getPublic(), publicKeyPath);
+
+                if (statusPrivate == 0 && statusPublic == 0) {
+                    // all went well
+                    return 0;
+                } else {
+                    return -2;
+                }
+            } catch (IOException e) {
+                Log_OC.d(TAG, "Failed to generate a keypair folder path" + e.getLocalizedMessage());
+            } catch (NoSuchAlgorithmException e) {
+                Log_OC.d(TAG, "RSA algorithm not supported");
+            }
+        } else {
+            // we already have the key
+            return -1;
+        }
+
+        // we failed to generate the key
+        return -2;
+    }
+
+    public static Key readKeyFromFile(boolean readPublicKey) {
+        String keyPath = MainApp.getStoragePath() + File.separator + MainApp.getDataFolder() + File.separator
+                + KEYPAIR_FOLDER;;
+        String privateKeyPath = keyPath + File.separator + KEYPAIR_FILE_NAME;
+        String publicKeyPath = keyPath + File.separator + KEYPAIR_FILE_NAME + KEYPAIR_PUB_EXTENSION;
+
+        String path;
+
+        if (readPublicKey) {
+            path = publicKeyPath;
+        } else {
+            path = privateKeyPath;
+        }
+
+        FileInputStream fileInputStream = null;
+        try {
+            fileInputStream = new FileInputStream(path);
+            byte[] bytes = new byte[fileInputStream.available()];
+            fileInputStream.read(bytes);
+            fileInputStream.close();
+
+            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+
+            if (readPublicKey) {
+                X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
+                PublicKey key = keyFactory.generatePublic(keySpec);
+                return key;
+            } else {
+                PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
+                PrivateKey key = keyFactory.generatePrivate(keySpec);
+                return key;
+            }
+
+        } catch (FileNotFoundException e) {
+            Log_OC.d(TAG, "Failed to find path while reading the Key");
+        } catch (IOException e) {
+            Log_OC.d(TAG, "IOException while reading the key");
+        } catch (InvalidKeySpecException e) {
+            Log_OC.d(TAG, "InvalidKeySpecException while reading the key");
+        } catch (NoSuchAlgorithmException e) {
+            Log_OC.d(TAG, "RSA algorithm not supported");
+        }
+
+        return null;
+    }
+
+    private static int saveKeyToFile(Key key, String path) {
+        byte[] encoded = key.getEncoded();
+        FileOutputStream keyFileOutputStream = null;
+        try {
+            keyFileOutputStream = new FileOutputStream(path);
+            keyFileOutputStream.write(encoded);
+            keyFileOutputStream.close();
+            return 0;
+        } catch (FileNotFoundException e) {
+            Log_OC.d(TAG, "Failed to save key to file");
+        } catch (IOException e) {
+            Log_OC.d(TAG, "Failed to save key to file via IOException");
+        }
+
+        return -1;
+    }
+}

+ 5 - 0
src/modified/res/values/setup.xml

@@ -109,6 +109,9 @@
 
     <!-- login data links -->
     <string name="login_data_own_scheme" translatable="false">cloud</string>
+    <!-- url for webview login, with the protocol prefix
+    If set, will replace all other login methods available -->
+    <string name="webview_login_url" translatable="false"></string>
 
     <!-- analytics enabled -->
     <bool name="analytics_enabled">false</bool>
@@ -116,6 +119,8 @@
     <!-- Files becomes Home -->
     <bool name="use_home">true</bool>
 
+    <!-- Push server url -->
+    <string name="push_server_url" translatable="false"></string>
 </resources>