Skip to content
This repository has been archived by the owner on Oct 19, 2022. It is now read-only.

Push support android #559

Merged
merged 21 commits into from
Jun 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,10 @@ flutter {
}

dependencies {
implementation 'com.google.crypto.tink:apps-webpush:1.3.0-rc1'
implementation 'com.google.firebase:firebase-core:17.2.1'
implementation 'com.google.crypto.tink:apps-webpush:1.3.0'
implementation 'org.bouncycastle:bcprov-jdk16:1.46'

testImplementation 'junit:junit:4.12'
testImplementation 'junit:junit:4.13'

androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
Expand Down
4 changes: 2 additions & 2 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
tools:replace="android:label">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize">
<!-- Specify that the launch screen should continue being displayed until Flutter renders its first frame. -->
<meta-data
Expand Down
108 changes: 41 additions & 67 deletions android/app/src/main/java/com/openxchange/oxcoi/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,147 +48,121 @@
import android.os.Bundle;
import android.util.Base64;

import androidx.annotation.NonNull;
import androidx.core.content.FileProvider;

import java.io.File;
import java.security.KeyPair;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import androidx.annotation.NonNull;
import androidx.core.content.FileProvider;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.plugin.common.MethodChannel;

public class MainActivity extends FlutterActivity {
private Map<String, String> sharedData = new HashMap<>();
private String startString = "";
private static final String SHARED_MIME_TYPE = "shared_mime_type";
private static final String SHARED_TEXT = "shared_text";
private static final String SHARED_PATH = "shared_path";
private static final String SHARED_FILE_NAME = "shared_file_name";
// TODO create constants for channel methods
private static final String INTENT_CHANNEL_NAME = "oxcoi.intent";
// TODO create constants for channel methods
private static final String SECURITY_CHANNEL_NAME = "oxcoi.security";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handleIntent(getIntent());
cacheDataFromPlatform(getIntent());
}

@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
DartExecutor dartExecutor = flutterEngine.getDartExecutor();
setupSharingMethodChannel(dartExecutor);
SecurityHelper securityHelper = new SecurityHelper(this);
setupSecurityMethodChannel(dartExecutor, securityHelper);
setupSecurityMethodChannel(dartExecutor);
}

@Override
protected void onNewIntent(@NonNull Intent intent) {
super.onNewIntent(intent);
cacheDataFromPlatform(intent);
}

