ruboto 1.0.3 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -2
- data/README.md +1 -0
- data/RELEASE_CANDICATE_DOC.md +21 -8
- data/RELEASE_DOC.md +71 -27
- data/Rakefile +27 -25
- data/assets/rakelib/ruboto.rake +137 -21
- data/assets/rakelib/{stdlib.rake → ruboto.stdlib.rake} +80 -29
- data/assets/rakelib/{stdlib_dependencies.rb → ruboto.stdlib.rb} +18 -24
- data/assets/rakelib/{stdlib.yml → ruboto.stdlib.yml} +0 -0
- data/assets/ruboto.yml +28 -18
- data/assets/src/org/ruboto/DexDex.java +329 -0
- data/assets/src/org/ruboto/FrameworkHack.java +177 -0
- data/assets/src/org/ruboto/JRubyAdapter.java +28 -4
- data/assets/src/org/ruboto/ScriptLoader.java +1 -1
- data/assets/src/org/ruboto/SplashActivity.java +1 -2
- data/assets/src/ruboto/activity/reload.rb +1 -0
- data/assets/src/ruboto/activity.rb +11 -5
- data/assets/src/ruboto/util/toast.rb +2 -2
- data/lib/ruboto/commands/base.rb +85 -37
- data/lib/ruboto/util/emulator.rb +32 -14
- data/lib/ruboto/util/setup.rb +34 -12
- data/lib/ruboto/util/update.rb +70 -40
- data/lib/ruboto/version.rb +1 -1
- data/test/activity/navigation_activity_test.rb +2 -0
- data/test/activity/ssl_activity.rb +26 -9
- data/test/activity/ssl_activity_test.rb +14 -6
- data/test/app_test_methods.rb +8 -3
- data/test/ruboto_gen_test.rb +13 -7
- data/test/ruboto_setup_test.rb +21 -0
- data/test/ruboto_update_test.rb +26 -28
- data/test/test_helper.rb +25 -21
- metadata +10 -7
| @@ -0,0 +1,329 @@ | |
| 1 | 
            +
            /*
         | 
| 2 | 
            +
             * Copyright 2013 ThinkFree
         | 
| 3 | 
            +
             *
         | 
| 4 | 
            +
             * Licensed under the Apache License, Version 2.0 (the "License");
         | 
| 5 | 
            +
             * you may not use this file except in compliance with the License.
         | 
| 6 | 
            +
             * You may obtain a copy of the License at
         | 
| 7 | 
            +
             *
         | 
| 8 | 
            +
             *    http://www.apache.org/licenses/LICENSE-2.0
         | 
| 9 | 
            +
             *
         | 
| 10 | 
            +
             * Unless required by applicable law or agreed to in writing, software
         | 
| 11 | 
            +
             * distributed under the License is distributed on an "AS IS" BASIS,
         | 
| 12 | 
            +
             * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         | 
| 13 | 
            +
             * See the License for the specific language governing permissions and
         | 
| 14 | 
            +
             * limitations under the License.
         | 
| 15 | 
            +
             */
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            package org.ruboto;
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            import android.app.Activity;
         | 
| 20 | 
            +
            import android.app.ProgressDialog;
         | 
| 21 | 
            +
            import android.content.Context;
         | 
| 22 | 
            +
            import android.content.Intent;
         | 
| 23 | 
            +
            import android.content.res.AssetManager;
         | 
| 24 | 
            +
            import android.os.Build;
         | 
| 25 | 
            +
            import android.util.Log;
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            import java.io.BufferedInputStream;
         | 
| 28 | 
            +
            import java.io.BufferedOutputStream;
         | 
| 29 | 
            +
            import java.io.File;
         | 
| 30 | 
            +
            import java.io.FileOutputStream;
         | 
| 31 | 
            +
            import java.io.IOException;
         | 
| 32 | 
            +
            import java.util.ArrayList;
         | 
| 33 | 
            +
            import java.util.Arrays;
         | 
| 34 | 
            +
            import java.util.Observable;
         | 
| 35 | 
            +
            import java.util.Observer;
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            import dalvik.system.PathClassLoader;
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            /**
         | 
| 40 | 
            +
             * Easy class loading for multi-dex Android application.
         | 
| 41 | 
            +
             *
         | 
| 42 | 
            +
             * 1) call validateClassPath() from Application.onCreate()
         | 
| 43 | 
            +
             * 2) check dexOptRequired then addAllJARsAssets() on non-UI thread.
         | 
| 44 | 
            +
             *
         | 
| 45 | 
            +
             * @author Alan Goo
         | 
| 46 | 
            +
             */
         | 
