Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Flask] snaps integration #6262

Closed
wants to merge 17 commits into from
Closed
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
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.0.2
3.0.3
11 changes: 11 additions & 0 deletions android/.project
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,15 @@
<natures>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
<filteredResources>
<filter>
<id>1684448150858</id>
<name></name>
<type>30</type>
<matcher>
<id>org.eclipse.core.resources.regexFilterMatcher</id>
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>
11 changes: 11 additions & 0 deletions android/.settings/org.eclipse.buildship.core.prefs
Original file line number Diff line number Diff line change
@@ -1,2 +1,13 @@
arguments=
auto.sync=false
build.scans.enabled=false
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
connection.project.dir=
eclipse.preferences.version=1
gradle.user.home=
java.home=
jvm.arguments=
offline.mode=false
override.workspace.settings=false
show.console.view=false
show.executions.view=false
18 changes: 18 additions & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,12 @@ android {
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
flask {
storeFile file('../keystores/flaskRelease.keystore')
storePassword System.getenv("BITRISEIO_ANDROID_FLASK_KEYSTORE_PASSWORD")
keyAlias System.getenv("BITRISEIO_ANDROID_FLASK_KEYSTORE_ALIAS")
keyPassword System.getenv("BITRISEIO_ANDROID_FLASK_KEYSTORE_PRIVATE_KEY_PASSWORD")
}
}

Expand Down Expand Up @@ -246,6 +252,14 @@ android {
applicationId "io.metamask"
signingConfig signingConfigs.release
}
flask {
dimension "version"
versionName "0.0.3"
versionCode 1128
applicationIdSuffix ".flask"
applicationId "io.metamask"
signingConfig signingConfigs.flask
}
}

buildTypes.each {
Expand Down Expand Up @@ -288,6 +302,10 @@ dependencies {
androidTestImplementation 'junit:junit:4.12'
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
implementation "com.android.installreferrer:installreferrer:2.2"
implementation 'org.apache.commons:commons-compress:1.22'
androidTestImplementation 'org.mockito:mockito-android:4.2.0'
androidTestImplementation 'androidx.test:core:1.5.0'
androidTestImplementation 'androidx.test:core-ktx:1.5.0'

debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.metamask.nativeModules.RNTarTest;

import androidx.test.core.app.ApplicationProvider;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import java.nio.file.StandardCopyOption;

import io.metamask.nativeModules.RNTar.RNTar;

@RunWith(JUnit4.class)
public class RNTarTest {
private RNTar tar;
private ReactApplicationContext reactContext;
private Promise promise;

@Before
public void setUp() {
reactContext = new ReactApplicationContext(ApplicationProvider.getApplicationContext());
tar = new RNTar(reactContext);
promise = mock(Promise.class);
}

@Test
public void testUnTar_validTgzFile() throws IOException {
// Prepare a sample .tgz file
InputStream tgzResource = Thread.currentThread().getContextClassLoader().getResourceAsStream("validTgzFile.tgz");
try {
File tgzFile = new File(reactContext.getCacheDir(), "validTgzFile.tgz");
Files.copy(tgzResource, tgzFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
String outputPath = reactContext.getCacheDir().getAbsolutePath() + "/output";
// Call unTar method
tar.unTar(tgzFile.getAbsolutePath(), outputPath, promise);
// Verify the promise was resolved
Path expectedDecompressedPath = Paths.get(outputPath, "package");
verify(promise).resolve(expectedDecompressedPath.toString());
} finally {
tgzResource.close();
}
}
}
owencraston marked this conversation as resolved.
Show resolved Hide resolved
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions android/app/src/flask/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<resources>
<string name="app_name">MetaMask Flask</string>
</resources>
4 changes: 2 additions & 2 deletions android/app/src/main/java/io/metamask/MainApplication.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.metamask;

import com.facebook.react.ReactApplication;
import com.cmcewen.blurview.BlurViewPackage;
import com.brentvatne.react.ReactVideoPackage;
import android.content.Context;
import com.facebook.react.PackageList;
Expand All @@ -25,6 +24,7 @@
import java.lang.reflect.Field;
import com.facebook.react.bridge.JSIModulePackage;
import com.swmansion.reanimated.ReanimatedJSIModulePackage;
import io.metamask.nativeModules.RNTar.RNTarPackage;

public class MainApplication extends MultiDexApplication implements ShareApplication, ReactApplication {

Expand All @@ -43,7 +43,7 @@ protected List<ReactPackage> getPackages() {
packages.add(new RCTAnalyticsPackage());
packages.add(new PreventScreenshotPackage());
packages.add(new ReactVideoPackage());

packages.add(new RNTarPackage());
return packages;
}

Expand Down
138 changes: 138 additions & 0 deletions android/app/src/main/java/io/metamask/nativeModules/RNTar/RNTar.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package io.metamask.nativeModules.RNTar;

import androidx.annotation.NonNull;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import android.util.Log;
import com.facebook.react.bridge.Promise;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.Path;
import android.os.Build;

public class RNTar extends ReactContextBaseJavaModule {
private static String MODULE_NAME = "RNTar";

public RNTar(ReactApplicationContext context) {
super(context);
}

@NonNull
@Override
public String getName() {
return MODULE_NAME;
}

private void createDirectories(String path) throws IOException {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Files.createDirectories(Paths.get(path));
} else {
File dir = new File(path);
if (!dir.exists()) {
dir.mkdirs();
}
}
}

private boolean isReadableWritable(String path) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Path dirPath = Paths.get(path);
return Files.isReadable(dirPath) && Files.isWritable(dirPath);
} else {
File dir = new File(path);
return dir.canRead() && dir.canWrite();
}
}

private boolean exists(String path) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return Files.exists(Paths.get(path));
} else {
return new File(path).exists();
}
}