private void setupSharingMethodChannel(DartExecutor dartExecutor) {
new MethodChannel(dartExecutor, INTENT_CHANNEL_NAME).setMethodCallHandler(
new io.flutter.plugin.common.MethodChannel(dartExecutor, MethodChannel.Sharing.NAME).setMethodCallHandler(
(call, result) -> {
if (call.method.contentEquals("getSharedData")) {
if (call.method.contentEquals(MethodChannel.Sharing.Methods.GET_SHARE_DATA)) {
result.success(sharedData);
sharedData.clear();
} else if (call.method.contentEquals("getInitialLink")) {
} else if (call.method.contentEquals(MethodChannel.Sharing.Methods.GET_INITIAL_LINK)) {
if (startString != null && !startString.isEmpty()) {
result.success(startString);
startString = "";
} else {
result.success(null);
}
} else if (call.method.contentEquals("sendSharedData")) {
shareFile(call.arguments);
} else if (call.method.contentEquals(MethodChannel.Sharing.Methods.SEND_SHARE_DATA)) {
shareDataWithPlatform(call.arguments);
result.success(null);
}
});
}

private void setupSecurityMethodChannel(DartExecutor dartExecutor, SecurityHelper securityHelper) {
new MethodChannel(dartExecutor, SECURITY_CHANNEL_NAME).setMethodCallHandler(
private void setupSecurityMethodChannel(DartExecutor dartExecutor) {
SecurityHelper securityHelper = new SecurityHelper();
new io.flutter.plugin.common.MethodChannel(dartExecutor, MethodChannel.Security.NAME).setMethodCallHandler(
(call, result) -> {
if (call.method.contentEquals("generateSecrets")) {
KeyPair keyPair = securityHelper.generateKey();
if (keyPair != null) {
securityHelper.persistKeyPair(keyPair);
} else {
throw new IllegalStateException("Key pair is empty");
}
String authSecret = securityHelper.generateAuthSecret();
if (authSecret != null && !authSecret.isEmpty()) {
securityHelper.persisAuthSecret(authSecret);
} else {
throw new IllegalStateException("Auth secret is empty");
}
result.success(null);
} else if (call.method.contentEquals("getKey")) {
String publicKeyBase64 = securityHelper.getPublicKeyBase64();
result.success(publicKeyBase64);
} else if (call.method.contentEquals("getAuthSecret")) {
String authSecret = securityHelper.getAuthSecretFromPersistedData();
result.success(authSecret);
} else if (call.method.contentEquals("decrypt")) {
String input = call.argument("input");
byte[] inputBytes = Base64.decode(input, Base64.DEFAULT);
String decryptMessage = securityHelper.decryptMessage(inputBytes);
if (call.method.contentEquals(MethodChannel.Security.Methods.DECRYPT)) {
String encryptedBase64Content = call.argument(MethodChannel.Security.Arguments.CONTENT);
String privateKeyBase64 = call.argument(MethodChannel.Security.Arguments.PRIVATE_KEY);
String publicKeyBase64 = call.argument(MethodChannel.Security.Arguments.PUBLIC_KEY);
String authBase64 = call.argument(MethodChannel.Security.Arguments.AUTH);
byte[] inputBytes = Base64.decode(encryptedBase64Content, Base64.DEFAULT);
String decryptMessage = securityHelper.decryptMessage(inputBytes, privateKeyBase64, publicKeyBase64, authBase64);
result.success(decryptMessage);
}

});
}

@Override
protected void onNewIntent(@NonNull Intent intent) {
super.onNewIntent(intent);
handleIntent(intent);
}

private void handleIntent(Intent intent) {
private void cacheDataFromPlatform(Intent intent) {
String action = intent.getAction();
String type = intent.getType();
Uri data = intent.getData();

if (Intent.ACTION_SEND.equals(action) && type != null) {
if (type.startsWith("text/")) {
String text = intent.getStringExtra(Intent.EXTRA_TEXT);
sharedData.put(SHARED_MIME_TYPE, type);
sharedData.put(SHARED_TEXT, text);
sharedData.put(MethodChannel.Sharing.Arguments.MIME_TYPE, type);
sharedData.put(MethodChannel.Sharing.Arguments.TEXT, text);
} else if (type.startsWith("application/") || type.startsWith("audio/") || type.startsWith("image/") || type.startsWith("video/")) {
Uri uri = (Uri) Objects.requireNonNull(getIntent().getExtras()).get(Intent.EXTRA_STREAM);
if (uri == null) {
ClipData clipData = intent.getClipData();
ClipData.Item item = clipData.getItemAt(0);
uri = item.getUri();
if (clipData != null) {
ClipData.Item item = clipData.getItemAt(0);
uri = item.getUri();
}
}
if (uri != null) {
String text = intent.getStringExtra(Intent.EXTRA_TEXT);
ShareHelper shareHelper = new ShareHelper();
String uriPath = shareHelper.getFilePathForUri(this, uri);
if (text != null && !text.isEmpty()) {
sharedData.put(SHARED_TEXT, text);
sharedData.put(MethodChannel.Sharing.Arguments.TEXT, text);
}
sharedData.put(SHARED_MIME_TYPE, type);
sharedData.put(SHARED_PATH, uriPath);
sharedData.put(SHARED_FILE_NAME, shareHelper.getFileName());
sharedData.put(MethodChannel.Sharing.Arguments.MIME_TYPE, type);
sharedData.put(MethodChannel.Sharing.Arguments.PATH, uriPath);
sharedData.put(MethodChannel.Sharing.Arguments.NAME, shareHelper.getFileName());
}
}
} else if (Intent.ACTION_VIEW.equals(action) && data != null) {
startString = data.toString();
}
}

private void shareFile(Object arguments) {
private void shareDataWithPlatform(Object arguments) {
@SuppressWarnings("unchecked")
HashMap<String, String> argsMap = (HashMap<String, String>) arguments;
String title = argsMap.get("title");
String path = argsMap.get("path");
String mimeType = argsMap.get("mimeType");
String text = argsMap.get("text");
String title = argsMap.get(MethodChannel.Sharing.Arguments.TITLE);
String path = argsMap.get(MethodChannel.Sharing.Arguments.PATH);
String mimeType = argsMap.get(MethodChannel.Sharing.Arguments.MIME_TYPE);
String text = argsMap.get(MethodChannel.Sharing.Arguments.TEXT);

Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType(mimeType);
Expand Down
80 changes: 80 additions & 0 deletions android/app/src/main/java/com/openxchange/oxcoi/MethodChannel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* OPEN-XCHANGE legal information
*
* All intellectual property rights in the Software are protected by
* international copyright laws.
*
*
* In some countries OX, OX Open-Xchange and open xchange
* as well as the corresponding Logos OX Open-Xchange and OX are registered
* trademarks of the OX Software GmbH group of companies.
* The use of the Logos is not covered by the Mozilla Public License 2.0 (MPL 2.0).
* Instead, you are allowed to use these Logos according to the terms and
* conditions of the Creative Commons License, Version 2.5, Attribution,
* Non-commercial, ShareAlike, and the interpretation of the term
* Non-commercial applicable to the aforementioned license is published
* on the web site https://www.open-xchange.com/terms-and-conditions/.
*
* Please make sure that third-party modules and libraries are used
* according to their respective licenses.
*
* Any modifications to this package must retain all copyright notices
* of the original copyright holder(s) for the original code used.
*
* After any such modifications, the original and derivative code shall remain
* under the copyright of the copyright holder(s) and/or original author(s) as stated here:
* https://www.open-xchange.com/legal/. The contributing author shall be
* given Attribution for the derivative code and a license granting use.
*
* Copyright (C) 2016-2020 OX Software GmbH
* Mail: [email protected]
*
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* 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 Mozilla Public License 2.0
* for more details.
*/

package com.openxchange.oxcoi;

class MethodChannel {

abstract static class Security {
static final String NAME = "oxcoi.security";

abstract static class Methods {
static final String DECRYPT = "decrypt";
}

abstract static class Arguments {
static final String CONTENT = "encryptedBase64Content";
static final String PRIVATE_KEY = "privateKeyBase64";
static final String PUBLIC_KEY = "publicKeyBase64";
static final String AUTH = "authBase64";
}
}

abstract static class Sharing {
static final String NAME = "oxcoi.sharing";

abstract static class Methods {
static final String GET_SHARE_DATA = "getSharedData";
static final String SEND_SHARE_DATA = "sendSharedData";
static final String GET_INITIAL_LINK = "getInitialLink";
}

abstract static class Arguments {
static final String MIME_TYPE = "mimeType";
static final String TEXT = "text";
static final String PATH = "path";
static final String NAME = "fileName";
static final String TITLE = "title";
}
}
}

Loading