| 47 | 
            +
            public class DexDex {
         | 
| 48 | 
            +
                public static final String DIR_SUBDEX = "dexdex";
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                private static final String TAG = "DexDex";
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                private static final int SDK_INT_ICS = 14;
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                private static final int SDK_INT_KITKAT = 19;
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                private static final int BUF_SIZE = 8 * 1024;
         | 
| 57 | 
            +
                public static final int PROGRESS_COMPLETE = 100;
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                private static ArrayList<String> theAppended = new ArrayList<String>();
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                public static boolean debug = false;
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                public static boolean dexOptRequired = false;
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                private static Activity uiBlockedActivity = null;
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                /**
         | 
| 68 | 
            +
                 * just reuse existing interface for convenience
         | 
| 69 | 
            +
                 * @hide
         | 
| 70 | 
            +
                 */
         | 
| 71 | 
            +
                public static Observer dexOptProgressObserver = null;
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                private DexDex() {
         | 
| 74 | 
            +
                    // do not create an instance
         | 
| 75 | 
            +
                }
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                private static boolean shouldDexOpt(File apkFile, File dexDir, String[] names) {
         | 
| 78 | 
            +
                    boolean result = shouldDexOptImpl(apkFile, dexDir, names);
         | 
| 79 | 
            +
                    if(debug) {
         | 
| 80 | 
            +
                        Log.d(TAG, "shouldDexOpt(" + apkFile + "," + dexDir + "," + Arrays.deepToString(names) + ") => " + result
         | 
| 81 | 
            +
                            + " on " + Thread.currentThread());
         | 
| 82 | 
            +
                    }
         | 
| 83 | 
            +
                    return result;
         | 
| 84 | 
            +
                }
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                private static boolean shouldDexOptImpl(File apkFile, File dexDir, String[] names) {
         | 
| 87 | 
            +
                    long apkDate = apkFile.lastModified();
         | 
| 88 | 
            +
                    // APK upgrade case
         | 
| 89 | 
            +
                    if(debug) {
         | 
| 90 | 
            +
                        Log.d(TAG, "APK Date : " + apkDate + " ,dexDir date : " + dexDir.lastModified());
         | 
| 91 | 
            +
                    }
         | 
| 92 | 
            +
                    if (apkDate > dexDir.lastModified()) {
         | 
| 93 | 
            +
                        return true;
         | 
| 94 | 
            +
                    }
         | 
| 95 | 
            +
                    // clean install (or crash during install) case
         | 
| 96 | 
            +
                    for (int i = 0; i < names.length; i++) {
         | 
| 97 | 
            +
                        String name = names[i];
         | 
| 98 | 
            +
                        File dexJar = new File(dexDir, name);
         | 
| 99 | 
            +
                        if (dexJar.exists()) {
         | 
| 100 | 
            +
                            if (dexJar.lastModified() < apkDate) {
         | 
| 101 | 
            +
                                return true;
         | 
| 102 | 
            +
                            }
         | 
| 103 | 
            +
                        } else {
         | 
| 104 | 
            +
                            return true;
         | 
| 105 | 
            +
                        }
         | 
| 106 | 
            +
                    }
         | 
| 107 | 
            +
                    return false;
         | 
| 108 | 
            +
                }
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                /**
         | 
| 111 | 
            +
                 * Should be called from <code>Application.onCreate()</code>.
         | 
| 112 | 
            +
                 * it returns quickly with little disk I/O.
         | 
| 113 | 
            +
                 */
         | 
| 114 | 
            +
                public static void validateClassPath(final Context app) {
         | 
| 115 | 
            +
                    try {
         | 
| 116 | 
            +
                        String[] arrJars = createSubDexList(app);
         | 
| 117 | 
            +
                        if(debug) {
         | 
| 118 | 
            +
                            Log.d(TAG, "validateClassPath : " + Arrays.deepToString(arrJars));
         | 
| 119 | 
            +
                        }
         | 
| 120 | 
            +
                        File apkFile = new File(app.getApplicationInfo().sourceDir);
         | 
| 121 | 
            +
                        final File dexDir = app.getDir(DIR_SUBDEX, Context.MODE_PRIVATE); // this API creates the directory if not exist
         | 
| 122 | 
            +
                        dexOptRequired = shouldDexOpt(apkFile, dexDir, arrJars);
         | 
| 123 | 
            +
                        if (dexOptRequired) {
         | 
| 124 | 
            +
                            Thread dexOptThread = new Thread("DexDex - DexOpting for " + Arrays.deepToString(arrJars)) {
         | 
| 125 | 
            +
                                @Override
         | 
| 126 | 
            +
                                public void run() {
         | 
| 127 | 
            +
                                    DexDex.addAllJARsInAssets(app);
         | 
| 128 | 
            +
                                    // finished
         | 
| 129 | 
            +
                                    dexOptRequired = false;
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                                    if(dexOptProgressObserver!=null) {
         | 
| 132 | 
            +
                                        dexOptProgressObserver.update(null, PROGRESS_COMPLETE);
         | 
| 133 | 
            +
                                        dexOptProgressObserver = null;
         | 
| 134 | 
            +
                                    }
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                                    if (uiBlockedActivity != null) {
         | 
| 137 | 
            +
                                        uiBlockedActivity.runOnUiThread(new Runnable() {
         | 
| 138 | 
            +
                                            @Override
         | 
| 139 | 
            +
                                            public void run() {
         | 
| 140 | 
            +
                                                // FIXME(uwe): Simplify when we stop supporting android-11
         | 
| 141 | 
            +
                                                // if (Build.VERSION.SDK_INT < 11) {
         | 
| 142 | 
            +
                                                    Intent callerIntent = uiBlockedActivity.getIntent();
         | 
| 143 | 
            +
                                                    uiBlockedActivity.finish();
         | 
| 144 | 
            +
                                                    uiBlockedActivity.startActivity(callerIntent);
         | 
| 145 | 
            +
                                                // } else {
         | 
| 146 | 
            +
                                                //     uiBlockedActivity.recreate();
         | 
| 147 | 
            +
                                                // }
         | 
| 148 | 
            +
                                                // EMXIF
         | 
| 149 | 
            +
                                                uiBlockedActivity = null;
         | 
| 150 | 
            +
                                            }
         | 
| 151 | 
            +
                                        });
         | 
| 152 | 
            +
                                    }
         | 
| 153 | 
            +
                                }
         | 
| 154 | 
            +
                            };
         | 
| 155 | 
            +
                            dexOptThread.start();
         | 
| 156 | 
            +
                        } else {
         | 
| 157 | 
            +
                            // all dex JAR are stable
         | 
| 158 | 
            +
                            appendOdexesToClassPath(app, dexDir, arrJars);
         | 
| 159 | 
            +
                        }
         | 
| 160 | 
            +
                        if(debug) {
         | 
| 161 | 
            +
                            Log.d(TAG, "validateClassPath - dexDir : " + dexDir);
         | 
| 162 | 
            +
                        }
         | 
| 163 | 
            +
                    } catch (IOException ex) {
         | 
| 164 | 
            +
                        throw new RuntimeException(ex);
         | 
| 165 | 
            +
                    }
         | 
| 166 | 
            +
                }
         | 
| 167 | 
            +
             | 
| 168 | 
            +
                /** find and append all JARs */
         | 
| 169 | 
            +
                public static void addAllJARsInAssets(final Context cxt) {
         | 
| 170 | 
            +
                    try {
         | 
| 171 | 
            +
                        if(debug) {
         | 
| 172 | 
            +
                            Log.d(TAG, "addAllJARsInAssets on " + Thread.currentThread());
         | 
| 173 | 
            +
                        }
         | 
| 174 | 
            +
                        String[] arrJars = createSubDexList(cxt);
         | 
| 175 | 
            +
                        copyJarsFromAssets(cxt, arrJars);
         | 
| 176 | 
            +
                    } catch (IOException e) {
         | 
| 177 | 
            +
                        throw new RuntimeException(e);
         | 
| 178 | 
            +
                    }
         | 
| 179 | 
            +
                }
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                private static String[] createSubDexList(final Context cxt) throws IOException {
         | 
| 182 | 
            +
                    String[] files = cxt.getAssets().list("");
         | 
| 183 | 
            +
                    ArrayList<String> jarList = new ArrayList<String>();
         | 
| 184 | 
            +
                    for (int i = 0; i < files.length; i++) {
         | 
| 185 | 
            +
                        String jar = files[i];
         | 
| 186 | 
            +
                        if (jar.endsWith(".jar")) {
         | 
| 187 | 
            +
                            jarList.add(jar);
         | 
| 188 | 
            +
                        }
         | 
| 189 | 
            +
                    }
         | 
| 190 | 
            +
                    String[] arrJars = new String[jarList.size()];
         | 
| 191 | 
            +
                    jarList.toArray(arrJars);
         | 
| 192 | 
            +
                    return arrJars;
         | 
| 193 | 
            +
                }
         | 
| 194 | 
            +
             | 
| 195 | 
            +
                /**
         | 
| 196 | 
            +
                 * MUST be called on non-Main Thread
         | 
| 197 | 
            +
                 * @param names array of file names in 'assets' directory
         | 
| 198 | 
            +
                 */
         | 
| 199 | 
            +
                public static void copyJarsFromAssets(final Context cxt, final String[] names) {
         | 
| 200 | 
            +
                    if(debug) {
         | 
| 201 | 
            +
                        Log.d(TAG, "copyJarsFromAssets(" + Arrays.deepToString(names) + ")");
         | 
| 202 | 
            +
                    }
         | 
| 203 | 
            +
                    final File dexDir = cxt.getDir(DIR_SUBDEX, Context.MODE_PRIVATE); // this API creates the directory if not exist
         | 
| 204 | 
            +
                    File apkFile = new File(cxt.getApplicationInfo().sourceDir);
         | 
| 205 | 
            +
                    // should copy subdex JARs to dexDir?
         | 
| 206 | 
            +
                    final boolean shouldInit = shouldDexOpt(apkFile, dexDir, names);
         | 
| 207 | 
            +
                    if (shouldInit) {
         | 
| 208 | 
            +
                        try {
         | 
| 209 | 
            +
                            copyToInternal(cxt, dexDir, names);
         | 
| 210 | 
            +
                            appendOdexesToClassPath(cxt, dexDir, names);
         | 
| 211 | 
            +
                        } catch (Exception e) {
         | 
| 212 | 
            +
                            e.printStackTrace();
         | 
| 213 | 
            +
                            throw new RuntimeException(e);
         | 
| 214 | 
            +
                        }
         | 
| 215 | 
            +
                    } else {
         | 
| 216 | 
            +
                        if (!inAppended(names)) {
         | 
| 217 | 
            +
                            appendOdexesToClassPath(cxt, dexDir, names);
         | 
| 218 | 
            +
                        }
         | 
| 219 | 
            +
                    }
         | 
| 220 | 
            +
                }
         | 
| 221 | 
            +
             | 
| 222 | 
            +
                /** checks if all <code>names</code> elements are in <code>theAppended</code> */
         | 
| 223 | 
            +
                private static boolean inAppended(String[] names) {
         | 
| 224 | 
            +
                    for (int i = 0; i < names.length; i++) {
         | 
| 225 | 
            +
                        if (!theAppended.contains(names[i])) {
         | 
| 226 | 
            +
                            return false;
         | 
| 227 | 
            +
                        }
         | 
| 228 | 
            +
                    }
         | 
| 229 | 
            +
                    return true;
         | 
| 230 | 
            +
                }
         | 
| 231 | 
            +
             | 
| 232 | 
            +
                /**
         | 
| 233 | 
            +
                 * append DexOptimized dex files to the classpath.
         | 
| 234 | 
            +
                 * @return true if additional DexOpt is required, false otherwise.
         | 
| 235 | 
            +
                 */
         | 
| 236 | 
            +
                private static boolean appendOdexesToClassPath(Context cxt, File dexDir, String[] names) {
         | 
| 237 | 
            +
                    // non-existing ZIP in classpath causes an exception on ICS
         | 
| 238 | 
            +
                    // so filter out the non-existent
         | 
| 239 | 
            +
                    String strDexDir = dexDir.getAbsolutePath();
         | 
| 240 | 
            +
                    ArrayList<String> jarPaths = new ArrayList<String>();
         | 
| 241 | 
            +
                    for (int i = 0; i < names.length; i++) {
         | 
| 242 | 
            +
                        String jarPath = strDexDir + '/' + names[i];
         | 
| 243 | 
            +
                        File f = new File(jarPath);
         | 
| 244 | 
            +
                        if (f.isFile()) {
         | 
| 245 | 
            +
                            jarPaths.add(jarPath);
         | 
| 246 | 
            +
                        }
         | 
| 247 | 
            +
                    }
         | 
| 248 | 
            +
             | 
| 249 | 
            +
                    String[] jarsOfDex = new String[jarPaths.size()];
         | 
| 250 | 
            +
                    jarPaths.toArray(jarsOfDex);
         | 
| 251 | 
            +
             | 
| 252 | 
            +
                    PathClassLoader pcl = (PathClassLoader) cxt.getClassLoader();
         | 
| 253 | 
            +
                    // do something dangerous
         | 
| 254 | 
            +
                    try {
         | 
| 255 | 
            +
                        if (Build.VERSION.SDK_INT < SDK_INT_ICS) {
         | 
| 256 | 
            +
                            FrameworkHack.appendDexListImplUnderICS(jarsOfDex, pcl, dexDir);
         | 
| 257 | 
            +
                        } else { // ICS+
         | 
| 258 | 
            +
                            boolean kitkatPlus = Build.VERSION.SDK_INT >= SDK_INT_KITKAT;
         | 
| 259 | 
            +
                            ArrayList<File> jarFiles = DexDex.strings2Files(jarsOfDex);
         | 
| 260 | 
            +
                            FrameworkHack.appendDexListImplICS(jarFiles, pcl, dexDir, kitkatPlus);
         | 
| 261 | 
            +
                        }
         | 
| 262 | 
            +
                        // update theAppended if succeeded to prevent duplicated classpath entry
         | 
| 263 | 
            +
                        for (String jarName : names) {
         | 
| 264 | 
            +
                            theAppended.add(jarName);
         | 
| 265 | 
            +
                        }
         | 
| 266 | 
            +
                        if(debug) {
         | 
| 267 | 
            +
                            Log.d(TAG, "appendOdexesToClassPath completed : " + pcl);
         | 
| 268 | 
            +
                            Log.d(TAG, "theAppended : " + theAppended);
         | 
| 269 | 
            +
                        }
         | 
| 270 | 
            +
                    } catch (Exception ex) {
         | 
| 271 | 
            +
                        throw new RuntimeException(ex);
         | 
| 272 | 
            +
                    }
         | 
| 273 | 
            +
                    return true;
         | 
| 274 | 
            +
                }
         | 
| 275 | 
            +
             | 
| 276 | 
            +
                private static void copyToInternal(Context cxt, File destDir, String[] names) {
         | 
| 277 | 
            +
                    String strDestDir = destDir.getAbsolutePath();
         | 
| 278 | 
            +
                    AssetManager assets = cxt.getAssets();
         | 
| 279 | 
            +
                    byte[] buf = new byte[BUF_SIZE];
         | 
| 280 | 
            +
                    for (int i = 0; i < names.length; i++) {
         | 
| 281 | 
            +
                        String name = names[i];
         | 
| 282 | 
            +
                        String destPath = strDestDir + '/' + name;
         | 
| 283 | 
            +
             | 
| 284 | 
            +
                        try {
         | 
| 285 | 
            +
                            BufferedInputStream bis = new BufferedInputStream(assets.open(name));
         | 
| 286 | 
            +
                            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destPath));
         | 
| 287 | 
            +
                            int len;
         | 
| 288 | 
            +
                            while ((len = bis.read(buf, 0, BUF_SIZE)) > 0) {
         | 
| 289 | 
            +
                                bos.write(buf, 0, len);
         | 
| 290 | 
            +
                            }
         | 
| 291 | 
            +
                            bis.close();
         | 
| 292 | 
            +
                            bos.close();
         | 
| 293 | 
            +
                        } catch (IOException ioe) {
         | 
| 294 | 
            +
                            ioe.printStackTrace();
         | 
| 295 | 
            +
                        }
         | 
| 296 | 
            +
                    }
         | 
