From dc1aa0d845766da180e385112836a6c42842a6e8 Mon Sep 17 00:00:00 2001 From: Benoit VALLON Date: Sat, 5 Mar 2016 13:49:34 -0500 Subject: [PATCH] Updates android app --- android/app/build.gradle | 109 +++++++++++++++++- android/app/proguard-rules.pro | 11 +- android/app/react.gradle | 97 ++++++++++++++++ android/app/src/main/AndroidManifest.xml | 3 +- .../MainActivity.java | 87 +++++--------- android/build.gradle | 4 + 6 files changed, 242 insertions(+), 69 deletions(-) create mode 100644 android/app/react.gradle diff --git a/android/app/build.gradle b/android/app/build.gradle index ee2ce68..165be2a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,4 +1,80 @@ -apply plugin: 'com.android.application' +apply plugin: "com.android.application" + +import com.android.build.OutputFile + +/** + * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets + * and bundleReleaseJsAndAssets). + * These basically call `react-native bundle` with the correct arguments during the Android build + * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the + * bundle directly from the development server. Below you can see all the possible configurations + * and their defaults. If you decide to add a configuration block, make sure to add it before the + * `apply from: "react.gradle"` line. + * + * project.ext.react = [ + * // the name of the generated asset file containing your JS bundle + * bundleAssetName: "index.android.bundle", + * + * // the entry file for bundle generation + * entryFile: "index.android.js", + * + * // whether to bundle JS and assets in debug mode + * bundleInDebug: false, + * + * // whether to bundle JS and assets in release mode + * bundleInRelease: true, + * + * // whether to bundle JS and assets in another build variant (if configured). + * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants + * // The configuration property can be in the following formats + * // 'bundleIn${productFlavor}${buildType}' + * // 'bundleIn${buildType}' + * // bundleInFreeDebug: true, + * // bundleInPaidRelease: true, + * // bundleInBeta: true, + * + * // the root of your project, i.e. where "package.json" lives + * root: "../../", + * + * // where to put the JS bundle asset in debug mode + * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", + * + * // where to put the JS bundle asset in release mode + * jsBundleDirRelease: "$buildDir/intermediates/assets/release", + * + * // where to put drawable resources / React Native assets, e.g. the ones you use via + * // require('./image.png')), in debug mode + * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", + * + * // where to put drawable resources / React Native assets, e.g. the ones you use via + * // require('./image.png')), in release mode + * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", + * + * // by default the gradle tasks are skipped if none of the JS files or assets change; this means + * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to + * // date; if you have any other folders that you want to ignore for performance reasons (gradle + * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ + * // for example, you might want to remove it from here. + * inputExcludes: ["android/**", "ios/**"] + * ] + */ + +apply from: "react.gradle" + +/** + * Set this to true to create two separate APKs instead of one: + * - An APK that only works on ARM devices + * - An APK that only works on x86 devices + * The advantage is the size of the APK is reduced by about 4MB. + * Upload all the APKs to the Play Store and people will download + * the correct one based on the CPU architecture of their device. + */ +def enableSeparateBuildPerCPUArchitecture = false + +/** + * Run Proguard to shrink the Java bytecode in release builds. + */ +def enableProguardInReleaseBuilds = false android { compileSdkVersion 23 @@ -14,16 +90,37 @@ android { abiFilters "armeabi-v7a", "x86" } } + splits { + abi { + reset() + enable enableSeparateBuildPerCPUArchitecture + universalApk false // If true, also generate a universal APK + include "armeabi-v7a", "x86" + } + } buildTypes { release { - minifyEnabled false // Set this to true to enable Proguard - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + minifyEnabled enableProguardInReleaseBuilds + proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" + } + } + // applicationVariants are e.g. debug, release + applicationVariants.all { variant -> + variant.outputs.each { output -> + // For each separate APK per architecture, set a unique version code as described here: + // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits + def versionCodes = ["armeabi-v7a":1, "x86":2] + def abi = output.getFilter(OutputFile.ABI) + if (abi != null) { // null for the universal-debug, universal-release variants + output.versionCodeOverride = + versionCodes.get(abi) * 1048576 + defaultConfig.versionCode + } } } } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:23.0.1' - compile 'com.facebook.react:react-native:0.13.+' + compile fileTree(dir: "libs", include: ["*.jar"]) + compile "com.android.support:appcompat-v7:23.0.1" + compile "com.facebook.react:react-native:+" // From node_modules } diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro index ffa8c9f..7d72e46 100644 --- a/android/app/proguard-rules.pro +++ b/android/app/proguard-rules.pro @@ -40,9 +40,12 @@ -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; } -keep class * extends com.facebook.react.bridge.NativeModule { *; } +-keepclassmembers,includedescriptorclasses class * { native ; } -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; } --keepclassmembers class * { @com.facebook.react.uimanager.ReactProp ; } --keepclassmembers class * { @com.facebook.react.uimanager.ReactPropGroup ; } +-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; } +-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; } + +-dontwarn com.facebook.react.** # okhttp @@ -58,3 +61,7 @@ -dontwarn java.nio.file.* -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement -dontwarn okio.** + +# stetho + +-dontwarn com.facebook.stetho.** diff --git a/android/app/react.gradle b/android/app/react.gradle new file mode 100644 index 0000000..11a4f8b --- /dev/null +++ b/android/app/react.gradle @@ -0,0 +1,97 @@ +import org.apache.tools.ant.taskdefs.condition.Os + +def config = project.hasProperty("react") ? project.react : []; + +def bundleAssetName = config.bundleAssetName ?: "index.android.bundle" +def entryFile = config.entryFile ?: "index.android.js" + +// because elvis operator +def elvisFile(thing) { + return thing ? file(thing) : null; +} + +def reactRoot = elvisFile(config.root) ?: file("../../") +def inputExcludes = config.inputExcludes ?: ["android/**", "ios/**"] + +void runBefore(String dependentTaskName, Task task) { + Task dependentTask = tasks.findByPath(dependentTaskName); + if (dependentTask != null) { + dependentTask.dependsOn task + } +} + +gradle.projectsEvaluated { + // Grab all build types and product flavors + def buildTypes = android.buildTypes.collect { type -> type.name } + def productFlavors = android.productFlavors.collect { flavor -> flavor.name } + + // When no product flavors defined, use empty + if (!productFlavors) productFlavors.add('') + + productFlavors.each { productFlavorName -> + buildTypes.each { buildTypeName -> + // Create variant and target names + def targetName = "${productFlavorName.capitalize()}${buildTypeName.capitalize()}" + def targetPath = productFlavorName ? + "${productFlavorName}/${buildTypeName}" : + "${buildTypeName}" + + // React js bundle directories + def jsBundleDirConfigName = "jsBundleDir${targetName}" + def jsBundleDir = elvisFile(config."$jsBundleDirConfigName") ?: + file("$buildDir/intermediates/assets/${targetPath}") + + def resourcesDirConfigName = "jsBundleDir${targetName}" + def resourcesDir = elvisFile(config."${resourcesDirConfigName}") ?: + file("$buildDir/intermediates/res/merged/${targetPath}") + def jsBundleFile = file("$jsBundleDir/$bundleAssetName") + + // Bundle task name for variant + def bundleJsAndAssetsTaskName = "bundle${targetName}JsAndAssets" + + def currentBundleTask = tasks.create( + name: bundleJsAndAssetsTaskName, + type: Exec) { + group = "react" + description = "bundle JS and assets for ${targetName}." + + // Create dirs if they are not there (e.g. the "clean" task just ran) + doFirst { + jsBundleDir.mkdirs() + resourcesDir.mkdirs() + } + + // Set up inputs and outputs so gradle can cache the result + inputs.files fileTree(dir: reactRoot, excludes: inputExcludes) + outputs.dir jsBundleDir + outputs.dir resourcesDir + + // Set up the call to the react-native cli + workingDir reactRoot + + // Set up dev mode + def devEnabled = !targetName.toLowerCase().contains("release") + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + commandLine "cmd", "/c", "react-native", "bundle", "--platform", "android", "--dev", "${devEnabled}", + "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir + } else { + commandLine "react-native", "bundle", "--platform", "android", "--dev", "${devEnabled}", + "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir + } + + enabled config."bundleIn${targetName}" || + config."bundleIn${buildTypeName.capitalize()}" ?: + targetName.toLowerCase().contains("release") + } + + // Hook bundle${productFlavor}${buildType}JsAndAssets into the android build process + currentBundleTask.dependsOn("merge${targetName}Resources") + currentBundleTask.dependsOn("merge${targetName}Assets") + + runBefore("processArmeabi-v7a${targetName}Resources", currentBundleTask) + runBefore("processX86${targetName}Resources", currentBundleTask) + runBefore("processUniversal${targetName}Resources", currentBundleTask) + runBefore("process${targetName}Resources", currentBundleTask) + } + } +} diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 3f1e877..b2ccbf7 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -10,7 +10,8 @@ android:theme="@style/AppTheme"> + android:label="@string/app_name" + android:configChanges="keyboard|keyboardHidden|orientation|screenSize"> diff --git a/android/app/src/main/java/com/reactnativenwreactcalculator/MainActivity.java b/android/app/src/main/java/com/reactnativenwreactcalculator/MainActivity.java index 0a3417e..8423e5f 100644 --- a/android/app/src/main/java/com/reactnativenwreactcalculator/MainActivity.java +++ b/android/app/src/main/java/com/reactnativenwreactcalculator/MainActivity.java @@ -1,78 +1,45 @@ package com.reactnativenwreactcalculator; -import android.app.Activity; -import android.os.Bundle; -import android.view.KeyEvent; - -import com.facebook.react.LifecycleState; -import com.facebook.react.ReactInstanceManager; -import com.facebook.react.ReactRootView; -import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; +import com.facebook.react.ReactActivity; +import com.facebook.react.ReactPackage; import com.facebook.react.shell.MainReactPackage; -import com.facebook.soloader.SoLoader; - -public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler { - - private ReactInstanceManager mReactInstanceManager; - private ReactRootView mReactRootView; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mReactRootView = new ReactRootView(this); - - mReactInstanceManager = ReactInstanceManager.builder() - .setApplication(getApplication()) - .setBundleAssetName("index.android.bundle") - .setJSMainModuleName("src/index.android") - .addPackage(new MainReactPackage()) - .setUseDeveloperSupport(BuildConfig.DEBUG) - .setInitialLifecycleState(LifecycleState.RESUMED) - .build(); - mReactRootView.startReactApplication(mReactInstanceManager, "nativeApp", null); +import java.util.Arrays; +import java.util.List; - setContentView(mReactRootView); - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) { - mReactInstanceManager.showDevOptionsDialog(); - return true; - } - return super.onKeyUp(keyCode, event); - } +public class MainActivity extends ReactActivity { + /** + * Returns the name of the main component registered from JavaScript. + * This is used to schedule rendering of the component. + */ @Override - public void onBackPressed() { - if (mReactInstanceManager != null) { - mReactInstanceManager.onBackPressed(); - } else { - super.onBackPressed(); - } + protected String getMainComponentName() { + return "nativeApp"; } @Override - public void invokeDefaultOnBackPressed() { - super.onBackPressed(); + protected String getJSMainModuleName() { + return "src/index.android"; } + /** + * Returns whether dev mode should be enabled. + * This enables e.g. the dev menu. + */ @Override - protected void onPause() { - super.onPause(); - - if (mReactInstanceManager != null) { - mReactInstanceManager.onPause(); - } + protected boolean getUseDeveloperSupport() { + return BuildConfig.DEBUG; } + /** + * A list of packages used by the app. If the app uses additional views + * or modules besides the default ones, add more packages here. + */ @Override - protected void onResume() { - super.onResume(); - - if (mReactInstanceManager != null) { - mReactInstanceManager.onResume(this); - } + protected List getPackages() { + return Arrays.asList( + new MainReactPackage() + ); } } diff --git a/android/build.gradle b/android/build.gradle index ccdfc4e..403a007 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -16,5 +16,9 @@ allprojects { repositories { mavenLocal() jcenter() + maven { + // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm + url "$projectDir/../../node_modules/react-native/android" + } } }