diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..735a704 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +.classpath +.DS_Store +.gradle +.idea +.project +bin +build +captures +gen +local.properties + +*.apk +*.ap_ +*.class +*.dex +*.iml \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..42ec1aa --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,7 @@ +Change Log +========== + +Version 1.0.0 *(2017-06-22)* +---------------------------- + +Initial release. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e339213 --- /dev/null +++ b/README.md @@ -0,0 +1,111 @@ +# Bridge + +A library for avoiding TransactionTooLargeException during state saving and restoration. + +## Contents + +* [Motivation](#motivation) +* [Setup](#setup) +* [Install](#install) +* [How Does It Work](#how) +* [Limitations](#limitations) +* [Testing](#testing) +* [License](#license) + + +## Motivation + +In spite of warnings from the Android development team stating that the state restoration framework should only be used for small amounts of view-related data, many developers have found it very useful to save large amounts of network-related data to avoid unnecessary network requests across configuration changes and when restoring from a background state. There was always a limitation to this, but that limit resulted in a silent failure to save state. In Android Nougat, that was upgraded to a crash via a [TransactionTooLargeException](https://developer.android.com/reference/android/os/TransactionTooLargeException.html). + +At Google I/O 2017, the Android development team gave a series of recommendations about app architecture that made clear that---rather than relying on the state restoration framework---the preferred method to save network data involves: + +- saving and restoring data from memory across configuration changes +- saving and restoring data from disk when restoring from a background state + +While new tools are available to achieve these goals, many developers will not be able to quickly update their apps to take advantage of them and will still face the dreaded `TransactionTooLargeException`. `Bridge` was created as a way to keep your existing app architecture in place while avoiding crashes related to state saving and restoration by following those two main principles. + + +## Setup + +`Bridge` is intended as a simple wrapper for annotation-based state saving libraries like [Icepick](https://github.com/frankiesardo/icepick), [Android-State](https://github.com/evernote/android-state), and [Icekick](https://github.com/tinsukE/icekick). For example, if you are already using `Icepick`, simply replace all calls to `Icepick.restoreInstanceState()` and `Icepick.saveInstanceState()` with their `Bridge` equivalents: + +```java +@Override +protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Bridge.restoreInstanceState(this, savedInstanceState); +} + +@Override +protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + Bridge.saveInstanceState(this, outState); +} +``` + +The only additional change to make is to initialize `Bridge` in your `Application.onCreate()` and specify `Icepick` as your "saved state handler": + +```java +Bridge.initialize(getApplicationContext(), new SavedStateHandler() { + @Override + public void saveInstanceState(@NonNull Object target, @NonNull Bundle state) { + Icepick.saveInstanceState(target, state); + } + + @Override + public void restoreInstanceState(@NonNull Object target, @Nullable Bundle state) { + Icepick.restoreInstanceState(target, state); + } +}); +``` + +That's it! You don't have to change any of your other code. If you are using any other `Icepick`-like library, simply swap out the library referred to in the `SavedStateHandler`. + + +## Install + +`Bridge` can be installed via gradle: + +```gradle +repositories { + maven { url "https://jitpack.io" } +} + +dependencies { + compile 'com.github.livefront:bridge:v1.0.0' +} +``` + + +## How Does It Work + +`Bridge` uses the `SavedStateHandler` to load your object's state into the given `Bundle`, but rather than send that `Bundle` to the main process of the OS (where it is subject to the `TransactionTooLargeException`) it saves it to memory and disk in a way that can restored to the same objects later. + +There is one main caveat here : in order to ensure that as little of your app's code needs to change as possible, `Bridge` will read its data from disk on the main thread. This is currently done in a way that may add a small amount of time to your app's startup process. Fortunately, `Bridge` leverages the compact nature of `Bundle` to store data as efficiently as possible, and even extremely large amounts of data well in excess of the `1MB` limit leading to `TransactionTooLargeException` should only add something on the order of 100ms to the startup time. + + +## Limitations + +Trying to save a `Bitmap` with `Bridge` will result in a crash, as their `Parcelable` implementation is highly optimized in a way that prevents `Bridge` from writing them to disk. + + +## Testing + +Typically state saving and restoration may be tested by simply testing device rotation. It is recommended that you also use tools that actually test full state restoration however, such as [Process Killer](https://github.com/livefront/process-killer-android) or the "Don't Keep Activities" developer option. + + +## License + + Copyright 2017 Livefront + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/bridge/.gitignore b/bridge/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/bridge/.gitignore @@ -0,0 +1 @@ +/build diff --git a/bridge/build.gradle b/bridge/build.gradle new file mode 100644 index 0000000..7436cf7 --- /dev/null +++ b/bridge/build.gradle @@ -0,0 +1,24 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 25 + buildToolsVersion "25.0.2" + defaultConfig { + minSdkVersion 12 + targetSdkVersion 25 + versionCode 1 + versionName "1.0.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:appcompat-v7:25.3.1' +} diff --git a/bridge/proguard-rules.pro b/bridge/proguard-rules.pro new file mode 100644 index 0000000..f8ccdeb --- /dev/null +++ b/bridge/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/brianyencho/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/bridge/src/main/AndroidManifest.xml b/bridge/src/main/AndroidManifest.xml new file mode 100644 index 0000000..9b78a8f --- /dev/null +++ b/bridge/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/bridge/src/main/java/com/livefront/bridge/Bridge.java b/bridge/src/main/java/com/livefront/bridge/Bridge.java new file mode 100644 index 0000000..14c986f --- /dev/null +++ b/bridge/src/main/java/com/livefront/bridge/Bridge.java @@ -0,0 +1,57 @@ +package com.livefront.bridge; + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +public class Bridge { + + private static BridgeDelegate sDelegate; + + private static void checkInitialization() { + if (sDelegate == null) { + throw new IllegalStateException( + "You must first call initialize before calling any other methods"); + } + } + + /** + * Initializes the framework used to save and restore data and route it to a location free from + * {@link android.os.TransactionTooLargeException}. The actual state saving and restoration + * of each object will be performed by the provided {@link SavedStateHandler}. + * + * @param context an application {@link Context} necessary for saving state to disk + * @param savedStateHandler used to do the actual state saving and restoring for a given object + */ + public static void initialize(@NonNull Context context, + @NonNull SavedStateHandler savedStateHandler) { + sDelegate = new BridgeDelegate(context, savedStateHandler); + } + + /** + * Restores the state of the given target object based on tracking information stored in the + * given {@link Bundle}. The actual saved data will be retrieved from a location in memory or + * stored on disk. + *