| 297 | 
            +
                    destDir.setLastModified(System.currentTimeMillis());
         | 
| 298 | 
            +
                }
         | 
| 299 | 
            +
             | 
| 300 | 
            +
                private static ArrayList<File> strings2Files(String[] paths) {
         | 
| 301 | 
            +
                    ArrayList<File> result = new ArrayList<File>(paths.length);
         | 
| 302 | 
            +
                    int size = paths.length;
         | 
| 303 | 
            +
                    for (int i = 0; i < size; i++) {
         | 
| 304 | 
            +
                        result.add(new File(paths[i]));
         | 
| 305 | 
            +
                    }
         | 
| 306 | 
            +
                    return result;
         | 
| 307 | 
            +
                }
         | 
| 308 | 
            +
             | 
| 309 | 
            +
                public static void showUiBlocker(Activity startActivity, CharSequence title, CharSequence msg) {
         | 
| 310 | 
            +
                    if(debug) {
         | 
| 311 | 
            +
                        Log.d(TAG, "showUiBlocker() for " + startActivity);
         | 
| 312 | 
            +
                    }
         | 
| 313 | 
            +
                    uiBlockedActivity = startActivity;
         | 
| 314 | 
            +
                    final ProgressDialog progressDialog = new ProgressDialog(startActivity);
         | 
| 315 | 
            +
                    progressDialog.setMessage(msg);
         | 
| 316 | 
            +
                    progressDialog.setTitle(title);
         | 
| 317 | 
            +
                    progressDialog.setIndeterminate(true);
         | 
| 318 | 
            +
                    dexOptProgressObserver = new Observer() {
         | 
| 319 | 
            +
                        @Override
         | 
| 320 | 
            +
                        public void update(Observable observable, Object o) {
         | 
| 321 | 
            +
                            if(o==Integer.valueOf(PROGRESS_COMPLETE)) {
         | 
| 322 | 
            +
                                progressDialog.dismiss();
         | 
| 323 | 
            +
                            }
         | 
| 324 | 
            +
                        }
         | 
| 325 | 
            +
                    };
         | 
| 326 | 
            +
                    
         | 
| 327 | 
            +
                    progressDialog.show();
         | 
| 328 | 
            +
                }
         | 