private String extractTgzFile(String tgzPath, String outputPath) throws IOException {
try {
// Check if .tgz file exists
if (!exists(tgzPath)) {
throw new IOException("The specified .tgz file does not exist.");
}

// Create output directory if it doesn't exist
createDirectories(outputPath);

// Check if the output directory is readable and writable
if (!isReadableWritable(outputPath)) {
throw new IOException("The output directory is not readable and/or writable.");
}

// Set up the input streams for reading the .tgz file
try (FileInputStream fileInputStream = new FileInputStream(tgzPath);
GZIPInputStream gzipInputStream = new GZIPInputStream(fileInputStream);
TarArchiveInputStream tarInputStream = new TarArchiveInputStream(new BufferedInputStream(gzipInputStream))) {

TarArchiveEntry entry;

// Loop through the entries in the .tgz file
while ((entry = (TarArchiveEntry) tarInputStream.getNextEntry()) != null) {
File outputFile = new File(outputPath, entry.getName());

// If it is a directory, create the output directory
if (entry.isDirectory()) {
createDirectories(outputFile.getAbsolutePath());
} else {
// Create parent directories if they don't exist
createDirectories(outputFile.getParent());

// Set up the output streams for writing the file
try (FileOutputStream fos = new FileOutputStream(outputFile);
BufferedWriter dest = new BufferedWriter(new OutputStreamWriter(fos, StandardCharsets.UTF_8))) {

// Set up a BufferedReader for reading the file from the .tgz file
BufferedReader tarReader = new BufferedReader(new InputStreamReader(tarInputStream, StandardCharsets.UTF_8));

// Read the file line by line and convert line endings to the system default
String line;
while ((line = tarReader.readLine()) != null) {
dest.write(line);
dest.newLine();
}
}
}
}
}
// Return the output directory path
return new File(outputPath, "package").getAbsolutePath();
} catch (IOException e) {
Log.e("DecompressTgzFile", "Error decompressing tgz file", e);
throw new IOException("Error decompressing tgz file: " + e.getMessage(), e);
}
}

@ReactMethod
public void unTar(String pathToRead, String pathToWrite, final Promise promise) {
Log.d(MODULE_NAME, "Create event called with name: " + pathToRead
+ " and location: " + pathToWrite);
try {
String decompressedPath = extractTgzFile(pathToRead, pathToWrite);
promise.resolve(decompressedPath);
} catch(Exception e) {
promise.reject("Error uncompressing file:", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.metamask.nativeModules.RNTar;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class RNTarPackage implements ReactPackage {

@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}

@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();

modules.add(new RNTar(reactContext));

return modules;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
</adaptive-icon>
2 changes: 1 addition & 1 deletion app/components/Nav/App/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import branch from 'react-native-branch';
import AppConstants from '../../../core/AppConstants';
import Logger from '../../../util/Logger';
import { trackErrorAsAnalytics } from '../../../util/analyticsV2';
import { routingInstrumentation } from '../../../util/sentryUtils';
import { routingInstrumentation } from '../../../util/sentry/sentryUtils';
import Analytics from '../../../core/Analytics/Analytics';
import { connect, useSelector, useDispatch } from 'react-redux';
import {
Expand Down
22 changes: 22 additions & 0 deletions app/components/Nav/Main/MainNavigator.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ import { colors as importedColors } from '../../../styles/common';
import OrderDetails from '../../UI/FiatOnRampAggregator/Views/OrderDetails';
import TabBar from '../../../component-library/components/Navigation/TabBar';
import BrowserUrlModal from '../../Views/BrowserUrlModal';
import { SnapsSettingsList } from '../../Views/Snaps/SnapsSettingsList';
import { SnapSettings } from '../../Views/Snaps/SnapSettings';
import Routes from '../../../constants/navigation/Routes';
import AnalyticsV2 from '../../../util/analyticsV2';
import { MetaMetricsEvents } from '../../../core/Analytics';
Expand Down Expand Up @@ -198,6 +200,21 @@ const BrowserFlow = () => (

export const DrawerContext = React.createContext({ drawerRef: null });

const SnapsSettingsStack = () => (
<Stack.Navigator>
<Stack.Screen
name={Routes.SNAPS.SNAPS_SETTINGS_LIST}
component={SnapsSettingsList}
options={SnapsSettingsList.navigationOptions}
/>
<Stack.Screen
name={Routes.SNAPS.SNAP_SETTINGS}
component={SnapSettings}
options={SnapSettings.navigationOptions}
/>
</Stack.Navigator>
);

const SettingsFlow = () => (
<Stack.Navigator initialRouteName={'Settings'}>
<Stack.Screen
Expand Down Expand Up @@ -293,6 +310,11 @@ const SettingsFlow = () => (
component={EnterPasswordSimple}
options={EnterPasswordSimple.navigationOptions}
/>
<Stack.Screen
name={Routes.SNAPS.SNAPS_SETTINGS_LIST}
component={SnapsSettingsStack}
options={{ headerShown: false }}
/>
</Stack.Navigator>
);

Expand Down
Loading