+ * It is required to call {@link #initialize(Context, SavedStateHandler)} before calling this + * method. + */ + public static void restoreInstanceState(@NonNull Object target, @Nullable Bundle state) { + checkInitialization(); + sDelegate.restoreInstanceStateInternal(target, state); + } + + /** + * Saves the state of the given target object to a location in memory and disk and stores + * tracking information in given {@link Bundle}. + *

+ * It is required to call {@link #initialize(Context, SavedStateHandler)} before calling this + * method. + */ + public static void saveInstanceState(@NonNull Object target, @NonNull Bundle state) { + checkInitialization(); + sDelegate.saveInstanceStateInternal(target, state); + } + +} diff --git a/bridge/src/main/java/com/livefront/bridge/BridgeDelegate.java b/bridge/src/main/java/com/livefront/bridge/BridgeDelegate.java new file mode 100644 index 0000000..b031d43 --- /dev/null +++ b/bridge/src/main/java/com/livefront/bridge/BridgeDelegate.java @@ -0,0 +1,118 @@ +package com.livefront.bridge; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.os.Parcel; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Base64; +import android.view.View; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.WeakHashMap; + +class BridgeDelegate { + + private static final String TAG = BridgeDelegate.class.getName(); + + private static final String KEY_BUNDLE = "bundle_%s"; + private static final String KEY_UUID = "uuid_%s"; + + private boolean mIsFirstRestoreCall = true; + private Map mUuidBundleMap = new HashMap<>(); + private Map mObjectUuidMap = new WeakHashMap<>(); + private SavedStateHandler mSavedStateHandler; + private SharedPreferences mSharedPreferences; + + BridgeDelegate(@NonNull Context context, + @NonNull SavedStateHandler savedStateHandler) { + mSharedPreferences = context.getSharedPreferences(TAG, Context.MODE_PRIVATE); + mSavedStateHandler = savedStateHandler; + } + + private String getKeyForEncodedBundle(@NonNull String uuid) { + return String.format(KEY_BUNDLE, uuid); + } + + private String getKeyForUuid(@NonNull Object target) { + String classId = target.getClass().getName(); + if (target instanceof View) { + classId += ((View) target).getId(); + } + return String.format(KEY_UUID, classId); + } + + @Nullable + private Bundle readFromDisk(@NonNull String uuid) { + String encodedString = mSharedPreferences.getString(getKeyForEncodedBundle(uuid), null); + if (encodedString == null) { + return null; + } + byte[] parcelBytes = Base64.decode(encodedString, 0); + Parcel parcel = Parcel.obtain(); + parcel.unmarshall(parcelBytes, 0, parcelBytes.length); + parcel.setDataPosition(0); + Bundle bundle = parcel.readBundle(BridgeDelegate.class.getClassLoader()); + parcel.recycle(); + return bundle; + } + + void restoreInstanceStateInternal(@NonNull Object target, @Nullable Bundle state) { + boolean isFirstRestoreCall = mIsFirstRestoreCall; + mIsFirstRestoreCall = false; + if (state == null) { + if (isFirstRestoreCall) { + mSharedPreferences.edit() + .clear() + .apply(); + } + return; + } + String uuid = mObjectUuidMap.containsKey(target) + ? mObjectUuidMap.get(target) + : state.getString(getKeyForUuid(target), null); + if (uuid == null) { + return; + } + mObjectUuidMap.put(target, uuid); + Bundle bundle = mUuidBundleMap.containsKey(uuid) + ? mUuidBundleMap.get(uuid) + : readFromDisk(uuid); + if (bundle == null) { + return; + } + mSavedStateHandler.restoreInstanceState(target, bundle); + } + + void saveInstanceStateInternal(@NonNull Object target, @NonNull Bundle state) { + String uuid = mObjectUuidMap.get(target); + if (uuid == null) { + uuid = UUID.randomUUID().toString(); + mObjectUuidMap.put(target, uuid); + } + state.putString(getKeyForUuid(target), uuid); + Bundle bundle = new Bundle(); + mSavedStateHandler.saveInstanceState(target, bundle); + if (bundle.isEmpty()) { + // Don't bother saving empty bundles + return; + } + mUuidBundleMap.put(uuid, bundle); + writeToDisk(uuid, bundle); + } + + private void writeToDisk(@NonNull String uuid, + @NonNull Bundle bundle) { + Parcel parcel = Parcel.obtain(); + parcel.writeBundle(bundle); + String encodedString = Base64.encodeToString(parcel.marshall(), 0); + mSharedPreferences.edit() + .putString(getKeyForEncodedBundle(uuid), encodedString) + .apply(); + parcel.recycle(); + } + +} \ No newline at end of file diff --git a/bridge/src/main/java/com/livefront/bridge/SavedStateHandler.java b/bridge/src/main/java/com/livefront/bridge/SavedStateHandler.java new file mode 100644 index 0000000..609bd73 --- /dev/null +++ b/bridge/src/main/java/com/livefront/bridge/SavedStateHandler.java @@ -0,0 +1,13 @@ +package com.livefront.bridge; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +public interface SavedStateHandler { + + void saveInstanceState(@NonNull Object target, @NonNull Bundle state); + + void restoreInstanceState(@NonNull Object target, @Nullable Bundle state); + +} diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..d0aa704 --- /dev/null +++ b/build.gradle @@ -0,0 +1,23 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.3.2' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..aac7c9b --- /dev/null +++ b/gradle.properties @@ -0,0 +1,17 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..13372ae Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ae8aa1c --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 16 09:21:28 CDT 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..9d82f78 --- /dev/null +++ b/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..5741c2a --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':bridge'