| 329 | 
            +
            }
         | 
| @@ -0,0 +1,177 @@ | |
| 1 | 
            +
            /*
         | 
| 2 | 
            +
             * Copyright 2013 ThinkFree
         | 
| 3 | 
            +
             *
         | 
| 4 | 
            +
             * Licensed under the Apache License, Version 2.0 (the "License");
         | 
| 5 | 
            +
             * you may not use this file except in compliance with the License.
         | 
| 6 | 
            +
             * You may obtain a copy of the License at
         | 
| 7 | 
            +
             *
         | 
| 8 | 
            +
             *    http://www.apache.org/licenses/LICENSE-2.0
         | 
| 9 | 
            +
             *
         | 
| 10 | 
            +
             * Unless required by applicable law or agreed to in writing, software
         | 
| 11 | 
            +
             * distributed under the License is distributed on an "AS IS" BASIS,
         | 
| 12 | 
            +
             * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         | 
| 13 | 
            +
             * See the License for the specific language governing permissions and
         | 
| 14 | 
            +
             * limitations under the License.
         | 
| 15 | 
            +
             */
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            package org.ruboto;
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            import android.util.Log;
         | 
| 20 | 
            +
            import dalvik.system.DexFile;
         | 
| 21 | 
            +
            import dalvik.system.PathClassLoader;
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            import java.io.File;
         | 
| 24 | 
            +
            import java.lang.reflect.Array;
         | 
| 25 | 
            +
            import java.lang.reflect.Field;
         | 
| 26 | 
            +
            import java.lang.reflect.Method;
         | 
| 27 | 
            +
            import java.util.ArrayList;
         | 
| 28 | 
            +
            import java.util.Arrays;
         | 
| 29 | 
            +
            import java.util.zip.ZipFile;
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            /**
         | 
| 32 | 
            +
             * Collection of dirty codes. don't tell android team this mess.
         | 
| 33 | 
            +
             * @author Alan Goo
         | 
| 34 | 
            +
             */
         | 
| 35 | 
            +
            public class FrameworkHack {
         | 
| 36 | 
            +
                private static final String TAG = "FrameworkHack";
         | 
| 37 | 
            +
                public static boolean debug = false;
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                private FrameworkHack() {
         | 
| 40 | 
            +
                    // do not create an instance
         | 
| 41 | 
            +
                }
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                /**
         | 
| 44 | 
            +
                 * dalvik do not have security manager
         | 
| 45 | 
            +
                 */
         | 
| 46 | 
            +
                private static void forceSet(Object obj, Field f, Object val) throws IllegalAccessException {
         | 
| 47 | 
            +
                    f.setAccessible(true);
         | 
| 48 | 
            +
                    f.set(obj, val);
         | 
| 49 | 
            +
                }
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                private static Object forceGetFirst(Object obj, Field fArray) throws IllegalAccessException {
         | 
| 52 | 
            +
                    fArray.setAccessible(true);
         | 
| 53 | 
            +
                    Object[] vArray = (Object[]) fArray.get(obj);
         | 
| 54 | 
            +
                    return vArray[0];
         | 
| 55 | 
            +
                }
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                private static String joinPaths(String[] paths) {
         | 
| 58 | 
            +
                    if (paths == null) {
         | 
| 59 | 
            +
                        return "";
         | 
| 60 | 
            +
                    }
         | 
| 61 | 
            +
                    StringBuilder buf = new StringBuilder();
         | 
| 62 | 
            +
                    for (int i = 0; i < paths.length; i++) {
         | 
| 63 | 
            +
                        buf.append(paths[i]);
         | 
| 64 | 
            +
                        buf.append(':');
         | 
| 65 | 
            +
                    }
         | 
| 66 | 
            +
                    return buf.toString();
         | 
| 67 | 
            +
                }
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                // https://android.googlesource.com/platform/dalvik/+/android-1.6_r1/libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java
         | 
| 70 | 
            +
                public static void appendDexListImplUnderICS(String[] jarPathsToAppend, PathClassLoader pcl, File optDir)
         | 
| 71 | 
            +
                        throws Exception {
         | 
| 72 | 
            +
                    int oldSize = 1; // gonna assume the original path had single entry for simplicity
         | 
| 73 | 
            +
                    Class pclClass = pcl.getClass();
         | 
| 74 | 
            +
                    Field fPath = pclClass.getDeclaredField("path");
         | 
| 75 | 
            +
                    fPath.setAccessible(true);
         | 
| 76 | 
            +
                    String orgPath = fPath.get(pcl).toString();
         | 
| 77 | 
            +
                    String pathToAdd = joinPaths(jarPathsToAppend);
         | 
| 78 | 
            +
                    String path = orgPath + ':' + pathToAdd;
         | 
| 79 | 
            +
                    forceSet(pcl, fPath, path);
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                    boolean wantDex = System.getProperty("android.vm.dexfile", "").equals("true");
         | 
| 82 | 
            +
                    File[] files = new File[oldSize + jarPathsToAppend.length];
         | 
| 83 | 
            +
                    ZipFile[] zips = new ZipFile[oldSize + jarPathsToAppend.length];
         | 
| 84 | 
            +
                    DexFile[] dexs = new DexFile[oldSize + jarPathsToAppend.length];
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                    Field fmPaths = pclClass.getDeclaredField("mPaths");
         | 
| 87 | 
            +
                    String[] newMPaths = new String[oldSize + jarPathsToAppend.length];
         | 
| 88 | 
            +
                    // set originals
         | 
| 89 | 
            +
                    newMPaths[0] = (String) forceGetFirst(pcl, fmPaths);
         | 
| 90 | 
            +
                    forceSet(pcl, fmPaths, newMPaths);
         | 
| 91 | 
            +
                    Field fmFiles = pclClass.getDeclaredField("mFiles");
         | 
| 92 | 
            +
                    files[0] = (File) forceGetFirst(pcl, fmFiles);
         | 
| 93 | 
            +
                    Field fmZips = pclClass.getDeclaredField("mZips");
         | 
| 94 | 
            +
                    zips[0] = (ZipFile) forceGetFirst(pcl, fmZips);
         | 
| 95 | 
            +
                    Field fmDexs = pclClass.getDeclaredField("mDexs");
         | 
| 96 | 
            +
                    dexs[0] = (DexFile) forceGetFirst(pcl, fmDexs);
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                    for (int i = 0; i < jarPathsToAppend.length; i++) {
         | 
| 99 | 
            +
                        newMPaths[oldSize + i] = jarPathsToAppend[i];
         | 
| 100 | 
            +
                        File pathFile = new File(jarPathsToAppend[i]);
         | 
| 101 | 
            +
                        files[oldSize + i] = pathFile;
         | 
| 102 | 
            +
                        zips[oldSize + i] = new ZipFile(pathFile);
         | 
| 103 | 
            +
                        if (wantDex) {
         | 
| 104 | 
            +
                            String outDexName = pathFile.getName() + ".dex";
         | 
| 105 | 
            +
                            File outFile = new File(optDir, outDexName);
         | 
| 106 | 
            +
                            dexs[oldSize + i] = DexFile.loadDex(pathFile.getAbsolutePath(), outFile.getAbsolutePath(), 0);
         | 
| 107 | 
            +
                        }
         | 
| 108 | 
            +
                    }
         | 
| 109 | 
            +
                    forceSet(pcl, fmFiles, files);
         | 
| 110 | 
            +
                    forceSet(pcl, fmZips, zips);
         | 
| 111 | 
            +
                    forceSet(pcl, fmDexs, dexs);
         | 
| 112 | 
            +
                }
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                // https://android.googlesource.com/platform/libcore/+/master/libdvm/src/main/java/dalvik/system/BaseDexClassLoader.java
         | 
| 115 | 
            +
                // https://android.googlesource.com/platform/libcore/+/master/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
         | 
| 116 | 
            +
                public static void appendDexListImplICS(ArrayList<File> jarFiles, PathClassLoader pcl, File optDir,
         | 
| 117 | 
            +
                                                        boolean kitkatPlus) throws Exception {
         | 
| 118 | 
            +
                    if(debug) {
         | 
| 119 | 
            +
                        Log.d(TAG, "appendDexListImplICS(" + jarFiles);
         | 
| 120 | 
            +
                    }
         | 
| 121 | 
            +
                    // to save original values
         | 
| 122 | 
            +
                    Class bdclClass = Class.forName("dalvik.system.BaseDexClassLoader");
         | 
| 123 | 
            +
                    // ICS+ - pathList
         | 
| 124 | 
            +
                    Field fPathList = bdclClass.getDeclaredField("pathList");
         | 
| 125 | 
            +
                    fPathList.setAccessible(true);
         | 
| 126 | 
            +
                    Object dplObj = fPathList.get(pcl);
         | 
| 127 | 
            +
                    // to call DexPathList.makeDexElements() for additional jar(apk)s
         | 
| 128 | 
            +
                    Class dplClass = dplObj.getClass();
         | 
| 129 | 
            +
                    Field fDexElements = dplClass.getDeclaredField("dexElements");
         | 
| 130 | 
            +
                    fDexElements.setAccessible(true);
         | 
| 131 | 
            +
                    Object objOrgDexElements = fDexElements.get(dplObj);
         | 
| 132 | 
            +
                    int orgDexCount = Array.getLength(objOrgDexElements);
         | 
| 133 | 
            +
                    if(debug) {
         | 
| 134 | 
            +
                        Log.d(TAG, "orgDexCount : " + orgDexCount);
         | 
| 135 | 
            +
                        debugDexElements(objOrgDexElements);
         | 
| 136 | 
            +
                    }
         | 
| 137 | 
            +
                    Class clazzElement = Class.forName("dalvik.system.DexPathList$Element");
         | 
| 138 | 
            +
                    // create new merged array
         | 
| 139 | 
            +
                    int jarCount = jarFiles.size();
         | 
| 140 | 
            +
                    Object newDexElemArray = Array.newInstance(clazzElement, orgDexCount + jarCount);
         | 
| 141 | 
            +
                    System.arraycopy(objOrgDexElements, 0, newDexElemArray, 0, orgDexCount);
         | 
| 142 | 
            +
                    Method mMakeDexElements = null;
         | 
| 143 | 
            +
                    if (kitkatPlus) {
         | 
| 144 | 
            +
                        mMakeDexElements =
         | 
| 145 | 
            +
                                dplClass.getDeclaredMethod("makeDexElements", ArrayList.class, File.class, ArrayList.class);
         | 
| 146 | 
            +
                    } else {
         | 
| 147 | 
            +
                        mMakeDexElements = dplClass.getDeclaredMethod("makeDexElements", ArrayList.class, File.class);
         | 
| 148 | 
            +
                    }
         | 
| 149 | 
            +
                    mMakeDexElements.setAccessible(true);
         | 
| 150 | 
            +
                    Object elemsToAdd;
         | 
| 151 | 
            +
                    if (kitkatPlus) {
         | 
| 152 | 
            +
                        elemsToAdd = mMakeDexElements.invoke(null, jarFiles, optDir, new ArrayList());
         | 
| 153 | 
            +
                    } else {
         | 
| 154 | 
            +
                        elemsToAdd = mMakeDexElements.invoke(null, jarFiles, optDir);
         | 
| 155 | 
            +
                    }
         | 
| 156 | 
            +
                    for (int i = 0; i < jarCount; i++) {
         | 
| 157 | 
            +
                        int pos = orgDexCount + i;
         | 
| 158 | 
            +
                        Object elemToAdd = Array.get(elemsToAdd, i);
         | 
| 159 | 
            +
                        Array.set(newDexElemArray, pos, elemToAdd);
         | 
| 160 | 
            +
                    }
         | 
| 161 | 
            +
                    if(debug) {
         | 
| 162 | 
            +
                        Log.d(TAG, "appendDexListImplICS() " + Arrays.deepToString((Object[]) newDexElemArray));
         | 
| 163 | 
            +
                    }
         | 
| 164 | 
            +
                    forceSet(dplObj, fDexElements, newDexElemArray);
         | 
| 165 | 
            +
                }
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                private static void debugDexElements(Object dexElements) throws Exception {
         | 
| 168 | 
            +
                    Object[] objArray = (Object[]) dexElements;
         | 
| 169 | 
            +
                    Class clazzElement = Class.forName("dalvik.system.DexPathList$Element");
         | 
| 170 | 
            +
                    Field fFile = clazzElement.getDeclaredField("file");
         | 
| 171 | 
            +
                    fFile.setAccessible(true);
         | 
| 172 | 
            +
                    for (int i = 0; i < objArray.length; i++) {
         | 
| 173 | 
            +
                        File f = (File) fFile.get(objArray[i]);
         | 
| 174 | 
            +
                        Log.d(TAG, "[" + i + "] " + f);
         | 
| 175 | 
            +
                    }
         | 
| 176 | 
            +
                }
         | 
| 177 | 
            +
            }
         | 
| @@ -11,6 +11,7 @@ import android.content.pm.ApplicationInfo; | |
| 11 11 | 
             
            import android.content.pm.PackageInfo;
         | 
| 12 12 | 
             
            import android.content.pm.PackageManager;
         | 
| 13 13 | 
             
            import android.content.pm.PackageManager.NameNotFoundException;
         | 
| 14 | 
            +
            import android.os.Build;
         | 
| 14 15 | 
             
            import android.os.Environment;
         | 
| 15 16 | 
             
            import dalvik.system.PathClassLoader;
         | 
| 16 17 |  | 
| @@ -135,7 +136,7 @@ public class JRubyAdapter { | |
| 135 136 | 
             
                        System.setProperty("jruby.backtrace.style", "normal"); // normal raw full mri
         | 
| 136 137 | 
             
                        System.setProperty("jruby.bytecode.version", "1.6");
         | 
| 137 138 | 
             
                        // BEGIN Ruboto RubyVersion
         | 
| 138 | 
            -
                        // System.setProperty("jruby.compat.version", " | 
| 139 | 
            +
                        // System.setProperty("jruby.compat.version", "RUBY2_0"); // RUBY1_9 is the default in JRuby 1.7
         | 
| 139 140 | 
             
                        // END Ruboto RubyVersion
         | 
| 140 141 | 
             
                        // System.setProperty("jruby.compile.backend", "DALVIK");
         | 
| 141 142 | 
             
                        System.setProperty("jruby.compile.mode", "OFF"); // OFF OFFIR JITIR? FORCE FORCEIR
         | 
| @@ -157,6 +158,18 @@ public class JRubyAdapter { | |
| 157 158 | 
             
                        System.setProperty("jruby.ji.upper.case.package.name.allowed", "true");
         | 
| 158 159 | 
             
                        System.setProperty("jruby.class.cache.path", appContext.getDir("dex", 0).getAbsolutePath());
         | 
| 159 160 |  | 
| 161 | 
            +
                        // FIXME(uwe): Simplify when we stop supporting android-15
         | 
| 162 | 
            +
                        if (Build.VERSION.SDK_INT >= 16) {
         | 
| 163 | 
            +
                            DexDex.debug = true;
         | 
| 164 | 
            +
                            DexDex.validateClassPath(appContext);
         | 
| 165 | 
            +
                            while (DexDex.dexOptRequired) {
         | 
| 166 | 
            +
                                System.out.println("Waiting for class loader setup...");
         | 
| 167 | 
            +
                                try {
         | 
| 168 | 
            +
                                    Thread.sleep(100);
         | 
| 169 | 
            +
                                } catch (InterruptedException ie) {}
         | 
| 170 | 
            +
                            }
         | 
| 171 | 
            +
                        }
         | 
| 172 | 
            +
             | 
| 160 173 | 
             
                        ClassLoader classLoader;
         | 
| 161 174 | 
             
                        Class<?> scriptingContainerClass;
         | 
| 162 175 | 
             
                        String apkName = null;
         | 
| @@ -240,6 +253,9 @@ public class JRubyAdapter { | |
| 240 253 | 
             
                                rubyInstanceConfigClass.getMethod("setError", PrintStream.class).invoke(config, output);
         | 
| 241 254 | 
             
                            }
         | 
| 242 255 |  | 
| 256 | 
            +
                            System.out.println("Ruby version: " + rubyInstanceConfigClass
         | 
| 257 | 
            +
                                    .getMethod("getCompatVersion").invoke(config));
         | 
| 258 | 
            +
             | 
| 243 259 | 
             
                            // This will become the global runtime and be used by our ScriptingContainer
         | 
| 244 260 | 
             
                            rubyClass.getMethod("newInstance", rubyInstanceConfigClass).invoke(null, config);
         | 
| 245 261 |  | 
| @@ -265,19 +281,27 @@ public class JRubyAdapter { | |
| 265 281 |  | 
| 266 282 | 
             
                            Thread.currentThread().setContextClassLoader(classLoader);
         | 
| 267 283 |  | 
| 284 | 
            +
                            String scriptsDir = scriptsDirName(appContext);
         | 
| 285 | 
            +
                            addLoadPath(scriptsDir);
         | 
| 268 286 | 
             
                            if (appContext.getFilesDir() != null) {
         | 
| 269 287 | 
             
                                String defaultCurrentDir = appContext.getFilesDir().getPath();
         | 
| 270 288 | 
             
                                Log.d("Setting JRuby current directory to " + defaultCurrentDir);
         | 
| 271 289 | 
             
                                callScriptingContainerMethod(Void.class, "setCurrentDirectory", defaultCurrentDir);
         | 
| 272 290 | 
             
                            } else {
         | 
| 273 291 | 
             
                                Log.e("Unable to find app files dir!");
         | 
| 292 | 
            +
                                if (new File(scriptsDir).exists()) {
         | 
| 293 | 
            +
                                    Log.d("Changing JRuby current directory to " + scriptsDir);
         | 
| 294 | 
            +
                                    callScriptingContainerMethod(Void.class, "setCurrentDirectory", scriptsDir);
         | 
| 295 | 
            +
                                }
         | 
| 274 296 | 
             
                            }
         | 
| 275 297 |  | 
| 276 | 
            -
                            addLoadPath(scriptsDirName(appContext));
         | 
| 277 298 | 
             
                            put("$package_name", appContext.getPackageName());
         | 
| 278 299 |  | 
| 279 300 | 
             
                            runScriptlet("::RUBOTO_JAVA_PROXIES = {}");
         | 
| 280 301 |  | 
| 302 | 
            +
                            System.out.println("JRuby version: " + Class.forName("org.jruby.runtime.Constants", true, scriptingContainerClass.getClassLoader())
         | 
| 303 | 
            +
                                    .getDeclaredField("VERSION").get(String.class));
         | 
| 304 | 
            +
             | 
| 281 305 | 
             
                            initialized = true;
         | 
| 282 306 | 
             
                        } catch (ClassNotFoundException e) {
         | 
| 283 307 | 
             
                            handleInitException(e);
         | 
| @@ -293,6 +317,8 @@ public class JRubyAdapter { | |
| 293 317 | 
             
                            handleInitException(e);
         | 
| 294 318 | 
             
                        } catch (NoSuchMethodException e) {
         | 
| 295 319 | 
             
                            handleInitException(e);
         | 
| 320 | 
            +
                        } catch (NoSuchFieldException e) {
         | 
| 321 | 
            +
                            handleInitException(e);
         | 
| 296 322 | 
             
                        }
         | 
| 297 323 | 
             
                    }
         | 
| 298 324 | 
             
                    return initialized;
         | 
| @@ -311,8 +337,6 @@ public class JRubyAdapter { | |
| 311 337 | 
             
                        Log.i("Added directory to load path: " + scriptsDir);
         | 
| 312 338 | 
             
                        Script.addDir(scriptsDir);
         | 
| 313 339 | 
             
                        runScriptlet("$:.unshift '" + scriptsDir + "' ; $:.uniq!");
         | 
| 314 | 
            -
                        Log.d("Changing JRuby current directory to " + scriptsDir);
         | 
| 315 | 
            -
                        callScriptingContainerMethod(Void.class, "setCurrentDirectory", scriptsDir);
         | 
| 316 340 | 
             
                        return true;
         | 
| 317 341 | 
             
                    } else {
         | 
| 318 342 | 
             
                        Log.i("Extra scripts dir not present: " + scriptsDir);
         | 
| @@ -34,7 +34,7 @@ public class ScriptLoader { | |
| 34 34 | 
             
                                Log.d("Found script.");
         | 
| 35 35 | 
             
                                rubyInstance = component;
         | 
| 36 36 | 
             
                                final String script = rubyScript.getContents();
         | 
| 37 | 
            -
                                boolean scriptContainsClass = script.matches("(?s).*class | 
| 37 | 
            +
                                boolean scriptContainsClass = script.matches("(?s).*class\\s+"
         | 
| 38 38 | 
             
                                        + component.getScriptInfo().getRubyClassName() + ".*");
         | 
| 39 39 | 
             
                                boolean hasBackingJavaClass = component.getScriptInfo().getRubyClassName()
         | 
| 40 40 | 
             
                                        .equals(component.getClass().getSimpleName());
         | 
| @@ -38,6 +38,7 @@ public class SplashActivity extends Activity { | |
| 38 38 | 
             
                    } catch (Exception e) {
         | 
| 39 39 | 
             
                        splash = -1;
         | 
| 40 40 | 
             
                    }
         | 
| 41 | 
            +
                    requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
         | 
| 41 42 | 
             
                    if (!JRubyAdapter.isInitialized()) {
         | 
| 42 43 | 
             
                        initJRuby(true);
         | 
| 43 44 | 
             
                    }
         | 
| @@ -164,7 +165,6 @@ public class SplashActivity extends Activity { | |
| 164 165 | 
             
                    if (loadingDialog == null) {
         | 
| 165 166 | 
             
                        if (splash > 0) {
         | 
| 166 167 | 
             
                            Log.i("Showing splash");
         | 
| 167 | 
            -
                            requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
         | 
| 168 168 | 
             
                            setContentView(splash);
         | 
| 169 169 | 
             
                        } else {
         | 
| 170 170 | 
             
                            Log.i("Showing progress");
         | 
| @@ -184,7 +184,6 @@ public class SplashActivity extends Activity { | |
| 184 184 | 
             
                    if (loadingDialog == null) {
         | 
| 185 185 | 
             
                        if (splash > 0) {
         | 
| 186 186 | 
             
                            Log.i("Showing splash");
         | 
| 187 | 
            -
                            requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
         | 
| 188 187 | 
             
                            setContentView(splash);
         | 
| 189 188 | 
             
                        } else {
         | 
| 190 189 | 
             
                            Log.i("Showing progress");
         | 
| @@ -43,6 +43,7 @@ module Ruboto::Activity::Reload | |
| 43 43 | 
             
                  file_string = reload_intent.get_string_extra('reload')
         | 
| 44 44 | 
             
                  if file_string
         | 
| 45 45 | 
             
                    scripts_dir = @activity.getExternalFilesDir(nil).absolute_path + '/scripts'
         | 
| 46 | 
            +
                    files = file_string.split(/(?<!&);/).map { |f| f.gsub(/&(.)/) { |m| m[1] } }
         | 
| 46 47 | 
             
                    files.each do |file|
         | 
| 47 48 | 
             
                      Log.d "load file: #{scripts_dir}/#{file}"
         | 
| 48 49 | 
             
                      load "#{scripts_dir}/#{file}"
         | 
| @@ -59,12 +59,18 @@ module Ruboto | |
| 59 59 |  | 
| 60 60 | 
             
                  script_name = options.delete(:script)
         | 
| 61 61 | 
             
                  extras = options.delete(:extras)
         | 
| 62 | 
            +
                  flags = options.delete(:flags)
         | 
| 63 | 
            +
             | 
| 62 64 | 
             
                  raise "Unknown options: #{options}" unless options.empty?
         | 
| 63 65 |  | 
| 64 | 
            -
                  if class_name.nil? | 
| 65 | 
            -
                     | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 66 | 
            +
                  if class_name.nil?
         | 
| 67 | 
            +
                    if block_given?
         | 
| 68 | 
            +
                      src_desc = source_descriptor(block)
         | 
| 69 | 
            +
                      class_name =
         | 
| 70 | 
            +
                          "#{java_class.name.split('::').last}_#{src_desc[0].split('/').last.gsub(/[.-]+/, '_')}_#{src_desc[1]}"
         | 
| 71 | 
            +
                    else
         | 
| 72 | 
            +
                      class_name = java_class.name.split('::').last
         | 
| 73 | 
            +
                    end
         | 
| 68 74 | 
             
                  end
         | 
| 69 75 |  | 
| 70 76 | 
             
                  class_name = class_name.to_s
         | 
| @@ -76,6 +82,7 @@ module Ruboto | |
| 76 82 | 
             
                  end
         | 
| 77 83 | 
             
                  i = android.content.Intent.new
         | 
| 78 84 | 
             
                  i.setClass self, java_class.java_class
         | 
| 85 | 
            +
                  i.add_flags(flags) if flags
         | 
| 79 86 | 
             
                  i.putExtra(Ruboto::THEME_KEY, theme) if theme
         | 
| 80 87 | 
             
                  i.putExtra(Ruboto::CLASS_NAME_KEY, class_name) if class_name
         | 
| 81 88 | 
             
                  i.putExtra(Ruboto::SCRIPT_NAME_KEY, script_name) if script_name
         | 
| @@ -124,4 +131,3 @@ end | |
| 124 131 | 
             
            java_import 'android.app.Activity'
         | 
| 125 132 | 
             
            java_import 'org.ruboto.RubotoActivity'
         | 
| 126 133 | 
             
            ruboto_configure_activity(RubotoActivity)
         | 
| 127 | 
            -
             |