Search in sources :

Example 1 with Inject

use of com.codename1.rad.annotations.Inject in project CodenameOne by codenameone.

the class AndroidGradleBuilder method build.

@Override
public boolean build(File sourceZip, final BuildRequest request) throws BuildException {
    debug("Request Args: ");
    debug("-----------------");
    for (String arg : request.getArgs()) {
        debug(arg + "=" + request.getArg(arg, null));
    }
    debug("-------------------");
    String defaultAndroidHome = isMac ? path(System.getProperty("user.home"), "Library", "Android", "sdk") : is_windows ? path(System.getProperty("user.home"), "AppData", "Local", "Android", "sdk") : // linux
    path(System.getProperty("user.home"), "Android", "Sdk");
    String androidHome = System.getenv("ANDROID_HOME");
    if (androidHome == null) {
        androidHome = defaultAndroidHome;
    }
    File androidSDKDir;
    String bat = "";
    if (is_windows) {
        bat = ".bat";
    }
    androidSDKDir = new File(androidHome);
    if (!androidSDKDir.exists()) {
        throw new BuildException("Cannot find Android SDK at " + androidHome + ".  Please install Android studio, or set the ANDROID_HOME environment variable to point to your android sdk directory.");
    }
    File sdkmanager = new File(androidSDKDir, path("tools", "bin", "sdkmanager" + bat));
    String sdkListStr;
    try {
        sdkListStr = execString(tmpDir, sdkmanager.getAbsolutePath(), "--list");
    } catch (Exception ex) {
        error("Failed to get SDK list using " + sdkmanager + ".  " + ex.getMessage(), ex);
        throw new BuildException("Failed to get SDK list using " + sdkmanager, ex);
    }
    Scanner sdkScanner = new Scanner(sdkListStr);
    List<String> installedPlatforms = new ArrayList<>();
    List<String> installedBuildToolsVersions = new ArrayList<>();
    while (sdkScanner.hasNextLine()) {
        String line = sdkScanner.nextLine().trim();
        if (line.startsWith("build-tools;")) {
            String[] columns = line.split("\\|");
            if (columns.length >= 4) {
                // If there are only 3 columns, then this is not referring to an installed build-tools
                // but an available one.
                String[] col1Parts = columns[0].split(";");
                if (col1Parts.length > 1) {
                    installedBuildToolsVersions.add(col1Parts[1].trim());
                }
            }
        } else if (line.startsWith("platforms;")) {
            String[] columns = line.split("\\|");
            if (columns.length > 1) {
                String[] col1Parts = columns[0].split(";");
                String platform = col1Parts[1].trim();
                if (platform.contains("-")) {
                    platform = platform.substring(platform.indexOf("-") + 1);
                }
                installedPlatforms.add(platform);
            }
        }
    }
    debug("Installed platforms: " + installedPlatforms);
    int maxBuildToolsVersionInt = 0;
    String maxBuildToolsVersion = "0";
    for (String ver : installedBuildToolsVersions) {
        int verInt = parseVersionStringAsInt(ver);
        if (verInt > maxBuildToolsVersionInt) {
            maxBuildToolsVersion = ver;
            maxBuildToolsVersionInt = verInt;
        }
    }
    int maxPlatformVersionInt = 0;
    String maxPlatformVersion = "0";
    for (String ver : installedPlatforms) {
        int verInt = parseVersionStringAsInt(ver);
        if (verInt > maxPlatformVersionInt) {
            maxPlatformVersionInt = verInt;
            maxPlatformVersion = ver;
        }
    }
    if (maxPlatformVersionInt == 0) {
        maxPlatformVersionInt = 30;
        maxPlatformVersion = "30";
    }
    if (maxBuildToolsVersionInt == 0) {
        maxBuildToolsVersionInt = 30;
        maxBuildToolsVersion = "30";
    }
    useAndroidX = request.getArg("android.useAndroidX", "false").equals("true");
    migrateToAndroidX = useAndroidX && request.getArg("android.migrateToAndroidX", "true").equals("true");
    buildToolsVersionInt = maxBuildToolsVersionInt;
    this.buildToolsVersion = request.getArg("android.buildToolsVersion", "" + maxBuildToolsVersion);
    String buildToolsVersionIntStr = this.buildToolsVersion;
    if (buildToolsVersionIntStr.indexOf(".") > 1) {
        buildToolsVersionIntStr = buildToolsVersionIntStr.substring(0, buildToolsVersionIntStr.indexOf("."));
    }
    buildToolsVersionInt = Integer.parseInt(buildToolsVersionIntStr.replaceAll("[^0-9]", ""));
    if (useAndroidX && buildToolsVersionInt < 29) {
        buildToolsVersionInt = 29;
        this.buildToolsVersion = "29";
    } else if (buildToolsVersionInt > 28 && !useAndroidX) {
        useAndroidX = true;
        migrateToAndroidX = useAndroidX && request.getArg("android.migrateToAndroidX", "true").equals("true");
    }
    debug("Effective build tools version = " + this.buildToolsVersion);
    // Augment the xpermissions request arg with explicit android.permissions.XXX build hints
    xPermissions = request.getArg("android.xpermissions", "");
    debug("Adding android permissions...");
    for (String xPerm : ANDROID_PERMISSIONS) {
        String permName = xPerm.substring(xPerm.lastIndexOf(".") + 1);
        if (request.getArg("android.permission." + permName, "false").equals("true")) {
            debug("Found permission " + permName);
            String maxSdk = request.getArg("android.permission." + permName + ".maxSdkVersion", "");
            String required = request.getArg("android.permission." + permName + ".required", "");
            String addString = "    <uses-permission android:name=\"" + xPerm + "\" ";
            if (!"".equals(required)) {
                addString += "android:required=\"" + required + "\" ";
            }
            if (!"".equals(maxSdk)) {
                addString += "android:maxSdkVersion=\"" + maxSdk + "\" ";
            }
            addString += "/>\n";
            xPermissions += permissionAdd(request, xPerm, addString);
        }
    }
    File tmpFile = getBuildDirectory();
    if (tmpFile == null) {
        throw new IllegalStateException("Build directory must be set before running build.");
    }
    if (tmpFile.exists()) {
        delTree(tmpFile);
    }
    tmpFile.mkdirs();
    File managedGradleHome = new File(path(System.getProperty("user.home"), ".codenameone", "gradle"));
    String gradleHome = System.getenv("GRADLE_HOME");
    if (gradleHome == null && managedGradleHome.exists()) {
        gradleHome = managedGradleHome.getAbsolutePath();
    }
    String gradleExe = System.getenv("GRADLE_PATH");
    if (gradleExe == null) {
        if (gradleHome != null) {
            gradleExe = new File(gradleHome + File.separator + "bin" + File.separator + "gradle" + bat).getAbsolutePath();
        } else {
            gradleExe = "gradle";
        }
    }
    if (PREFER_MANAGED_GRADLE) {
        debug("PREFER_MANAGED_GRADLE flag is set.  Ignoring GRADLE_HOME and GRADLE_PATH environment variables.  Using managed gradle at " + managedGradleHome + " instead");
        gradleHome = managedGradleHome.getAbsolutePath();
        gradleExe = new File(managedGradleHome, path("bin", "gradle" + bat)).getAbsolutePath();
    }
    String gradleVersion;
    try {
        gradleVersion = getGradleVersion(gradleExe);
    } catch (Exception ex) {
        gradleVersion = "0";
    }
    debug("FOUND gradleVersion " + gradleVersion);
    int gradleVersionInt = parseVersionStringAsInt(gradleVersion);
    debug("Found gradleVersionInt=" + gradleVersionInt);
    if (gradleVersionInt < MIN_GRADLE_VERSION) {
        // The minimum version is too low.
        if (managedGradleHome.exists()) {
            gradleExe = new File(managedGradleHome, path("bin", "gradle" + bat)).getAbsolutePath();
            try {
                gradleVersion = getGradleVersion(gradleExe);
            } catch (Exception ex) {
                gradleVersion = "0";
            }
            gradleVersionInt = parseVersionStringAsInt(gradleVersion);
        }
        if (gradleVersionInt < MIN_GRADLE_VERSION) {
            if (managedGradleHome.exists()) {
                delTree(managedGradleHome);
            }
            File gradleZip = new File(managedGradleHome + ".zip");
            if (gradleZip.exists()) {
                gradleZip.delete();
            }
            try {
                log("Downloading gradle distribution from " + gradleDistributionUrl);
                FileUtils.copyURLToFile(new URL(gradleDistributionUrl), gradleZip);
            } catch (Exception ex) {
                throw new BuildException("Failed to download gradle distribution from URL " + gradleDistributionUrl, ex);
            }
            try {
                ZipFile gradleZipFile = new ZipFile(gradleZip);
                File extracted = new File(path(gradleZip.getAbsolutePath() + "-extracted"));
                extracted.mkdir();
                gradleZipFile.extractAll(extracted.getAbsolutePath());
                gradleZip.delete();
                for (File extractedChild : extracted.listFiles()) {
                    if (extractedChild.getName().startsWith("gradle") && extractedChild.isDirectory()) {
                        extractedChild.renameTo(managedGradleHome);
                        break;
                    }
                }
            } catch (ZipException zex) {
                throw new BuildException("Failed to unzip gradle distribution after downloading it", zex);
            }
            if (!managedGradleHome.exists()) {
                throw new BuildException("There was a problem extracting the gradle distribution. Expected it to be extracted at " + managedGradleHome + ", but was not found");
            }
            File managedGradleExe = new File(managedGradleHome, path("bin", "gradle" + bat));
            if (!managedGradleExe.exists()) {
                throw new BuildException("Expected to find gradle executable at " + managedGradleExe + " after download and extraction, but it wasn't there.  Something about the gradle install must have failed.  Try again.");
            }
            gradleExe = managedGradleExe.getAbsolutePath();
            try {
                gradleVersion = getGradleVersion(gradleExe);
            } catch (Exception ex) {
                throw new BuildException("Failed to get gradle version even after downloading it from " + gradleDistributionUrl + ".  Something must have gone wrong with the gradle installation.");
            }
            gradleVersionInt = parseVersionStringAsInt(gradleVersion);
            if (gradleVersionInt < MIN_GRADLE_VERSION) {
                throw new BuildException("Required gradle version is " + MIN_GRADLE_VERSION + " but found version " + gradleVersion);
            }
        }
    }
    File androidToolsDir = new File(androidSDKDir, "tools");
    File androidCommand = new File(androidToolsDir, "android" + bat);
    File projectDir = new File(tmpFile, request.getMainClass());
    gradleProjectDirectory = projectDir;
    String androidVersion = "android-14";
    String defaultVersion = maxPlatformVersion;
    String usesLibrary = "        <uses-library android:name=\"org.apache.http.legacy\" android:required=\"false\" />\n";
    String targetNumber = request.getArg("android.targetSDKVersion", defaultVersion);
    if (!targetNumber.equals(defaultVersion)) {
        try {
            if (Integer.parseInt(targetNumber) < 28) {
                usesLibrary = "";
            }
        } catch (Exception err) {
        }
    }
    String targetSDKVersion = targetNumber;
    final int targetSDKVersionInt = Integer.parseInt(targetSDKVersion);
    if (targetSDKVersionInt > 14) {
        androidVersion = "android-" + targetSDKVersion;
    }
    targetSDKVersion = " android:targetSdkVersion=\"" + targetSDKVersion + "\" ";
    log("TargetSDKVersion=" + targetSDKVersion);
    String gradlePluginVersion = "1.3.1";
    if (gradleVersionInt < 3) {
        gradlePluginVersion = "2.0.0";
    } else {
        if (gradleVersionInt < 6) {
            if (useAndroidX) {
                gradlePluginVersion = "3.2.0";
            } else {
                gradlePluginVersion = "3.0.1";
            }
        } else {
            gradlePluginVersion = "4.1.1";
        }
    }
    boolean androidAppBundle = request.getArg("android.appBundle", gradleVersionInt >= 5 ? "true" : "false").equals("true");
    debug("gradlePluginVersion=" + gradlePluginVersion);
    projectDir = new File(projectDir, "app");
    File studioProjectDir = projectDir.getParentFile();
    if (isUnitTestMode()) {
        throw new BuildException("Unit Test mode not currently supported for local android builds.");
    } else {
        try {
            log("Creating AndroidStudioProject from template");
            if (studioProjectDir.exists()) {
                delTree(studioProjectDir);
            }
            createAndroidStudioProject(studioProjectDir);
        } catch (Exception ex) {
            error("Failed to create AndroidStudioProject: " + ex.getMessage(), ex);
            throw new BuildException("Failed to create android project", ex);
        }
    }
    File assetsDir = new File(projectDir + "/src/main", "assets");
    assetsDir.mkdirs();
    File resDir = new File(projectDir + "/src/main", "res");
    resDir.mkdirs();
    File valsDir = new File(resDir, "values");
    valsDir.mkdirs();
    File vals11Dir = null;
    vals11Dir = new File(resDir, "values-v11");
    vals11Dir.mkdirs();
    File vals21Dir = null;
    vals21Dir = new File(resDir, "values-v21");
    vals21Dir.mkdirs();
    File layoutDir = new File(resDir, "layout");
    layoutDir.mkdirs();
    File xmlDir = new File(resDir, "xml");
    xmlDir.mkdirs();
    File srcDir = new File(projectDir, "src/main/java");
    srcDir.mkdirs();
    File dummyClassesDir = new File(tmpFile, "Classes");
    dummyClassesDir.mkdirs();
    File libsDir = new File(projectDir, "libs");
    libsDir.mkdirs();
    try {
        debug("Extracting " + sourceZip);
        unzip(sourceZip, dummyClassesDir, assetsDir, srcDir, libsDir, xmlDir);
    } catch (Exception ex) {
        throw new BuildException("Failed to extract source zip " + sourceZip, ex);
    }
    File appDir = buildToolsVersionInt >= 27 ? new File(srcDir.getParentFile(), "app") : new File(libsDir.getParentFile(), "app");
    File googleServicesJson = new File(appDir, "google-services.json");
    googleServicesJson = new File(libsDir.getParentFile(), "google-services.json");
    try {
        if (!retrolambda(new File(System.getProperty("user.dir")), request, dummyClassesDir)) {
            return false;
        }
    } catch (Exception ex) {
        throw new BuildException("Failed to run retrolambda on classes", ex);
    }
    String additionalImports = request.getArg("android.activityClassImports", "");
    String additionalMembers = request.getArg("android.activityClassBody", "");
    String additionalKeyVals = "";
    String mopubActivities = "";
    String mopubBannerXML = "";
    String permissions = "";
    String telephonyRequired = "false";
    String aarDependencies = "";
    // move dependant projects to a separate directory
    File[] childs = libsDir.listFiles();
    for (int i = 0; i < childs.length; i++) {
        File file = childs[i];
        if (file.getName().endsWith(".andlib")) {
            throw new BuildException("andlib format is not supported anymore, use aar instead");
        }
        if (file.getName().endsWith(".aar")) {
            String name = file.getName().substring(0, file.getName().lastIndexOf("."));
            if (request.getArg("android.arrimplementation", "").contains(name)) {
                aarDependencies += "    implementation(name:'" + name + "', ext:'aar')\n";
            } else {
                aarDependencies += "    compile(name:'" + name + "', ext:'aar')\n";
            }
        }
    }
    String minSDK = request.getArg("android.min_sdk_version", "15");
    String facebookSupport = "";
    String facebookProguard = "";
    String facebookActivityMetaData = "";
    String facebookActivity = "";
    String facebookHashCode = "";
    boolean facebookSupported = request.getArg("facebook.appId", null) != null;
    if (facebookSupported) {
        facebookHashCode = "        try {\n" + "            android.content.pm.PackageInfo info = getPackageManager().getPackageInfo(\n" + "                  \"" + request.getPackageName() + "\", android.content.pm.PackageManager.GET_SIGNATURES);\n" + "            for (android.content.pm.Signature signature : info.signatures){\n" + "                   java.security.MessageDigest md = java.security.MessageDigest.getInstance(\"SHA\");\n" + "                   md.update(signature.toByteArray());\n" + "                   android.util.Log.d(\"KeyHash:\", android.util.Base64.encodeToString(md.digest(), android.util.Base64.DEFAULT));\n" + "                   Display.getInstance().setProperty(\"facebook_hash\", android.util.Base64.encodeToString(md.digest(), android.util.Base64.DEFAULT));\n" + "            }\n" + "        } catch (android.content.pm.PackageManager.NameNotFoundException e) {\n" + "            e.printStackTrace();\n" + "        } catch (java.security.NoSuchAlgorithmException e) {\n" + "            e.printStackTrace();\n" + "        }\n\n";
        String permissionsStr = request.getArg("android.facebook_permissions", "\"public_profile\",\"email\",\"user_friends\"");
        permissionsStr = request.getArg("and.facebook_permissions", permissionsStr);
        permissionsStr = permissionsStr.replace('"', ' ');
        facebookSupport = "Display.getInstance().setProperty(\"facebook_app_id\", \"" + request.getArg("facebook.appId", "706695982682332") + "\");\n" + "        Display.getInstance().setProperty(\"facebook_permissions\", \"" + permissionsStr + "\");\n" + " com.codename1.social.FacebookImpl.init();\n";
        facebookProguard = "-keep class com.facebook.** { *; }\n" + "-keepattributes Signature\n" + "-dontwarn bolts.**\n" + "-dontnote android.support.**\n" + "-dontnote androidx.**";
        facebookActivityMetaData = " <meta-data android:name=\"com.facebook.sdk.ApplicationId\" android:value=\"@string/facebook_app_id\"/>\n";
        facebookActivity = " <activity android:name=\"com.facebook.FacebookActivity\"/>\n";
        additionalKeyVals += "<string name=\"facebook_app_id\">" + request.getArg("facebook.appId", "706695982682332") + "</string>";
    }
    String googlePlayAdsMetaData = "";
    String googlePlayAdsActivity = "";
    String googlePlayObfuscation = "";
    String googleAdUnitId = request.getArg("android.googleAdUnitId", request.getArg("google.adUnitId", null));
    String googlePlayAdViewCode = "";
    if (googleAdUnitId != null && googleAdUnitId.length() > 0) {
        minSDK = maxInt("9", minSDK);
        googlePlayAdsMetaData = "<meta-data android:name=\"com.google.android.gms.version\" android:value=\"@integer/google_play_services_version\"/>";
        googlePlayAdsActivity = "<activity android:name=\"com.google.android.gms.ads.AdActivity\" android:configChanges=\"keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize\"/>";
        accessNetworkStatePermission = true;
        String testDevice = request.getArg("android.googleAdUnitTestDevice", "C6783E2486F0931D9D09FABC65094FDF");
        googlePlayAdViewCode = "            com.google.android.gms.ads.AdView adView = new com.google.android.gms.ads.AdView(this);\n" + "            adView.setAdUnitId(\"" + googleAdUnitId + "\");\n" + "            adView.setId(2002);\n" + "            adView.setAdSize(com.google.android.gms.ads.AdSize.SMART_BANNER);\n" + "            AndroidImplementation.setViewAboveBelow(null, adView, 0, com.google.android.gms.ads.AdSize.SMART_BANNER.getHeightInPixels(this));\n" + "            com.google.android.gms.ads.AdRequest adRequest = new com.google.android.gms.ads.AdRequest.Builder().addTestDevice(\"" + testDevice + "\").build();\n" + "            adView.loadAd(adRequest);\n";
        googlePlayObfuscation = "-keep class * extends java.util.ListResourceBundle {\n" + "    protected Object[][] getContents();\n" + "}\n" + "\n" + "-keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable {\n" + "    public static final *** NULL;\n" + "}\n" + "\n" + "-keepnames @com.google.android.gms.common.annotation.KeepName class *\n" + "-keepclassmembernames class * {\n" + "    @com.google.android.gms.common.annotation.KeepName *;\n" + "}\n" + "\n" + "-keepnames class * implements android.os.Parcelable {\n" + "    public static final ** CREATOR;\n" + "}\n";
    }
    playServicesVersion = request.getArg("android.playServicesVersion", playServicesVersion);
    final String playServicesValue = request.getArg("android.includeGPlayServices", null);
    playFlag = "true";
    gpsPermission = request.getArg("android.gpsPermission", "false").equals("true");
    try {
        scanClassesForPermissions(dummyClassesDir, new Executor.ClassScanner() {

            @Override
            public void usesClass(String cls) {
                if (cls.indexOf("com/codename1/notifications") == 0) {
                    recieveBootCompletedPermission = true;
                }
                if (cls.indexOf("com/codename1/capture") == 0) {
                    capturePermission = true;
                }
                if (cls.indexOf("com/codename1/ads") == 0) {
                    debug("Adding phone permission because of class " + cls);
                    phonePermission = true;
                }
                if (cls.indexOf("com/codename1/components/Ads") == 0) {
                    debug("Adding phone permission because of class " + cls);
                    phonePermission = true;
                }
                if (cls.indexOf("com/codename1/maps") == 0 || cls.indexOf("com/codename1/location") == 0) {
                    gpsPermission = true;
                }
                if (cls.indexOf("com/codename1/push") > -1) {
                    pushPermission = true;
                }
                if (cls.indexOf("com/codename1/contacts") > -1) {
                    contactsReadPermission = true;
                }
                if (cls.indexOf("com/codename1/payment") > -1) {
                    purchasePermissions = true;
                }
                if (cls.indexOf("com/codename1/location/Geofence") > -1) {
                    if (!"true".equals(playServicesValue)) {
                        // If play services are not currently "blanket" enabled
                        // we will enable them here
                        debug("Adding location playservice");
                        request.putArgument("android.location.minPlayServicesVersion", "12.0.1");
                        playServicesLocation = true;
                        playFlag = "false";
                        if (targetSDKVersionInt >= 29) {
                            backgroundLocationPermission = true;
                        }
                    }
                }
                if (cls.indexOf("com/codename1/social") > -1) {
                    credentialsPermission = true;
                    getAccountsPermission = true;
                }
            }

            @Override
            public void usesClassMethod(String cls, String method) {
                if (cls.indexOf("com/codename1/ui/Display") == 0 && (method.indexOf("vibrate") > -1 || method.indexOf("notifyStatusBar") > -1)) {
                    vibratePermission = true;
                }
                if ((cls.indexOf("com/codename1/media/MediaManager") == 0 && method.indexOf("createBackgroundMedia") > -1)) {
                    if (targetSDKVersionInt >= 28) {
                        foregroundServicePermission = true;
                    }
                }
                if ((cls.indexOf("com/codename1/ui/Display") == 0 && method.indexOf("createBackgroundMedia") > -1)) {
                    if (targetSDKVersionInt >= 28) {
                        foregroundServicePermission = true;
                    }
                }
                if (cls.indexOf("com/codename1/location/LocationManager") == 0 && (method.indexOf("addGeoFencing") > -1 || method.indexOf("setBackgroundLocationListener") > -1)) {
                    if (!"true".equals(playServicesValue)) {
                        if (targetSDKVersionInt >= 29) {
                            backgroundLocationPermission = true;
                        }
                    }
                }
                if (cls.indexOf("com/codename1/location/LocationManager") == 0 && (method.indexOf("addGeoFencing") > -1 || method.indexOf("getLocationManager") > -1)) {
                    if (!"true".equals(playServicesValue)) {
                        // If play services are not currently "blanket" enabled
                        // we will enable them here
                        debug("Adding location playservice");
                        request.putArgument("android.location.minPlayServicesVersion", "12.0.1");
                        playServicesLocation = true;
                        playFlag = "false";
                    }
                }
                if (cls.indexOf("com/codename1/media/MediaManager") == 0 && method.indexOf("setRemoteControlListener") > -1) {
                    debug("Adding wake lock permission due to use of MediaManager.setRemoteControlListener");
                    // smsPermission = true;
                    wakeLock = true;
                    addRemoteControlService = true;
                }
                if (cls.indexOf("com/codename1/ui/Display") == 0 && method.indexOf("getUdid") > -1) {
                    debug("Adding phone permission because of Display.getUdid method");
                    phonePermission = true;
                }
                if (cls.indexOf("com/codename1/ui/Display") == 0 && method.indexOf("getMsisdn") > -1) {
                    phonePermission = true;
                }
                if (cls.indexOf("com/codename1/ui/Display") == 0 && method.indexOf("getAllContacts") > -1) {
                    contactsReadPermission = true;
                }
                if (cls.indexOf("com/codename1/ui/Display") == 0 && method.indexOf("lockScreen") > -1) {
                    wakeLock = true;
                }
                if (cls.indexOf("com/codename1/ui/Display") == 0 && method.indexOf("setScreenSaverEnabled") > -1) {
                    wakeLock = true;
                }
                if (cls.indexOf("com/codename1/media/MediaManager") == 0 && method.indexOf("createMediaRecorder") > -1) {
                    recordAudio = true;
                }
                if (cls.indexOf("com/codename1/ui/Display") == 0 && method.indexOf("createMediaRecorder") > -1) {
                    recordAudio = true;
                }
                if (cls.indexOf("com/codename1/ui/Display") == 0 && method.indexOf("createContact") > -1) {
                    contactsWritePermission = true;
                }
                if (cls.indexOf("com/codename1/ui/Display") == 0 && method.indexOf("deleteContact") > -1) {
                    contactsWritePermission = true;
                }
                if (cls.indexOf("com/codename1/contacts/ContactsManager") == 0 && method.indexOf("createContact") > -1) {
                    contactsWritePermission = true;
                }
                if (cls.indexOf("com/codename1/contacts/ContactsManager") == 0 && method.indexOf("deleteContact") > -1) {
                    contactsWritePermission = true;
                }
            }
        });
    } catch (IOException ex) {
        throw new BuildException("An error occurred while trying to scan the classes for API usage.", ex);
    }
    boolean useFCM = pushPermission && "fcm".equalsIgnoreCase(request.getArg("android.messagingService", "fcm"));
    if (useFCM) {
        request.putArgument("android.fcm.minPlayServicesVersion", "12.0.1");
    }
    debug("Starting playServicesVersion " + playServicesVersion);
    for (String arg : request.getArgs()) {
        if (arg.endsWith(".minPlayServicesVersion")) {
            if (compareVersions(request.getArg(arg, null), playServicesVersion) > 0) {
                playServicesVersion = request.getArg(arg, null);
                debug("playServicesVersion increased to " + playServicesVersion + " due to " + arg);
            }
        }
    }
    request.putArgument("android.playServicesVersion", playServicesVersion);
    debug("-----USING PLAY SERVICES VERSION " + playServicesVersion + "----");
    if (useFCM) {
        if (!googleServicesJson.exists()) {
            error("google-services.json not found.  When using FCM for push notifications (i.e. android.messagingService=fcm), you must include valid google-services.json file.  Use the Firebase console to add Firebase messaging to your app.  https://console.firebase.google.com/u/0/ Then download the google-services.json file and place it in the native/android directory of your project. If you still want to use GCM (which no longer works) define the build hint android.messagingService=gcm", new RuntimeException());
            return false;
        }
        if (buildToolsVersionInt < 27) {
            error("FCM push notifications require build tools version 27 or higher.  Please set the android.buildToolsVersion to 27.0.0 or higher or remove the android.messagingService=fcm build hint.", new RuntimeException());
            return false;
        }
        if (!request.getArg("android.topDependency", "").contains("com.google.gms:google-services")) {
            request.putArgument("android.topDependency", request.getArg("android.topDependency", "") + "\n    classpath 'com.google.gms:google-services:4.0.1'\n");
        }
        if (!request.getArg("android.xgradle", "").contains("apply plugin: 'com.google.gms.google-services'")) {
            request.putArgument("android.xgradle", request.getArg("android.xgradle", "") + "\napply plugin: 'com.google.gms.google-services'\n");
        }
        if (!request.getArg("gradleDependencies", "").contains("com.google.firebase:firebase-core")) {
            debug("Adding firebase core to gradle dependencies.");
            debug("Play services version: " + request.getArg("var.android.playServicesVersion", ""));
            debug("gradleDependencies before: " + request.getArg("gradleDependencies", ""));
            request.putArgument("gradleDependencies", request.getArg("gradleDependencies", "") + "\ncompile \"com.google.firebase:firebase-core:${var.android.playServicesVersion}\"\n");
            debug("gradleDependencies after: " + request.getArg("gradleDependencies", ""));
        }
        if (!request.getArg("gradleDependencies", "").contains("com.google.firebase:firebase-messaging")) {
            request.putArgument("gradleDependencies", request.getArg("gradleDependencies", "") + "\ncompile \"com.google.firebase:firebase-messaging:${var.android.playServicesVersion}\"\n");
        }
    }
    // if a flag is declared we don't want the default play flag to be true
    if (request.getArg("android.playService.plus", null) != null || request.getArg("android.playService.auth", (googleServicesJson.exists()) ? "true" : null) != null || request.getArg("android.playService.base", null) != null || request.getArg("android.playService.identity", null) != null || request.getArg("android.playService.indexing", null) != null || request.getArg("android.playService.appInvite", null) != null || request.getArg("android.playService.analytics", null) != null || request.getArg("android.playService.cast", null) != null || request.getArg("android.playService.gcm", null) != null || request.getArg("android.playService.drive", null) != null || request.getArg("android.playService.fitness", null) != null || request.getArg("android.playService.location", null) != null || request.getArg("android.playService.maps", null) != null || request.getArg("android.playService.ads", null) != null || request.getArg("android.playService.vision", null) != null || request.getArg("android.playService.nearby", null) != null || request.getArg("android.playService.panorama", null) != null || request.getArg("android.playService.games", null) != null || request.getArg("android.playService.safetynet", null) != null || request.getArg("android.playService.wallet", null) != null || request.getArg("android.playService.wearable", null) != null || request.getArg("android.playService.ads", null) != null) {
        playFlag = "false";
    }
    boolean legacyGplayServicesMode = false;
    if (playServicesValue != null) {
        if (playServicesValue.equals("true")) {
            // compatibility mode...
            legacyGplayServicesMode = true;
            if (playFlag.equals("false")) {
                // legacy gplay can't be mixed with explicit gplay fail the build right now!
                if (googleServicesJson.exists()) {
                    debug("The android.playService.auth flag was automatically enabled because the project includes the google-services.json file");
                }
                error("Error: you can't use the build hint android.includeGPlayServices together with android.playService.* build hints. They are exclusive of one another. Please remove the old android.includeGPlayServices hint from your code or from the cn1lib that might have injected it", new RuntimeException());
                return false;
            }
            playFlag = "true";
        } else {
            playFlag = "false";
        }
    }
    playServicesPlus = request.getArg("android.playService.plus", "false").equals("true");
    playServicesAuth = request.getArg("android.playService.auth", (Boolean.valueOf(playFlag) || googleServicesJson.exists()) ? "true" : "false").equals("true");
    playServicesBase = request.getArg("android.playService.base", playFlag).equals("true");
    playServicesIdentity = request.getArg("android.playService.identity", "false").equals("true");
    playServicesIndexing = request.getArg("android.playService.indexing", "false").equals("true");
    playServicesInvite = request.getArg("android.playService.appInvite", "false").equals("true");
    playServicesAnalytics = request.getArg("android.playService.analytics", playFlag).equals("true");
    playServicesCast = request.getArg("android.playService.cast", "false").equals("true");
    playServicesGcm = request.getArg("android.playService.gcm", playFlag).equals("true") || request.getArg("gcm.sender_id", null) != null;
    playServicesDrive = request.getArg("android.playService.drive", "false").equals("true");
    playServicesFit = request.getArg("android.playService.fitness", "false").equals("true");
    playServicesLocation = playServicesLocation || request.getArg("android.playService.location", playFlag).equals("true");
    playServicesMaps = request.getArg("android.playService.maps", playFlag).equals("true");
    playServicesAds = request.getArg("android.playService.ads", playFlag).equals("true");
    if (request.getArg("android.googleAdUnitId", request.getArg("google.adUnitId", null)) != null) {
        playServicesAds = true;
    }
    playServicesVision = request.getArg("android.playService.vision", "false").equals("true");
    playServicesNearBy = request.getArg("android.playService.nearby", "false").equals("true");
    playServicesSafetyPanorama = request.getArg("android.playService.panorama", "false").equals("true");
    playServicesGames = request.getArg("android.playService.games", "false").equals("true");
    playServicesSafetyNet = request.getArg("android.playService.safetynet", "false").equals("true");
    playServicesWallet = request.getArg("android.playService.wallet", "false").equals("true");
    playServicesWear = request.getArg("android.playService.wearable", "false").equals("true");
    if (googleAdUnitId == null && playServicesAds) {
        minSDK = maxInt("9", minSDK);
        googlePlayAdsMetaData = "<meta-data android:name=\"com.google.android.gms.version\" android:value=\"@integer/google_play_services_version\"/>";
    }
    if (playServicesLocation) {
        debug("Play Services Location Enabled");
        googlePlayObfuscation += "-keep class com.codename1.location.AndroidLocationPlayServiceManager {\n" + "*;\n" + "}\n\n";
        googlePlayObfuscation += "-keep class com.codename1.location.BackgroundLocationHandler {\n" + "*;\n" + "}\n\n";
        googlePlayObfuscation += "-keep class com.codename1.location.BackgroundLocationBroadcastReceiver {\n" + "*;\n" + "}\n\n";
        googlePlayObfuscation += "-keep class com.codename1.impl.android.BackgroundFetchHandler {\n" + "*;\n" + "}\n\n";
        googlePlayObfuscation += "-keep class com.codename1.location.GeofenceHandler {\n" + "*;\n" + "}\n\n";
        googlePlayObfuscation += "-keep class com.codename1.location.CodenameOneBackgroundLocationActivity {\n" + "*;\n" + "}\n\n";
    } else {
        debug("Play services location disabled");
    }
    shouldIncludeGoogleImpl = playServicesAuth;
    if (shouldIncludeGoogleImpl) {
        googlePlayObfuscation += "-keep class com.codename1.social.GoogleImpl {\n" + "*;\n" + "}\n\n";
    }
    File stubFileSourceDir = new File(srcDir, request.getPackageName().replace('.', File.separatorChar));
    stubFileSourceDir.mkdirs();
    String headphonesVars = "";
    String headphonesOnResume = "";
    if (request.getArg("android.headphoneCallback", "false").equals("true")) {
        headphonesVars = "    HeadSetReceiver myHeadphoneReceiver;\n\n" + "    public static void headphonesConnected() {\n" + "        i.headphonesConnected();" + "    }" + "    public static void headphonesDisconnected() {\n" + "        i.headphonesDisconnected();" + "    }";
        headphonesOnResume = "        HeadSetReceiver myReceiver = new HeadSetReceiver();\n" + "        IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);\n" + "        registerReceiver(myReceiver, filter);\n";
        File headphonesFile = new File(stubFileSourceDir, "HeadSetReceiver.java");
        String stubSourceCode = "package " + request.getPackageName() + ";\n\n" + "import android.content.Context;\n" + "import android.content.Intent;\n" + "import android.content.IntentFilter;\n" + "import android.content.BroadcastReceiver;\n\n" + "public class HeadSetReceiver extends BroadcastReceiver {\n" + "    @Override public void onReceive(Context context, Intent intent) {\n" + "        if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) {\n" + "            int state = intent.getIntExtra(\"state\", -1);\n" + "            switch (state) {\n" + "            case 0:\n" + "                " + request.getMainClass() + "Stub.headphonesDisconnected();\n" + "                break;\n" + "            case 1:\n" + "                " + request.getMainClass() + "Stub.headphonesConnected();\n" + "                break;\n" + "            }\n" + "        }\n" + "    }\n" + "}";
        try {
            createFile(headphonesFile, stubSourceCode.getBytes());
        } catch (IOException ex) {
            throw new BuildException("Failed to create HeadSetReceiver class", ex);
        }
    }
    if (request.getArg("noExtraResources", "false").equals("true")) {
        new File(assetsDir, "CN1Resource.res").delete();
        new File(assetsDir, "androidTheme.res").delete();
        new File(assetsDir, "android_holo_light.res").delete();
    }
    if (getAndroidPortSrcJar() == null) {
        try {
            setAndroidPortSrcJar(getResourceAsFile("/com/codename1/android/android_port_sources.jar", ".jar"));
        } catch (IOException ex) {
            throw new BuildException("Failed to find android_port_sources.jar");
        }
    }
    if (!getAndroidPortSrcJar().exists()) {
        throw new IllegalStateException("Configuration error.  Cannot find androidPortSrcJar at " + getAndroidPortSrcJar());
    }
    try {
        unzip(androidPortSrcJar, srcDir, assetsDir, srcDir);
    } catch (IOException ex) {
        throw new BuildException("Failed to extract android port sources from " + androidPortSrcJar, ex);
    }
    // We need to choose the correct PlayServices class file for the version of play services
    // we are building for.
    File androidImpl = new File(srcDir, "com/codename1/impl/android");
    File playServicesClassFile = getPlayServicesJavaSourceFile(srcDir, playServicesVersion);
    String playServicesClassName = playServicesClassFile.getName().substring(0, playServicesClassFile.getName().indexOf("."));
    // Delete all of the PlayServices_X_X_X files that we aren't going to use
    for (File f : androidImpl.listFiles()) {
        if (f.getName().startsWith("PlayServices_") && f.getName().endsWith(".java")) {
            if (!f.equals(playServicesClassFile)) {
                f.delete();
            }
        }
    }
    if (!playServicesClassFile.getName().equals("PlayServices.java")) {
        // We will change the instance of the PlayServices class used to the most recent one we selected
        // The AndroidImplementation class has call to PlayServices.setInstance(...) in its init
        // method which we will update here.
        File androidImplementation = new File(androidImpl, "AndroidImplementation.java");
        try {
            if (playServicesLocation) {
                replaceInFile(androidImplementation, "new PlayServices()", "new com.codename1.impl.android." + playServicesClassName + "()");
                replaceInFile(androidImplementation, "new com.codename1.impl.android.PlayServices()", "new com.codename1.impl.android." + playServicesClassName + "()");
            } else {
                replaceInFile(androidImplementation, "PlayServices.setInstance(", "//PlayServices.setInstance(");
            }
        } catch (IOException ex) {
            throw new BuildException("Failed to inject settings into PlayServices class.", ex);
        }
    }
    if (targetSDKVersionInt >= 29) {
        File androidLocationPlayServicesManager = new File(srcDir, "com/codename1/location/AndroidLocationPlayServicesManager.java");
        if (androidLocationPlayServicesManager.exists()) {
            try {
                replaceInFile(androidLocationPlayServicesManager, "//29+", "");
            } catch (IOException ex) {
                throw new BuildException("Failed to activate lines in " + androidLocationPlayServicesManager + " for API 29+");
            }
        }
    }
    xQueries = "";
    if (targetSDKVersionInt >= 30) {
        xQueries = "<queries>\n" + request.getArg("android.manifest.queries", "") + "</queries>\n";
    }
    // Delete the Facebook implemetation if this app does not use FB.
    if (!facebookSupported) {
        File fb = new File(srcDir, "com/codename1/social/FacebookImpl.java");
        fb.delete();
    } else {
        // special case for pubnub that includes a cn1lib for json that masks the one defined in Android
        File json = new File(dummyClassesDir, "org/json");
        if (json.exists()) {
            delTree(json);
        }
    }
    if (!playServicesLocation) {
        File fb = new File(srcDir, "com/codename1/location/AndroidLocationPlayServiceManager.java");
        fb.delete();
        fb = new File(srcDir, "com/codename1/location/BackgroundLocationHandler.java");
        fb.delete();
        fb = new File(srcDir, "com/codename1/location/BackgroundLocationBroadcastReceiver.java");
        fb.delete();
        fb = new File(srcDir, "com/codename1/location/GeofenceHandler.java");
        fb.delete();
        fb = new File(srcDir, "com/codename1/location/CodenameOneBackgroundLocationActivity.java");
        fb.delete();
        for (File f : androidImpl.listFiles()) {
            if (f.getName().startsWith("PlayServices_") && f.getName().endsWith(".java")) {
                f.delete();
            } else if (f.getName().equals("PlayServices.java")) {
                f.delete();
            }
        }
    }
    if (!shouldIncludeGoogleImpl) {
        File fb = new File(srcDir, "com/codename1/social/GoogleImpl.java");
        fb.delete();
    }
    final String moPubAdUnitId = request.getArg("android.mopubId", null);
    if (moPubAdUnitId != null && moPubAdUnitId.length() > 0) {
        integrateMoPub = true;
    }
    if (request.getArg("android.textureView", "false").equals("true")) {
        File impl = new File(srcDir, "com" + File.separator + "codename1" + File.separator + "impl" + File.separator + "android" + File.separator + "AndroidImplementation.java");
        try {
            replaceInFile(impl, "public static boolean textureView = false;", "public static boolean textureView = true;");
        } catch (IOException ex) {
            throw new BuildException("Failed to process android.textureView build hint", ex);
        }
    }
    if (request.getArg("android.hideStatusBar", "false").equals("true")) {
        File impl = new File(srcDir, "com" + File.separator + "codename1" + File.separator + "impl" + File.separator + "android" + File.separator + "AndroidImplementation.java");
        try {
            replaceInFile(impl, "statusBarHidden;", "statusBarHidden = true;");
        } catch (IOException ex) {
            throw new BuildException("Failed to process android.hideStatusBar build hint", ex);
        }
    }
    if (request.getArg("android.asyncPaint", "true").equals("true")) {
        File impl = new File(srcDir, "com" + File.separator + "codename1" + File.separator + "impl" + File.separator + "android" + File.separator + "AndroidImplementation.java");
        try {
            replaceInFile(impl, "public static boolean asyncView = false;", "public static boolean asyncView = true;");
        } catch (IOException ex) {
            throw new BuildException("Failed to process android.asyncPaint build hint", ex);
        }
    }
    if (request.getArg("android.keyboardOpen", "true").equals("true")) {
        File impl = new File(srcDir, "com" + File.separator + "codename1" + File.separator + "impl" + File.separator + "android" + File.separator + "AndroidImplementation.java");
        try {
            replaceInFile(impl, "private boolean asyncEditMode = false;", "private boolean asyncEditMode = true;");
        } catch (IOException ex) {
            throw new BuildException("Failed to process android.keyboardOpen build hint", ex);
        }
    }
    // String sdkVersion = request.getArg("android.targetSDKVersion", defaultVersion);
    if (targetNumber != null) {
        if (Integer.parseInt(targetNumber) >= 17) {
            try {
                File androidBrowserComponentCallback = new File(srcDir, "com" + File.separator + "codename1" + File.separator + "impl" + File.separator + "android" + File.separator + "AndroidBrowserComponentCallback.java");
                replaceInFile(androidBrowserComponentCallback, "//import android.webkit.JavascriptInterface;", "import android.webkit.JavascriptInterface;");
                replaceInFile(androidBrowserComponentCallback, "//@JavascriptInterface", "@JavascriptInterface");
            } catch (Exception e) {
            // swallow this and continue.
            }
        }
    }
    File drawableDir = new File(resDir, "drawable");
    drawableDir.mkdirs();
    File drawableHdpiDir = new File(resDir, "drawable-hdpi");
    drawableHdpiDir.mkdirs();
    File drawableLdpiDir = new File(resDir, "drawable-ldpi");
    drawableLdpiDir.mkdirs();
    File drawableMdpiDir = new File(resDir, "drawable-mdpi");
    drawableMdpiDir.mkdirs();
    File drawableXhdpiDir = new File(resDir, "drawable-xhdpi");
    drawableXhdpiDir.mkdirs();
    File drawableXXhdpiDir = new File(resDir, "drawable-xxhdpi");
    drawableXXhdpiDir.mkdirs();
    File drawableXXXhdpiDir = new File(resDir, "drawable-xxxhdpi");
    drawableXXXhdpiDir.mkdirs();
    try {
        BufferedImage iconImage = ImageIO.read(new ByteArrayInputStream(request.getIcon()));
        createIconFile(new File(drawableDir, "icon.png"), iconImage, 128, 128);
        createIconFile(new File(drawableHdpiDir, "icon.png"), iconImage, 72, 72);
        createIconFile(new File(drawableLdpiDir, "icon.png"), iconImage, 36, 36);
        createIconFile(new File(drawableMdpiDir, "icon.png"), iconImage, 48, 48);
        createIconFile(new File(drawableXhdpiDir, "icon.png"), iconImage, 96, 96);
        createIconFile(new File(drawableXXhdpiDir, "icon.png"), iconImage, 144, 144);
        createIconFile(new File(drawableXXXhdpiDir, "icon.png"), iconImage, 192, 192);
        File notifFile = new File(assetsDir, "ic_stat_notify.png");
        if (notifFile.exists()) {
            BufferedImage bi = ImageIO.read(notifFile);
            createIconFile(new File(drawableDir, "ic_stat_notify.png"), bi, 24, 24);
            notifFile.delete();
        } else {
            // with white
            if (Integer.parseInt(targetNumber) >= 21) {
                // notification small icon
                Image img = makeColorTransparent(iconImage, new Color(iconImage.getRGB(2, 2)));
                BufferedImage notifSmallIcon = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
                Graphics2D bGr = notifSmallIcon.createGraphics();
                bGr.drawImage(img, 0, 0, null);
                bGr.dispose();
                iconImage = notifSmallIcon;
            }
            createIconFile(new File(drawableDir, "ic_stat_notify.png"), iconImage, 24, 24);
        }
    } catch (IOException ex) {
        throw new BuildException("Failed to generate icon files", ex);
    }
    if (!purchasePermissions) {
        File billingSupport = new File(srcDir, path("com", "codename1", "impl", "android", "BillingSupport.java"));
        if (billingSupport.exists()) {
            billingSupport.delete();
        }
    }
    try {
        zipDir(new File(libsDir, "userClasses.jar").getAbsolutePath(), dummyClassesDir.getAbsolutePath());
    } catch (Exception ex) {
        throw new BuildException("Failed to create userClasses.jar", ex);
    }
    File stringsFile = new File(valsDir, "strings.xml");
    String stringsFileContent = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<resources>\n" + "    <string name=\"app_name\">" + xmlize(request.getDisplayName()).replace("'", "\\'") + "</string>\n" + additionalKeyVals + request.getArg("android.stringsXml", "") + "</resources>";
    try {
        OutputStream stringsSourceStream = new FileOutputStream(stringsFile);
        stringsSourceStream.write(stringsFileContent.getBytes());
        stringsSourceStream.close();
        String locales = request.getArg("android.locales", null);
        if (locales != null && locales.length() > 0) {
            for (String loc : locales.split(";")) {
                File currentValuesDir = new File(valsDir.getParent(), "values-" + loc);
                currentValuesDir.mkdirs();
                File currentStringsFile = new File(currentValuesDir, "strings.xml");
                stringsSourceStream = new FileOutputStream(currentStringsFile);
                stringsSourceStream.write(stringsFileContent.getBytes());
                stringsSourceStream.close();
            }
        }
    } catch (IOException ex) {
        error("Failed to generate strings file", ex);
        throw new BuildException("Failed to generate strings file " + stringsFile, ex);
    }
    // declare the android native theme.
    File stylesFile = new File(valsDir, "styles.xml");
    File colors = new File(valsDir, "colors.xml");
    String colorsStr = "";
    if (colors.exists()) {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        try {
            // Using factory get an instance of document builder
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document dom = db.parse(colors);
            NodeList nl = dom.getElementsByTagName("color");
            for (int i = 0; i < nl.getLength(); i++) {
                Node color = nl.item(i);
                NamedNodeMap attr = color.getAttributes();
                Node key = attr.getNamedItem("name");
                String k = key.getNodeValue();
                colorsStr += "<item name=\"android:" + k + "\">@color/" + k + "</item>\n";
            }
        } catch (Exception e) {
            error("Failed to create DocumentBuilder", e);
        }
    }
    String themeName = "android:Theme.Black";
    String itemName = androidAppBundle ? "cn1Style" : "attr/cn1Style";
    String stylesFileContent = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<resources>\n" + "    <style name=\"CustomTheme\" parent=\"" + themeName + "\">\n" + "        <item name=\"" + itemName + "\">@style/CN1.EditText.Style</item>\n" + "    </style>\n" + "    <attr name=\"cn1Style\" format=\"reference\" />\n" + "    <style name=\"CN1.EditText.Style\" parent=\"@android:style/Widget.EditText\">\n" + "        <item name=\"android:textCursorDrawable\">@null</item>\n" + "    </style>\n" + request.getArg("android.style", "") + "</resources>";
    try {
        OutputStream stylesSourceStream = new FileOutputStream(stylesFile);
        stylesSourceStream.write(stylesFileContent.getBytes());
        stylesSourceStream.close();
        String theme = request.getArg("android.theme", "Light");
        if (theme.length() > 0 && theme.equalsIgnoreCase("Dark")) {
            theme = "";
        } else {
            theme = "." + theme;
        }
        File styles11File = new File(vals11Dir, "styles.xml");
        String styles11FileContent = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<resources>\n" + "    <style name=\"CustomTheme\" parent=\"@android:style/Theme.Holo" + theme + "\">\n" + "        <item name=\"" + itemName + "\">@style/CN1.EditText.Style</item>\n" + "        <item name=\"android:windowActionBar\">false</item>\n" + "        <item name=\"android:windowTitleSize\">0dp</item>\n" + "    </style>\n" + "    <style name=\"CN1.EditText.Style\" parent=\"@android:style/Widget.EditText\">\n" + "        <item name=\"android:textCursorDrawable\">@null</item>\n" + "    </style>\n" + "</resources>\n";
        OutputStream styles11SourceStream = new FileOutputStream(styles11File);
        styles11SourceStream.write(styles11FileContent.getBytes());
        styles11SourceStream.close();
        File styles21File = new File(vals21Dir, "styles.xml");
        String styles21FileContent = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<resources>\n" + "    <style name=\"CustomTheme\" parent=\"@android:style/Theme.Material" + theme + "\">\n" + "        <item name=\"" + itemName + "\">@style/CN1.EditText.Style</item>\n" + "        <item name=\"android:windowActionBar\">false</item>\n" + "        <item name=\"android:windowTitleSize\">0dp</item>\n" + colorsStr + "   </style>\n" + "    <style name=\"CN1.EditText.Style\" parent=\"@android:style/Widget.EditText\">\n" + "        <item name=\"android:textCursorDrawable\">@null</item>\n" + "    </style>\n" + "</resources>\n";
        OutputStream styles21SourceStream = new FileOutputStream(styles21File);
        styles21SourceStream.write(styles21FileContent.getBytes());
        styles21SourceStream.close();
    } catch (IOException ex) {
        error("Failed to generate style files", ex);
        throw new BuildException("Failed to generate styles files", ex);
    }
    try {
        File layoutFile = new File(layoutDir, "main.xml");
        String layoutFileContent = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + "    android:layout_width=\"fill_parent\"\n" + "    android:layout_height=\"fill_parent\"\n" + request.getArg("android.xlayout_attr", "") + "    android:background=\"#ff000000\" >\n" + mopubBannerXML + "</RelativeLayout>\n";
        OutputStream layoutSourceStream = new FileOutputStream(layoutFile);
        layoutSourceStream.write(layoutFileContent.getBytes());
        layoutSourceStream.close();
        String customLayout = request.getArg("android.cusom_layout1", null);
        int counter = 1;
        while (customLayout != null) {
            File customFile = new File(layoutDir, "cusom_layout" + counter + ".xml");
            layoutSourceStream = new FileOutputStream(customFile);
            layoutSourceStream.write(customLayout.getBytes());
            layoutSourceStream.close();
            counter++;
            customLayout = request.getArg("android.cusom_layout" + counter, null);
        }
    } catch (IOException ex) {
        throw new BuildException("Failed to generate layout XML file", ex);
    }
    String storeIds = request.getArg("android.store_ids", null);
    if (storeIds != null) {
        String[] sp = storeIds.split(";");
        storeIds = "";
        for (String s : sp) {
            storeIds += "        Display.getInstance().setProperty(\"" + s + "\", \"\" + " + s + ");\n";
        }
    } else {
        storeIds = "";
    }
    String gcmSenderId = request.getArg("gcm.sender_id", null);
    if (gcmSenderId != null) {
        gcmSenderId = "        Display.getInstance().setProperty(\"gcm.sender_id\", \"" + gcmSenderId + "\");\n";
    } else {
        if (googleServicesJson != null && googleServicesJson.exists()) {
            try {
                JSONParser parser = new JSONParser();
                Map<String, Object> parsedJson = parser.parseJSON(new FileReader(googleServicesJson));
                Map projectInfo = (Map) parsedJson.get("project_info");
                gcmSenderId = (String) projectInfo.get("project_number");
                if (gcmSenderId != null) {
                    gcmSenderId = "        Display.getInstance().setProperty(\"gcm.sender_id\", \"" + gcmSenderId + "\");\n";
                } else {
                    gcmSenderId = "";
                }
            } catch (IOException ex) {
                throw new BuildException("Failed to parse the google services JSON file " + googleServicesJson);
            }
        } else {
            gcmSenderId = "";
        }
    }
    File manifestFile = new File(projectDir + "/src/main", "AndroidManifest.xml");
    float version = 1.0f;
    int intVersion = 1;
    try {
        version = Float.parseFloat(request.getVersion());
        String vcOverride = request.getArg("android.versionCode", null);
        if (vcOverride != null && vcOverride.length() > 0) {
            intVersion = Integer.parseInt(vcOverride);
        } else {
            intVersion = Math.round(100 * version);
        }
    } catch (Throwable thrown) {
    }
    String locationServices = "<activity android:name=\"com.codename1.location.CodenameOneBackgroundLocationActivity\" android:theme=\"@android:style/Theme.NoDisplay\" />\n" + "<service android:name=\"com.codename1.location.BackgroundLocationHandler\" android:exported=\"false\" />\n" + "<service android:name=\"com.codename1.location.GeofenceHandler\" android:exported=\"false\" />\n";
    String mediaService = "<service android:name=\"com.codename1.media.AudioService\" android:exported=\"false\" />";
    String remoteControlService = "<service android:name=\"com.codename1.media.BackgroundAudioService\">\n" + "            <intent-filter>\n" + "                <action android:name=\"android.intent.action.MEDIA_BUTTON\" />\n" + "                <action android:name=\"android.media.AUDIO_BECOMING_NOISY\" />\n" + "                <action android:name=\"android.media.browse.MediaBrowserService\" />\n" + "            </intent-filter>\n" + "        </service>";
    String mediabuttonReceiver = "<receiver android:name=\"" + xclass("android.support.v4.media.session.MediaButtonReceiver") + "\">\n" + "            <intent-filter>\n" + "                <action android:name=\"android.intent.action.MEDIA_BUTTON\" />\n" + "                <action android:name=\"android.media.AUDIO_BECOMING_NOISY\" />\n" + "            </intent-filter>\n" + "        </receiver>";
    if (!addRemoteControlService) {
        remoteControlService = "";
        mediabuttonReceiver = "";
    }
    String alarmRecevier = "<receiver android:name=\"com.codename1.impl.android.LocalNotificationPublisher\" ></receiver>\n";
    String backgroundLocationReceiver = "<receiver android:name=\"com.codename1.location.BackgroundLocationBroadcastReceiver\" ></receiver>\n";
    if (!playServicesLocation) {
        backgroundLocationReceiver = "";
    }
    String backgroundFetchService = "<service android:name=\"com.codename1.impl.android.BackgroundFetchHandler\" android:exported=\"false\" />\n" + "<activity android:name=\"com.codename1.impl.android.CodenameOneBackgroundFetchActivity\" android:theme=\"@android:style/Theme.NoDisplay\" />\n";
    if (foregroundServicePermission) {
        permissions += permissionAdd(request, "\"android.permission.FOREGROUND_SERVICE\"", "    <uses-permission android:name=\"android.permission.FOREGROUND_SERVICE\" />\n");
    }
    if (capturePermission) {
        String andc = request.getArg("android.captureRecord", "enabled");
        if (request.getArg("and.captureRecord", andc).equals("enabled")) {
            permissions += permissionAdd(request, "\"android.hardware.camera\"", "<uses-feature android:name=\"android.hardware.camera\" android:required=\"false\" />\n") + permissionAdd(request, "RECORD_AUDIO", "    <uses-permission android:name=\"android.permission.RECORD_AUDIO\" android:required=\"false\" />\n");
        } else {
            permissions += permissionAdd(request, "\"android.hardware.camera\"", "<uses-feature android:name=\"android.hardware.camera\" android:required=\"false\" />\n");
        }
    }
    if (vibratePermission) {
        permissions += permissionAdd(request, "VIBRATE", "    <uses-permission android:name=\"android.permission.VIBRATE\" android:required=\"false\" />\n");
    }
    if (smsPermission) {
        permissions += permissionAdd(request, "SEND_SMS", "<uses-permission android:name=\"android.permission.SEND_SMS\" android:required=\"false\" />\n");
    }
    if (gpsPermission) {
        permissions += "    <uses-feature android:name=\"android.hardware.location\" android:required=\"false\" />\n" + "    <uses-feature android:name=\"android.hardware.location.gps\" android:required=\"false\" />\n" + permissionAdd(request, "ACCESS_FINE_LOCATION", "    <uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" android:required=\"false\" />\n") + permissionAdd(request, "ACCESS_COARSE_LOCATION", "    <uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"  android:required=\"false\" />\n");
        if (request.getArg("android.mockLocation", "true").equals("true")) {
            permissions += permissionAdd(request, "ACCESS_MOCK_LOCATION", "    <uses-permission android:name=\"android.permission.ACCESS_MOCK_LOCATION\"  android:required=\"false\" />\n");
        }
    }
    if (pushPermission && !useFCM) {
        permissions += "<permission android:name=\"" + request.getPackageName() + ".permission.C2D_MESSAGE\" android:protectionLevel=\"signature\" />\n" + "    <uses-permission android:name=\"" + request.getPackageName() + ".permission.C2D_MESSAGE\" />\n" + "    <uses-permission android:name=\"com.google.android.c2dm.permission.RECEIVE\" />\n";
    // + permissionAdd(request, "RECEIVE_BOOT_COMPLETED",
    // "    <uses-permission android:name=\"android.permission.RECEIVE_BOOT_COMPLETED\" android:required=\"false\" />\n");
    }
    if (contactsReadPermission) {
        permissions += permissionAdd(request, "READ_CONTACTS", "    <uses-permission android:name=\"android.permission.READ_CONTACTS\" android:required=\"false\" />\n");
    }
    if (contactsWritePermission) {
        permissions += permissionAdd(request, "WRITE_CONTACTS", "    <uses-permission android:name=\"android.permission.WRITE_CONTACTS\" android:required=\"false\" />\n");
    }
    if (accessWifiStatePermissions) {
        permissions += permissionAdd(request, "ACCESS_WIFI_STATE", "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" android:required=\"false\" />\n");
    }
    if (browserBookmarksPermissions) {
        permissions += "<uses-permission android:name=\"com.android.browser.permission.WRITE_HISTORY_BOOKMARKS\" android:required=\"false\"/>\n" + "<uses-permission android:name=\"com.android.browser.permission.READ_HISTORY_BOOKMARKS\" android:required=\"false\"/>\n";
    }
    if (launcherPermissions) {
        permissions += "<uses-permission android:name=\"com.android.launcher.permission.INSTALL_SHORTCUT\"/>\n" + "<uses-permission android:name=\"com.android.launcher.permission.UNINSTALL_SHORTCUT\"/>\n" + "<uses-permission android:name=\"com.android.launcher.permission.READ_SETTINGS\"/>\n" + "<!--device specific permissions -->\n" + "<uses-permission android:name=\"com.htc.launcher.permission.READ_SETTINGS\"/>\n" + "<uses-permission android:name=\"com.motorola.launcher.permission.READ_SETTINGS\"/>\n" + "<uses-permission android:name=\"com.motorola.dlauncher.permission.READ_SETTINGS\"/>\n" + "<uses-permission android:name=\"com.fede.launcher.permission.READ_SETTINGS\"/>\n" + "<uses-permission android:name=\"com.lge.launcher.permission.READ_SETTINGS\"/>\n" + "<uses-permission android:name=\"org.adw.launcher.permission.READ_SETTINGS\"/>\n" + "<uses-permission android:name=\"com.motorola.launcher.permission.INSTALL_SHORTCUT\"/>\n" + "<uses-permission android:name=\"com.motorola.dlauncher.permission.INSTALL_SHORTCUT\"/>\n" + "<uses-permission android:name=\"com.lge.launcher.permission.INSTALL_SHORTCUT\"/>\n";
    }
    if (recordAudio) {
        permissions += permissionAdd(request, "RECORD_AUDIO", "<uses-permission android:name=\"android.permission.RECORD_AUDIO\" android:required=\"false\" />\n");
    }
    if (wakeLock) {
        permissions += permissionAdd(request, "WAKE_LOCK", "<uses-permission android:name=\"android.permission.WAKE_LOCK\" android:required=\"false\" />\n");
    }
    if (phonePermission) {
        permissions += permissionAdd(request, "READ_PHONE_STATE", "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\" android:required=\"false\" />\n");
    }
    if (accessNetworkStatePermission) {
        permissions += permissionAdd(request, "ACCESS_NETWORK_STATE", "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" android:required=\"false\" />\n");
    }
    if (recieveBootCompletedPermission) {
        permissions += permissionAdd(request, "RECEIVE_BOOT_COMPLETED", "<uses-permission android:name=\"android.permission.RECEIVE_BOOT_COMPLETED\" android:required=\"false\" />\n");
    }
    if (getAccountsPermission) {
        permissions += permissionAdd(request, "GET_ACCOUNTS", "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\" android:required=\"false\" />\n");
    }
    if (credentialsPermission) {
        permissions += permissionAdd(request, "USE_CREDENTIALS", "<uses-permission android:name=\"android.permission.USE_CREDENTIALS\" />\n");
    }
    if (backgroundLocationPermission && !xPermissions.contains("android.permission.ACCESS_BACKGROUND_LOCATION")) {
        permissions += "<uses-permission android:name=\"android.permission.ACCESS_BACKGROUND_LOCATION\"  android:required=\"false\" />\n";
    }
    String billingServiceData = "";
    String activityBillingSource = "";
    String consumable = "";
    if (purchasePermissions) {
        String k = request.getArg("android.licenseKey", null);
        // if the android.licenseKey is not defined abort the build
        if (k == null) {
            throw new BuildException("android.licenseKey must be defined in the build hints, grab the key from the \"Monetization setup\" section in the android dev portal" + ", then paste the Base64-encoded RSA public key into the android.licenseKey build hint.\n\n");
        }
        String cons = request.getArg("android.nonconsumable", null);
        if (cons != null) {
            cons = cons.trim();
            if (cons.contains(",")) {
                StringTokenizer token = new StringTokenizer(cons, ",");
                if (token.countTokens() > 0) {
                    try {
                        while (token.hasMoreElements()) {
                            String t = (String) token.nextToken();
                            t = t.trim();
                            consumable += "\"" + t + "\",";
                        }
                        consumable = consumable.substring(0, consumable.length() - 1);
                    } catch (Exception e) {
                    // the pattern is not valid
                    }
                }
            } else {
                consumable = "\"" + cons + "\"";
            }
        }
        permissions += "    <uses-permission android:name=\"com.android.vending.BILLING\" android:required=\"false\" />\n";
        activityBillingSource = "    protected boolean isBillingEnabled() {\n" + "        return true;\n" + "    }\n\n" + "    protected com.codename1.impl.android.IBillingSupport createBillingSupport() {\n" + "        return new com.codename1.impl.android.BillingSupport(this);\n" + "    }\n\n";
    }
    String sharedUserId = request.getArg("android.sharedUserId", "");
    if (sharedUserId.length() > 0) {
        sharedUserId = "      android:sharedUserId=\"" + sharedUserId + "\"\n";
    }
    String sharedUserLabel = request.getArg("android.sharedUserLabel", "");
    if (sharedUserLabel.length() > 0) {
        sharedUserLabel = "      android:sharedUserLabel=\"" + sharedUserLabel + "\"\n";
    }
    String basePermissions = "    <uses-feature android:name=\"android.hardware.telephony\" android:required=\"" + telephonyRequired + "\" />\n" + "    <uses-permission android:name=\"android.permission.INTERNET\" android:required=\"false\" />\n";
    if (request.getArg("android.removeBasePermissions", "false").equals("true")) {
        basePermissions = "";
    }
    String externalStoragePermission = "    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" android:required=\"false\" />\n";
    if (request.getArg("android.blockExternalStoragePermission", "false").equals("true")) {
        externalStoragePermission = "";
    }
    String xmlizedDisplayName = xmlize(request.getDisplayName());
    String applicationAttr = request.getArg("android.xapplication_attr", "");
    String allowBackup = " android:allowBackup=\"" + request.getArg("android.allowBackup", "true") + "\" ";
    if (applicationAttr.contains("allowBackup")) {
        allowBackup = "";
    }
    String applicationNode = "  <application ";
    if (!applicationAttr.contains("android:label")) {
        applicationNode += " android:label=\"" + xmlizedDisplayName + "\" ";
    }
    if (!applicationAttr.contains("android:icon")) {
        applicationNode += " android:icon=\"@drawable/icon\" ";
    }
    if (request.getArg("android.multidex", "false").equals("true") && Integer.parseInt(minSDK) < 21) {
        debug("Setting Application node to MultiDexApplication because minSDK=" + minSDK + " < 21");
        applicationNode += " android:name=\"" + xclass("android.support.multidex.MultiDexApplication") + "\" ";
    }
    applicationNode += applicationAttr;
    applicationNode += allowBackup;
    applicationNode += ">\n";
    // Note: It is OK to reference android.support.FILE_PROVIDER_PATHS in android X still
    // https://stackoverflow.com/a/57584508/2935174
    String providerTag = "<provider\n" + "          android:name=\"" + xclass("android.support.v4.content.FileProvider") + "\"\n" + "          android:authorities=\"${applicationId}.provider\"\n" + "          android:exported=\"false\"\n" + "          android:grantUriPermissions=\"true\">\n" + "          <meta-data\n" + "              android:name=\"android.support.FILE_PROVIDER_PATHS\"\n" + "              android:resource=\"@xml/file_paths\">\n" + "          </meta-data>\n" + "      </provider>";
    if (!providerTag.isEmpty()) {
        File filePathsFile = new File(xmlDir, "file_paths.xml");
        String filePathsContent = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<paths xmlns:android=\"http://schemas.android.com/apk/res/android\">\n" + "    <cache-path name=\"intent_files\" path=\"intent_files/\" />\n" + request.getArg("android.file_paths", "    <files-path name=\"app_files\" path=\".\" />") + "</paths>";
        try {
            OutputStream filePathsStream = new FileOutputStream(filePathsFile);
            filePathsStream.write(filePathsContent.getBytes());
            filePathsStream.close();
        } catch (IOException ex) {
            throw new BuildException("Failed to write file path providers file", ex);
        }
    }
    String pushManifestEntries = "        <service android:name=\"PushNotificationService\">\n" + "            <intent-filter>\n" + "                <action android:name=\"" + request.getPackageName() + ".PushNotificationService\" />\n" + "            </intent-filter>\n" + "        </service>\n" + "        <receiver android:name=\".PushReceiver\" android:permission=\"com.google.android.c2dm.permission.SEND\">\n" + "            <intent-filter>\n" + "                <action android:name=\"com.google.android.c2dm.intent.RECEIVE\" />\n" + "                <category android:name=\"" + request.getPackageName() + "\" />\n" + "            </intent-filter>\n" + "            <intent-filter>\n" + "                <action android:name=\"com.google.android.c2dm.intent.REGISTRATION\" />\n" + "                <category android:name=\"" + request.getPackageName() + "\" />\n" + "            </intent-filter>\n" + "        </receiver>\n";
    if (!pushPermission) {
        pushManifestEntries = "";
    } else if (useFCM) {
        pushManifestEntries = "<service\n" + "          android:name=\"com.codename1.impl.android.CN1FirebaseMessagingService\">\n" + "          <intent-filter>\n" + "              <action android:name=\"com.google.firebase.MESSAGING_EVENT\" />\n" + "          </intent-filter>\n" + "      </service>\n";
    }
    String launchMode = request.getArg("android.activity.launchMode", "singleTop");
    String manifestSource = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + "      package=\"" + request.getPackageName() + "\"\n" + "      android:versionCode=\"" + intVersion + "\"\n" + "      android:versionName=\"" + request.getVersion() + "\"\n" + "      xmlns:tools=\"http://schemas.android.com/tools\"\n" + sharedUserLabel + sharedUserId + "      android:minSdkVersion=\"" + minSDK + "\"\n" + "      android:installLocation=\"" + request.getArg("android.installLocation", "auto") + "\">\n" + "    <uses-sdk android:minSdkVersion=\"" + minSDK + "\"" + targetSDKVersion + request.getArg("android.xmanifest", "") + " />\n" + "    <supports-screens android:smallScreens=\"" + request.getArg("android.smallScreens", "true") + "\"\n" + "          android:normalScreens=\"" + request.getArg("android.normalScreens", "true") + "\"\n" + "          android:largeScreens=\"" + request.getArg("android.largeScreens", "true") + "\"\n" + "          android:xlargeScreens=\"" + request.getArg("android.xlargeScreens", "true") + "\"\n" + request.getArg("android.supportScreens", "") + "          android:anyDensity=\"" + request.getArg("android.anyDensity", "true") + "\" />\n" + applicationNode + providerTag + usesLibrary + googlePlayAdsMetaData + "        <activity android:name=\"" + request.getMainClass() + "Stub\"\n" + request.getArg("android.xactivity", "") + "                  android:theme=\"@style/CustomTheme\"\n" + "                  android:configChanges=\"orientation|keyboardHidden|screenSize|smallestScreenSize|screenLayout\"\n" + "                  android:launchMode=\"" + launchMode + "\"\n" + "                  android:label=\"" + xmlizedDisplayName + "\">\n" + "            <intent-filter>\n" + "                <action android:name=\"android.intent.action.MAIN\" />\n" + "                <category android:name=\"android.intent.category.LAUNCHER\" />\n" + "            </intent-filter>\n" + request.getArg("android.xintent_filter", "") + "        </activity>\n" + facebookActivityMetaData + facebookActivity + googlePlayAdsActivity + pushManifestEntries + billingServiceData + "  " + request.getArg("android.xapplication", "") + mopubActivities + alarmRecevier + backgroundLocationReceiver + mediabuttonReceiver + backgroundFetchService + locationServices + mediaService + remoteControlService + "    </application>\n" + "    <uses-feature android:name=\"android.hardware.touchscreen\" android:required=\"false\" />\n" + basePermissions + externalStoragePermission + permissions + "  " + xPermissions + "  " + xQueries + "</manifest>\n";
    try {
        OutputStream manifestSourceStream = new FileOutputStream(manifestFile);
        manifestSourceStream.write(manifestSource.getBytes());
        manifestSourceStream.close();
    } catch (IOException ex) {
        throw new BuildException("Failed to write manifest file", ex);
    }
    debug("Generated manifest file: " + manifestSource);
    String oncreate = request.getArg("android.onCreate", "");
    String initStackSize = "";
    String stackSize = request.getArg("android.stack_size", null);
    if (stackSize != null) {
        initStackSize = "        com.codename1.impl.CodenameOneThread.STACK_FRAME_SIZE = " + stackSize + ";\n";
    }
    String licenseKey = request.getArg("android.licenseKey", null);
    String androidLicenseKey = licenseKey;
    if (androidLicenseKey != null) {
        androidLicenseKey = "Display.getInstance().setProperty(\"android.licenseKey\", \"" + androidLicenseKey + "\");\n";
    } else {
        androidLicenseKey = "";
    }
    String useBackgroundPermissionSnippet = "";
    if (backgroundLocationPermission) {
        useBackgroundPermissionSnippet = "Display.getInstance().setProperty(\"android.requiresBackgroundLocationPermissionForAPI29\", \"true\");\n";
    }
    String streamMode = request.getArg("android.streamMode", null);
    if (streamMode != null) {
        if (streamMode.equals("music")) {
            streamMode = "        setVolumeControlStream(android.media.AudioManager.STREAM_MUSIC);\n";
        } else {
            streamMode = "";
        }
    } else {
        streamMode = "";
    }
    String localNotificationCode = "";
    localNotificationCode = "" + "        if(i instanceof com.codename1.notifications.LocalNotificationCallback){\n" + "            Intent intent = getIntent();\n" + "            if(intent != null && intent.getExtras() != null && intent.getExtras().containsKey(\"LocalNotificationID\")){\n" + "                String id = intent.getExtras().getString(\"LocalNotificationID\");\n" + "                intent.removeExtra(\"LocalNotificationID\");\n" + "                ((com.codename1.notifications.LocalNotificationCallback)i).localNotificationReceived(id);\n" + "            }\n" + "        }\n";
    String reinitCode0 = "Display.init(this);\n";
    reinitCode0 = "AndroidImplementation.startContext(this);\n";
    String reinitCode = "Display.init(this);\n";
    // We need to explicitly call initImpl() to setup the activity in case the
    // last used context is a service, since Display.init() won't actually do
    // a reinitialize in this case.
    // We don't want to actually call deinitialize here because this is too heavy-handed,
    // (if the service is running, this will create a new implementation and edt thread
    // which will cause problems for existing background procresses.
    // Doing it this way ensures that the EDT and implemenation objects will remain unchanged,
    // but other things will be set up properly.
    reinitCode = "AndroidImplementation.startContext(this);\n";
    String waitingForPermissionsRequestOnStop = "        if (isWaitingForPermissionResult()) {\n" + "            return;\n" + "        }\n";
    String onStopCode = "protected void onStop() {\n" + "        super.onStop();\n" + waitingForPermissionsRequestOnStop + "        if(isWaitingForResult()){\n" + "             return;\n" + "        }\n" + "        synchronized(LOCK) {\n" + "             currentForm = null;\n" + "        }\n" + "        Display.getInstance().callSerially(new Runnable() { public void run() {i.stop();} });\n" + "        running = false;\n" + "    }\n\n";
    // Added a bit of blocking to onStop() to prevent onDestroy() from being
    // run before stop() is completed.  This probably only shows up if the
    // device is very low on RAM or is set to not keep activity due
    // to developer options... but it is still better to finish onStop()
    // before onDestroy() is run.
    onStopCode = "protected void onStop() {\n";
    onStopCode += "        com.codename1.impl.android.AndroidImplementation.writeServiceProperties(this);\n";
    onStopCode += "        super.onStop();\n" + "        if(isWaitingForResult()){\n" + "             return;\n" + "        }\n" + "        synchronized(LOCK) {\n" + "             currentForm = null;\n" + "        }\n" + "        final boolean[] complete = new boolean[1];\n" + "\n" + "        Display.getInstance().callSerially(new Runnable() {\n" + "            public void run() {\n" + "                i.stop();\n" + "                synchronized(complete) {\n" + "                    try {\n" + "                        complete[0] = true;\n" + "                        complete.notify();\n" + "                    } catch (Exception ex) {\n" + "                    }\n" + "                }\n" + "            }\n" + "        });\n" + "        while (!complete[0]) {\n" + "            synchronized(complete) {\n" + "                try {\n" + "                    complete.wait(500);\n" + "                } catch (Exception ex){}\n" + "            }\n" + "        }\n" + "        running = false;\n" + "    }\n\n";
    String onDestroyCode = "    protected void onDestroy() {\n" + createOnDestroyCode(request) + "        super.onDestroy();\n" + "        Display.getInstance().callSerially(new Runnable() { public void run() {i.destroy(); Display.deinitialize();} });\n" + "        running = false;\n" + "    }\n";
    onDestroyCode = "protected void onDestroy() {\n" + createOnDestroyCode(request) + "        super.onDestroy();\n" + "\n" + "        Display.getInstance().callSerially(new Runnable() { public void run() {i.destroy();} });\n" + "        AndroidImplementation.stopContext(this);\n" + "        running = false;\n" + "    }";
    File stubFileSourceFile = new File(stubFileSourceDir, request.getMainClass() + "Stub.java");
    String consumableCode;
    consumableCode = "public boolean isConsumable(String sku) {\n" + "  boolean retVal = super.isConsumable(sku);\n" + "  java.util.List l = new java.util.ArrayList();\n" + "  java.util.Collections.addAll(l, consumable);\n" + "  return retVal || l.contains(sku);\n" + "}\n";
    String firstTimeStatic = "";
    firstTimeStatic = " static";
    String notificationChannelId = request.getArg("android.NotificationChannel.id", "cn1-channel");
    String notificationChannelName = request.getArg("android.NotificationChannel.name", "Notifications");
    String notificationChannelDescription = request.getArg("android.NotificationChannel.description", "Remote notifications");
    // https://developer.android.com/reference/android/app/NotificationManager#IMPORTANCE_LOW
    String notificationChannelImportance = request.getArg("android.NotificationChannel.importance", "2");
    String notificationChannelEnableLights = request.getArg("android.NotificationChannel.enableLights", "true");
    String notificationChannelLightColor = request.getArg("android.NotificationChannel.lightColor", "" + 0xffff0000);
    String notificationChannelEnableVibration = request.getArg("android.NotificationChannel.enableVibration", "false");
    String notificationChannelVibrationPattern = request.getArg("android.NotificationChannel.vibrationPattern", request.getArg("android.pushVibratePattern", null));
    if (notificationChannelVibrationPattern != null) {
        notificationChannelVibrationPattern = "\"" + notificationChannelVibrationPattern + "\"";
    }
    String pushInitDisplayProperties = "";
    if (buildToolsVersionInt >= 26 && Integer.parseInt(targetNumber) >= 26) {
        pushInitDisplayProperties = "        Display.getInstance().setProperty(\"android.NotificationChannel.id\", \"" + notificationChannelId + "\");\n" + "        Display.getInstance().setProperty(\"android.NotificationChannel.name\", \"" + notificationChannelName + "\");\n" + "        Display.getInstance().setProperty(\"android.NotificationChannel.description\", \"" + notificationChannelDescription + "\");\n" + "        Display.getInstance().setProperty(\"android.NotificationChannel.importance\", \"" + notificationChannelImportance + "\");\n" + "        Display.getInstance().setProperty(\"android.NotificationChannel.enableLights\", \"" + notificationChannelEnableLights + "\");\n" + "        Display.getInstance().setProperty(\"android.NotificationChannel.lightColor\", \"" + notificationChannelLightColor + "\");\n" + "        Display.getInstance().setProperty(\"android.NotificationChannel.enableVibration\", \"" + notificationChannelEnableVibration + "\");\n" + "        Display.getInstance().setProperty(\"android.NotificationChannel.vibrationPattern\", " + notificationChannelVibrationPattern + ");\n" + "        try {\n" + "            Display.getInstance().setProperty(\"android.NotificationChannel.soundUri\", android.media.RingtoneManager.getDefaultUri(android.media.RingtoneManager.TYPE_NOTIFICATION).toString());\n" + "        } catch (Exception ex){}\n";
        ;
        if (request.getArg("android.pushSound", null) != null) {
            pushInitDisplayProperties += "        try {\n" + "            Display.getInstance().setProperty(\"android.NotificationChannel.soundUri\", \"android.resource://" + request.getPackageName() + "/raw/" + request.getArg("android.pushSound", null) + "\");\n" + "        } catch (Exception ex){}\n";
        }
    }
    String waitingForPermissionsRequest = "        if (isWaitingForPermissionResult()) {\n" + "            setWaitingForPermissionResult(false);\n" + "            return;\n" + "        }\n";
    String stubSourceCode;
    try {
        stubSourceCode = "package " + request.getPackageName() + ";\n\n" + "import com.codename1.ui.*;\n" + "import android.os.Bundle;\n" + "import android.content.Intent;\n" + "import android.view.KeyEvent;\n" + "import com.codename1.system.*;\n" + "import com.codename1.impl.android.CodenameOneActivity;\n" + "import com.codename1.impl.android.AndroidImplementation;\n" + "import com.codename1.system.NativeLookup;\n" + "import com.codename1.push.*;\n" + "import com.codename1.ui.*;\n" + "import android.content.IntentFilter;\n" + additionalImports + "\n\n" + "public class " + request.getMainClass() + "Stub extends " + request.getArg("android.customActivity", "CodenameOneActivity") + "{\n";
        stubSourceCode += decodeFunction();
        stubSourceCode += "    public static final String BUILD_KEY = \"" + xorEncode(getBuildKey()) + "\";\n" + "    public static final String PACKAGE_NAME = \"" + request.getPackageName() + "\";\n" + "    public static final String BUILT_BY_USER = \"" + xorEncode(request.getUserName()) + "\";\n" + "    public static final String LICENSE_KEY = \"" + xorEncode(licenseKey) + "\";\n" + "    String [] consumable = new String[]{" + consumable + "};\n" + "    private static " + request.getMainClass() + "Stub stubInstance;\n" + "    private static " + request.getMainClass() + " i;\n" + "    private boolean running;\n" + "    private" + firstTimeStatic + " boolean firstTime = true;\n" + "    private Form currentForm;\n" + "    private static final Object LOCK = new Object();\n" + additionalMembers + headphonesVars + "    public static " + request.getMainClass() + " getAppInstance() {\n" + "        return i;\n" + "    }\n\n" + activityBillingSource + "    protected Object getApp() {\n" + "        return i;\n" + "    }\n\n" + "    public static " + request.getMainClass() + "Stub getInstance() {\n" + "        return stubInstance;\n" + "    }\n\n" + "    public " + request.getMainClass() + "Stub() {\n" + "        stubInstance = this;\n" + "    }\n\n" + "    public static boolean isRunning() {\n" + "        return stubInstance != null && stubInstance.running;\n" + "    }\n\n" + "    public void onCreate(Bundle savedInstanceState) {\n" + "        super.onCreate(savedInstanceState);\n" + facebookHashCode + facebookSupport + streamMode + registerNativeImplementationsAndCreateStubs(new URLClassLoader(new URL[] { codenameOneJar.toURI().toURL() }), srcDir, dummyClassesDir) + oncreate + "\n" + createOnCreateCode(request) + "    }\n" + "    protected void onResume() {\n" + "        running = true;\n" + "        super.onResume();\n" + waitingForPermissionsRequest + "        if(!Display.isInitialized()) {\n" + initStackSize + headphonesOnResume + googlePlayAdViewCode + reinitCode0 + storeIds + gcmSenderId + "        Display.getInstance().setProperty(\"build_key\", d(BUILD_KEY));\n" + "        Display.getInstance().setProperty(\"package_name\", PACKAGE_NAME);\n" + "        Display.getInstance().setProperty(\"built_by_user\", d(BUILT_BY_USER));\n" + useBackgroundPermissionSnippet + pushInitDisplayProperties + // + corporateServer
        androidLicenseKey + // + "        " + registerNativeImplementationsAndCreateStubs(srcDir, dummyClassesDir)
        "        " + createPostInitCode(request) + "        }else{\n" + reinitCode + "        }\n" + "        if (i == null) {\n" + "          i = new " + request.getMainClass() + "();\n" + "          if(i instanceof PushCallback) {\n" + "                com.codename1.impl.CodenameOneImplementation.setPushCallback((PushCallback)i);\n" + "          }\n";
        stubSourceCode += "           if (i instanceof com.codename1.push.PushActionsProvider) {\n" + "                try{AndroidImplementation.installNotificationActionCategories((com.codename1.push.PushActionsProvider)i);}catch(java.io.IOException ex){ex.printStackTrace();}\n" + "           }\n";
    } catch (Exception ex) {
        throw new BuildException("Failed to generate stub source code", ex);
    }
    String fcmRegisterPushCode = "";
    if (useFCM) {
        fcmRegisterPushCode = "try {\n" + "\n" + "                String token = com.google.firebase.iid.FirebaseInstanceId.getInstance().getToken();\n" + "                if (token != null) {\n" + "                    com.codename1.io.Preferences.set(\"push_key\", \"cn1-fcm-\"+token);\n" + "                    if (i instanceof PushCallback) {\n" + "                        ((PushCallback)i).registeredForPush(\"cn1-fcm-\"+token);\n" + "                    }\n" + "                } else {\n" + "                    java.util.Timer timer = new java.util.Timer();\n" + "                    timer.schedule(new java.util.TimerTask() {\n" + "                        public void run() {\n" + "                            runOnUiThread(new Runnable() {\n" + "                                public void run() {\n" + "                                    String token = com.google.firebase.iid.FirebaseInstanceId.getInstance().getToken();\n" + "                                    if (token != null) {\n" + "                                        com.codename1.io.Preferences.set(\"push_key\", \"cn1-fcm-\" + token);\n" + "                                        if (i instanceof PushCallback) {\n" + "                                            ((PushCallback) i).registeredForPush(\"cn1-fcm-\" + token);\n" + "                                        }\n" + "                                    }\n" + "                                }\n" + "                            });\n" + "                        }\n" + "                    }, 2000);\n" + "                }\n" + "            } catch (Exception ex) {\n" + "                if (i instanceof PushCallback) {\n" + "                    ((PushCallback)i).pushRegistrationError(\"Failed to register push: \"+ex.getMessage(), 0);\n" + "                }\n" + "                System.out.println(\"Failed to get fcm token.\");\n" + "                ex.printStackTrace();\n" + "            }";
    }
    try {
        stubSourceCode += "        }\n" + "        if(i instanceof PushCallback) {\n" + "            AndroidImplementation.firePendingPushes((PushCallback)i, this);\n" + "        }\n" + localNotificationCode + "        Display.getInstance().callSerially(new Runnable(){\n" + "            boolean wasStopped = (currentForm == null);\n" + "            Form currForm = currentForm;\n" + "            public void run() {\n" + "                Form displayForm = Display.getInstance().getCurrent();\n" + "                " + request.getMainClass() + "Stub.this.run(displayForm == null ? currForm : displayForm, wasStopped);\n" + "            }\n" + "        });\n" + "        synchronized(LOCK) {\n" + "            currentForm = null;\n" + "        }\n" + "    }\n\n" + "    protected void onPause() {\n" + "        super.onPause();\n" + "        synchronized(LOCK) {\n" + "            currentForm = Display.getInstance().getCurrent();\n" + "        }\n" + "        running = false;\n" + "    }\n\n" + "    public void run(Form currentForm, boolean wasStopped) {\n" + "        if(firstTime) {\n" + "            firstTime = false;\n" + "            i.init(this);\n" + fcmRegisterPushCode + "         } else {\n" + "             synchronized(LOCK) {\n" + "                 if(!wasStopped) {\n" + "                     if(currentForm instanceof Dialog) {\n" + "                         ((Dialog)currentForm).showModeless();\n" + "                     }else{\n" + "                         currentForm.show();\n" + "                     }\n" + "                     fireIntentResult();\n" + "                     setWaitingForResult(false);\n" + "                     return;\n" + "                 }\n" + "             }\n" + "         }\n" + createStartInvocation(request, "i") + "    }\n" + onStopCode + onDestroyCode + " public boolean onKeyDown(int keyCode, KeyEvent event){\n" + " return super.onKeyDown(keyCode, event);\n" + " }\n" + " public String getBase64EncodedPublicKey() {\n" + "     return d(LICENSE_KEY);\n" + " }\n" + consumableCode + "}\n";
    } catch (Exception ex) {
        throw new BuildException("Failure while generating stub source code", ex);
    }
    File androidImplDir = new File(srcDir, "com" + File.separator + "codename1" + File.separator + "impl" + File.separator + "android");
    File stubUtilFile = new File(androidImplDir, "StubUtil.java");
    if (stubUtilFile.exists()) {
        try {
            replaceInFile(stubUtilFile, "//!", "");
            replaceInFile(stubUtilFile, "{{Stub}}", request.getPackageName() + "." + request.getMainClass() + "Stub");
        } catch (IOException ex) {
            throw new BuildException("Failed to update stub Util file", ex);
        }
    }
    boolean backgroundPushHandling = "true".equals(request.getArg("android.background_push_handling", "false"));
    if (!useFCM) {
        File pushServiceFileSourceFile = new File(stubFileSourceDir, "PushNotificationService.java");
        String pushServiceOnCreate = "";
        if (buildToolsVersionInt >= 26 && Integer.parseInt(targetNumber) >= 26) {
            pushServiceOnCreate = "\n    @Override\n" + "    public void onCreate() {\n" + "        super.onCreate();\n" + "        setProperty(\"android.NotificationChannel.id\", \"" + notificationChannelId + "\");\n" + "        setProperty(\"android.NotificationChannel.name\", \"" + notificationChannelName + "\");\n" + "        setProperty(\"android.NotificationChannel.description\", \"" + notificationChannelDescription + "\");\n" + "        setProperty(\"android.NotificationChannel.importance\", \"" + notificationChannelImportance + "\");\n" + "        setProperty(\"android.NotificationChannel.enableLights\", \"" + notificationChannelEnableLights + "\");\n" + "        setProperty(\"android.NotificationChannel.lightColor\", \"" + notificationChannelLightColor + "\");\n" + "        setProperty(\"android.NotificationChannel.enableVibration\", \"" + notificationChannelEnableVibration + "\");\n" + "        setProperty(\"android.NotificationChannel.vibrationPattern\", " + notificationChannelVibrationPattern + ");\n" + "    }\n\n";
        }
        // The runtime test to use to determine if we should call the push() method immediately
        // upon receiving the notification.  By default, we only send *immediately* if the app is
        // in the foreground.  You can use the android.background_push_handling to allow
        // immediate handling in the background as well.
        String handlePushImmediatelyCheck = request.getMainClass() + "Stub.isRunning()";
        String stubIsRunningCheck = handlePushImmediatelyCheck;
        if (backgroundPushHandling) {
            handlePushImmediatelyCheck += " || Display.isInitialized()";
        }
        String pushServiceSourceCode = "package " + request.getPackageName() + ";\n\n" + "import com.codename1.ui.*;\n" + "import com.codename1.push.PushCallback;\n\n" + "public class PushNotificationService extends com.codename1.impl.android.PushNotificationService {\n" + "    public PushCallback getPushCallbackInstance() {\n" + "         if(" + handlePushImmediatelyCheck + ") {\n" + "             " + request.getMainClass() + "Stub stub = " + request.getMainClass() + "Stub.getInstance();\n" + "             final " + request.getMainClass() + " main = stub.getAppInstance();\n" + "             if(main instanceof PushCallback) {\n" + "                 return (PushCallback)main;\n" + "             }\n" + "         }\n" + "         return null;\n" + "    }\n\n" + "    public Class getStubClass() {\n" + "        return " + request.getMainClass() + "Stub.class;\n" + "    }\n" + pushServiceOnCreate + "}\n";
        File pushFileSourceFile = new File(stubFileSourceDir, "PushReceiver.java");
        String vibrateCode = "";
        if (request.getArg("android.pushVibratePattern", null) != null) {
            String pattern = request.getArg("android.pushVibratePattern", null);
            pattern = pattern.trim();
            StringTokenizer token = new StringTokenizer(pattern, ",");
            if (token.countTokens() > 0) {
                try {
                    while (token.hasMoreElements()) {
                        String t = (String) token.nextToken();
                        t = t.trim();
                        Long.parseLong(t);
                    }
                    vibrateCode = "mNotifyBuilder.setVibrate(new long[]{" + pattern + "});";
                } catch (Exception e) {
                // the pattern is not valid
                }
            }
        }
        String pushSound = "";
        if (request.getArg("android.pushSound", null) != null) {
            String soundPath = request.getArg("android.pushSound", null).toLowerCase();
            pushSound = "mNotifyBuilder.setSound(android.net.Uri.parse(\"android.resource://" + request.getPackageName() + "/raw/" + soundPath + "\"));";
        }
        String pushReceiverSourceCode = "package " + request.getPackageName() + ";\n\n" + "import com.codename1.ui.*;\n\n" + "import android.os.Bundle;\n\n" + "import com.codename1.system.*;\n" + "import com.codename1.impl.android.CodenameOneActivity;\n\n" + "import com.codename1.impl.android.AndroidImplementation;\n\n" + "import com.codename1.system.NativeLookup;\n\n" + "import com.codename1.io.ConnectionRequest;\n\n" + "import com.codename1.io.Preferences;\n\n" + "import com.codename1.io.NetworkManager;\n\n" + "import com.codename1.push.PushCallback;\n\n" + "import java.io.InputStream;\n" + "import java.io.DataInputStream;\n" + "import java.io.IOException;\n" + "import android.app.Notification;\n" + "import android.app.NotificationManager;\n" + "import android.app.PendingIntent;\n" + "import android.content.BroadcastReceiver;\n" + "import android.content.Context;\n" + "import android.content.Intent;\n" + "import android.telephony.TelephonyManager;\n" + "import android.content.SharedPreferences.Editor;\n" + "import android.app.NotificationManager\n;" + "import android.app.Activity;\n" + "import com.codename1.impl.android.PushNotificationService;\n" + "import com.codename1.ui.*;\n" + "import android.graphics.Bitmap;\n" + "import android.graphics.drawable.BitmapDrawable;\n" + "import android.graphics.drawable.Drawable;\n" + "import " + xclass("android.support.v4.app.NotificationCompat") + ".Builder;\n" + "import " + xclass("android.support.v4.app.NotificationCompat") + ";\n" + "import android.media.RingtoneManager;\n" + "import android.net.Uri;\n\n" + "public class PushReceiver extends BroadcastReceiver {\n" + "     public static final String C2DM_MESSAGE_TYPE_EXTRA = \"messageType\";\n" + "     public static final String C2DM_MESSAGE_EXTRA = \"message\";\n" + "     public static final String C2DM_MESSAGE_IMAGE = \"image\";\n" + "     public static final String C2DM_MESSAGE_CATEGORY = \"category\";\n" + "     public static final String BUILD_KEY = \"" + xorEncode(getBuildKey()) + "\"\n;" + "     public static final String PACKAGE_NAME = \"" + request.getPackageName() + "\"\n;" + "     public static final String BUILT_BY_USER = \"" + xorEncode(request.getUserName()) + "\"\n;" + "	private static String KEY = \"c2dmPref\";\n" + "     private static String REGISTRATION_KEY = \"registrationKey\";" + "     private Context context;\n\n";
        pushReceiverSourceCode += decodeFunction();
        boolean includePushContent = true;
        pushReceiverSourceCode += "     @Override\n" + "     public void onReceive(Context context, Intent intent) {\n" + "         this.context = context;\n" + "         if (intent.getAction().equals(\"com.google.android.c2dm.intent.REGISTRATION\")) {\n" + "             handleRegistration(context, intent);\n" + "             return;\n" + "         }\n" + "         if (intent.getAction().equals(\"com.google.android.c2dm.intent.RECEIVE\")) {\n" + "             handleMessage(context, intent);\n" + "             return;\n" + "         }\n" + "         if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {\n" + "             if(!com.codename1.impl.android.AndroidImplementation.hasAndroidMarket(context)) {\n" + "                 PushNotificationService.startServiceIfRequired(" + request.getPackageName() + ".PushNotificationService.class, context);\n" + "             }\n" + "             return;\n" + "         }\n" + "     }\n\n" + "     private void handleRegistration(Context context, Intent intent) {\n" + "         final String registration = intent.getStringExtra(\"registration_id\");\n" + "         System.out.println(\"Push handleRegistration() received: \" + registration);\n" + "         " + request.getMainClass() + "Stub stub = " + request.getMainClass() + "Stub.getInstance();\n" + "         if (intent.getStringExtra(\"error\") != null) {\n" + "             final String error = intent.getStringExtra(\"error\");\n" + "             System.out.println(\"Push handleRegistration() error: \" + error);\n" + "             final " + request.getMainClass() + " main = stub.getAppInstance();\n" + "             if(main instanceof PushCallback) {\n" + "                 Display.getInstance().callSerially(new Runnable() {\n" + "                     public void run() {\n" + "                         ((PushCallback)main).pushRegistrationError(error, 0);\n" + "                     }\n" + "                 });\n" + "             }\n" + "         } else if (intent.getStringExtra(\"unregistered\") != null) {\n // do something??? \n" + "             System.out.println(\"Push deregistered!\");\n" + "         } else if (registration != null) {\n" + "             System.out.println(\"Push handleRegistration() Sending registration to server!\");\n" + "             Editor editor = context.getSharedPreferences(KEY, Context.MODE_PRIVATE).edit();\n" + "             editor.putString(REGISTRATION_KEY, registration);\n" + "             Preferences.set(\"push_key\", registration);\n" + "             editor.commit();\n" + "             com.codename1.impl.android.AndroidImplementation.registerPushOnServer(registration, d(BUILT_BY_USER) + '/' + PACKAGE_NAME, (byte)1, \"\", \"" + request.getPackageName() + "\");\n" + "             final " + request.getMainClass() + " main = stub.getAppInstance();\n" + "             if(main instanceof PushCallback) {\n" + "                 Display.getInstance().callSerially(new Runnable() {\n" + "                     public void run() {\n" + "                         ((PushCallback)main).registeredForPush(registration);\n" + "                     }\n" + "                 });\n" + "             }\n" + "         }\n" + "     }\n\n" + "     private android.graphics.Bitmap getBitmapfromUrl(String imageUrl) {\n" + "        try {\n" + "            java.net.URL url = new java.net.URL(imageUrl);\n" + "            java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();\n" + "            connection.setDoInput(true);\n" + "            connection.connect();\n" + "            InputStream input = connection.getInputStream();\n" + "            Bitmap bitmap = android.graphics.BitmapFactory.decodeStream(input);\n" + "            return bitmap;\n" + "\n" + "        } catch (Exception e) {\n" + "            // TODO Auto-generated catch block\n" + "            e.printStackTrace();\n" + "            return null;\n" + "\n" + "        }\n" + "    }\n\n" + "     private void handleMessage(final Context context, Intent intent) {\n" + "         final String messageType = intent.getExtras().getString(C2DM_MESSAGE_TYPE_EXTRA);\n" + "         final String message = intent.getExtras().getString(C2DM_MESSAGE_EXTRA);\n" + "         final String image = intent.getExtras().getString(C2DM_MESSAGE_IMAGE);\n" + "         final String category = intent.getExtras().getString(C2DM_MESSAGE_CATEGORY);\n" + "         System.out.println(\"Push message received: \" + message);\n" + "         System.out.println(\"Push type: \" + messageType);\n" + "         System.out.println(\"Is running: \" + " + request.getMainClass() + "Stub.isRunning());\n" + "         if(" + handlePushImmediatelyCheck + ") {\n" + "             " + request.getMainClass() + "Stub stub = " + request.getMainClass() + "Stub.getInstance();\n" + "             final " + request.getMainClass() + " main = stub.getAppInstance();\n" + "             if(main instanceof PushCallback) {\n" + "                 Display.getInstance().setProperty(\"pushType\", messageType);\n";
        pushReceiverSourceCode += "                 Display.getInstance().callSerially(new Runnable() {\n" + "                     public void run() {\n";
        if (includePushContent) {
            pushReceiverSourceCode += "                         com.codename1.impl.android.AndroidImplementation.initPushContent(message, image, messageType, category, context);\n";
        }
        pushReceiverSourceCode += "                         if(messageType != null && (Integer.parseInt(messageType) == 3 || Integer.parseInt(messageType) == 6) ) {\n" + "                             String[] a = message.split(\";\");\n";
        pushReceiverSourceCode += "                             ((PushCallback)main).push(a[0]);\n" + "                             ((PushCallback)main).push(a[1]);\n" + "                             return;\n" + "                         } else if (\"101\".equals(messageType)) {\n" + "                            ((PushCallback) main).push(message.substring(message.indexOf(\" \")+1));\n" + "                            return;\n" + "                        }\n";
        pushReceiverSourceCode += "                         ((PushCallback)main).push(message);\n" + "                     }\n" + "                 });\n" + "             }\n" + "         }" + "         if (!" + stubIsRunningCheck + ") {\n" + "             ";
        pushReceiverSourceCode += "             com.codename1.impl.android.AndroidImplementation.appendNotification(messageType, message, image, category, context);\n";
        pushReceiverSourceCode += "             int badgeNumber = -1;\n" + "             if (\"101\".equals(messageType)) {\n" + "                 badgeNumber = Integer.parseInt(message.substring(0, message.indexOf(\" \")));\n" + "\n" + "             }" + "if(messageType == null || messageType.length() == 0 || Integer.parseInt(messageType) < 2 || messageType.equals(\"3\") || messageType.equals(\"4\") || messageType.equals(\"5\") || messageType.equals(\"6\") || messageType.equals(\"101\")) {\n" + "                 String actualMessage = message;\n" + "             if (\"101\".equals(messageType)) {\n" + "                     actualMessage = message.substring(message.indexOf(\" \")+1);\n" + "                 }" + "                 String title = \"" + request.getDisplayName() + "\";\n" + "                 if(messageType != null && (Integer.parseInt(messageType) == 3 || Integer.parseInt(messageType) == 6)) {\n" + "                     String[] a = message.split(\";\");\n" + "                     actualMessage = a[0];\n" + "                 }\n" + "                if (messageType != null && Integer.parseInt(messageType) == 4) {\n" + "                    String[] a = message.split(\";\");\n" + "                    title = a[0];\n" + "                    actualMessage = a[1];\n" + "                }\n" + "                 NotificationManager nm = (NotificationManager)context.getSystemService(Activity.NOTIFICATION_SERVICE);\n" + "                 Intent newIntent = new Intent(context, " + request.getMainClass() + "Stub.class);\n" + "                 PendingIntent contentIntent = PendingIntent.getActivity(context, 0, newIntent, PendingIntent.FLAG_CANCEL_CURRENT);\n" + "                 Drawable myIcon = context.getResources().getDrawable(R.drawable.icon);\n" + "                 Bitmap icon = ((BitmapDrawable) myIcon).getBitmap();\n" + "                 int notifyID = 1;\n" + "                 Builder mNotifyBuilder = new NotificationCompat.Builder(context)\n" + "                         .setContentTitle(title)\n" + "                         .setSmallIcon(R.drawable.ic_stat_notify)\n" + "                         .setLargeIcon(icon)\n" + "                         .setContentIntent(contentIntent)\n" + "                         .setAutoCancel(true)\n" + "                         .setWhen(System.currentTimeMillis())\n" + "                         .setTicker(actualMessage);\n" + vibrateCode + "                 if (messageType == null || (Integer.parseInt(messageType) != 5 && Integer.parseInt(messageType) != 6)) {\n" + "                     Uri alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);\n" + "                     mNotifyBuilder.setSound(alarmSound);\n" + pushSound + "                 }\n" + "                 if(android.os.Build.VERSION.SDK_INT >= 21){\n" + "                     mNotifyBuilder.setCategory(\"Notification\");\n" + "                 }\n";
        if (buildToolsVersionInt >= 26 && Integer.parseInt(targetNumber) >= 26) {
            pushReceiverSourceCode += "                com.codename1.impl.android.AndroidImplementation.setNotificationChannel(nm, mNotifyBuilder, context);\n";
        }
        pushReceiverSourceCode += "                 String[] messages = com.codename1.impl.android.AndroidImplementation.getPendingPush(messageType, context);\n" + "                 int numMessages = messages.length;\n" + "                 if (numMessages == 1) {\n" + "                     mNotifyBuilder.setContentText(messages[0]);\n" + "                 } else {\n" + "                         NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();\n" + "                         for (int i = 0; i < messages.length; i++) {\n" + "                             inboxStyle.addLine(messages[i]);\n" + "                         }\n" + "                         mNotifyBuilder.setStyle(inboxStyle);\n" + "                 }\n" + "     if(android.os.Build.VERSION.SDK_INT >= 22) {\n" + "         if (badgeNumber >= 0) {\n" + "             mNotifyBuilder.setNumber(badgeNumber);\n" + "         } else {\n" + "                 mNotifyBuilder.setNumber(numMessages);\n" + "         }\n" + "     }\n";
        pushReceiverSourceCode += "                 if (category != null && numMessages == 1) {\n" + "                     try {\n" + "                         AndroidImplementation.addActionsToNotification(null, category, mNotifyBuilder, newIntent, context);\n" + "                     } catch (java.io.IOException ex) {\n" + "                         ex.printStackTrace();\n" + "                     }\n" + "                 }" + "                 if (image != null && numMessages == 1) {\n" + "                     final Builder fNotifyBuilder = mNotifyBuilder;\n" + "                     final int fNotifyID = notifyID;\n" + "                     final NotificationManager fnm = nm;\n" + "                     android.os.AsyncTask.execute(new Runnable() {\n" + "                         public void run() {\n" + "                             fNotifyBuilder.setStyle(new NotificationCompat.BigPictureStyle()\n" + "                                     .bigPicture(getBitmapfromUrl(image)));/*Notification with Image*/\n" + "                             fnm.notify(fNotifyID, fNotifyBuilder.build());\n" + "                         }\n" + "                     });\n" + "\n" + "               } else {\n" + "                     nm.notify(notifyID, mNotifyBuilder.build());\n" + "               }\n";
        pushReceiverSourceCode += "             }\n" + "         }\n" + "    }\n" + "}\n";
        if (pushPermission) {
            try {
                OutputStream pushSourceStream = new FileOutputStream(pushFileSourceFile);
                pushSourceStream.write(pushReceiverSourceCode.getBytes());
                pushSourceStream.close();
                OutputStream pushServiceSourceStream = new FileOutputStream(pushServiceFileSourceFile);
                pushServiceSourceStream.write(pushServiceSourceCode.getBytes());
                pushServiceSourceStream.close();
            } catch (IOException ex) {
                throw new BuildException("Failed to generate push file", ex);
            }
        }
    } else {
        InputStream is = null;
        OutputStream os = null;
        debug("Generating FirebaseMessagingService...");
        File fcmMessagingServiceFile = new File(androidImplDir, "CN1FirebaseMessagingService.java");
        try {
            String fireBaseMessagingServiceSourcePath = "CN1FirebaseMessagingService.javas";
            fireBaseMessagingServiceSourcePath = "CN1FirebaseMessagingService7.javas";
            is = getClass().getResourceAsStream(fireBaseMessagingServiceSourcePath);
            os = new FileOutputStream(fcmMessagingServiceFile);
            copy(is, os);
        } catch (IOException ex) {
            error("Failed to generate FirebaseMessagingService", ex);
            throw new BuildException("Failed to generate FirebaseMessagingService", ex);
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (Throwable t) {
                }
            }
            if (os != null) {
                try {
                    os.close();
                } catch (Throwable t) {
                }
            }
        }
        try {
            replaceInFile(fcmMessagingServiceFile, "{{DISPLAY_NAME}}", request.getDisplayName());
            if (backgroundPushHandling) {
                replaceInFile(fcmMessagingServiceFile, "allowBackgroundPush = false;", "allowBackgroundPush = true;");
            }
        } catch (IOException ex) {
            throw new BuildException("Failed to update FCM messaging service with app details", ex);
        }
    }
    try {
        OutputStream stubSourceStream = new FileOutputStream(stubFileSourceFile);
        stubSourceStream.write(stubSourceCode.getBytes());
        stubSourceStream.close();
    } catch (IOException ex) {
        throw new BuildException("Failed to write stub source file", ex);
    }
    try {
        File projectPropertiesFile = new File(projectDir, "project.properties");
        Properties projectPropertiesObject = new Properties();
        if (projectPropertiesFile.exists()) {
            FileInputStream fi = new FileInputStream(projectPropertiesFile);
            projectPropertiesObject.load(fi);
            fi.close();
        }
        projectPropertiesObject.setProperty("proguard.config", "proguard.cfg");
        if (request.getArg("android.enableProguard", "true").equals("false")) {
            projectPropertiesObject.remove("proguard.config");
        }
        projectPropertiesObject.setProperty("dex.force.jumbo", "true");
        FileOutputStream projectPropertiesOutputStream = new FileOutputStream(projectPropertiesFile);
        projectPropertiesObject.store(projectPropertiesOutputStream, "Project properties for android build generated by Codename One");
        projectPropertiesOutputStream.close();
    } catch (IOException ex) {
        throw new BuildException("Failed to write project properties", ex);
    }
    String dontObfuscate = "";
    if (request.getArg("android.enableProguard", "true").equals("false")) {
        dontObfuscate = "-dontobfuscate\n";
    }
    // workaround broken optimizer in proguard
    String proguardConfigOverride = "-dontusemixedcaseclassnames\n" + "-dontskipnonpubliclibraryclasses\n" + "-dontpreverify\n" + "-verbose\n" + "-dontoptimize\n" + dontObfuscate + "\n" + "-dontwarn com.google.android.gms.**\n" + "-keep class com.codename1.impl.android.AndroidBrowserComponentCallback {\n" + "*;\n" + "}\n\n" + "-keep class com.codename1.impl.android.AndroidNativeUtil {\n" + "*;\n" + "}\n\n" + "-keepclassmembers class **.R$* {\n" + " public static <fields>;\n" + "}\n\n" + "-keep class **.R$*\n" + "-keep public class * extends android.app.Activity\n" + "-keep public class * extends android.app.Application\n" + "-keep public class * extends android.app.Service\n" + "-keep public class * extends android.content.BroadcastReceiver\n" + "-keep public class * extends android.content.ContentProvider\n" + "-keep public class * extends android.app.backup.BackupAgentHelper\n" + "-keep public class * extends android.preference.Preference\n" + "-keep public class com.android.vending.licensing.ILicensingService\n\n" + "-keep public class " + xclass("android.support.v4.app.RemoteInput") + " {*;}\n" + "-keep public class " + xclass("android.support.v4.app.RemoteInput") + "$Builder {*;}\n" + "-keep public class " + xclass("android.support.v4.app.NotificationCompat") + "$Builder {*;}\n" + "-keep public class " + xclass("android.support.v4.app.NotificationCompat") + "$Action {*;}\n" + "-keep public class " + xclass("android.support.v4.app.NotificationCompat") + "$Action$Builder {*;}\n" + "-keepclasseswithmembernames class * {\n" + "    native <methods>;\n" + "}\n\n" + "-keepclasseswithmembers class * {\n" + "    public <init>(android.content.Context, android.util.AttributeSet);\n" + "}\n\n" + "-keepclasseswithmembers class * {\n" + "    public <init>(android.content.Context, android.util.AttributeSet, int);\n" + "}\n\n" + "-keepclassmembers class * extends android.app.Activity {\n" + "    public void *(android.view.View);\n" + "}\n\n" + "-keepclassmembers enum * {\n" + "    public static **[] values();\n" + "    public static ** valueOf(java.lang.String);\n" + "}\n\n" + "-keep class * implements android.os.Parcelable {\n" + "    public static final android.os.Parcelable$Creator *;\n" + "}\n" + "-keep class com.apperhand.common.** {\n" + "*;\n" + "}\n\n" + "-keep class com.apperhand.device.android.EULAActivity$EulaJsInterface {\n" + "*;\n" + "}\n\n" + "-keepclassmembers public class " + xclass("android.support.v4.app.NotificationCompat") + "$Builder {\n" + "    public " + xclass("android.support.v4.app.NotificationCompat") + "$Builder setChannelId(java.lang.String);\n" + "}\n\n" + facebookProguard + " " + request.getArg("android.proguardKeep", "") + "\n" + googlePlayObfuscation + "-keep class com.google.mygson.**{\n" + "*;\n" + "}\n\n" + "-dontwarn android.support.**\n" + "-dontwarn androidx.**\n" + "-dontwarn com.google.ads.**\n" + "-keepattributes Exceptions, InnerClasses, Signature, Deprecated, SourceFile, LineNumberTable, *Annotation*, EnclosingMethod";
    String gradleObfuscate = "";
    File proguardConfigOverrideFile = new File(projectDir, "proguard.cfg");
    proguardConfigOverrideFile.delete();
    if (request.getArg("android.enableProguard", "true").equals("true")) {
        try {
            createFile(proguardConfigOverrideFile, proguardConfigOverride.getBytes());
        } catch (IOException ex) {
            throw new BuildException("Failed to create proguard config file", ex);
        }
        gradleObfuscate = "            minifyEnabled true\n" + (request.getArg("android.shrinkResources", "false").equals("true") ? "            shrinkResources true\n" : "") + "            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard.cfg'\n";
    }
    HashMap<String, String> env = new HashMap<String, String>();
    env.put("ANDROID_HOME", androidSDKDir.getAbsolutePath());
    env.put("SLAVE_AAPT_TIMEOUT", "20");
    request.putArgument("var.android.playServicesVersion", playServicesVersion);
    String additionalDependencies = request.getArg("gradleDependencies", "");
    if (facebookSupported) {
        minSDK = maxInt("15", minSDK);
        String facebookSdkVersion = request.getArg("android.facebookSdkVersion", "4.39.0");
        ;
        if (request.getArg("android.excludeBolts", "false").equals("true")) {
            additionalDependencies += " compile ('com.facebook.android:facebook-android-sdk:" + facebookSdkVersion + "'){ exclude module: 'bolts-android' }\n";
        } else {
            additionalDependencies += " compile 'com.facebook.android:facebook-android-sdk:" + facebookSdkVersion + "'\n";
        }
    }
    if (legacyGplayServicesMode) {
        additionalDependencies += " compile 'com.google.android.gms:play-services:6.5.87'\n";
    } else {
        if (playServicesPlus) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-plus:" + playServicesVersion + "'\n";
        }
        if (playServicesAuth) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-auth:" + playServicesVersion + "'\n";
        }
        if (playServicesBase) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-base:" + playServicesVersion + "'\n";
        }
        if (playServicesIdentity) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-identity:" + playServicesVersion + "'\n";
        }
        if (playServicesIndexing) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-appindexing:" + playServicesVersion + "'\n";
        }
        if (playServicesInvite) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-appinvite:" + playServicesVersion + "'\n";
        }
        if (playServicesAnalytics) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-analytics:" + playServicesVersion + "'\n";
        }
        if (playServicesCast) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-cast:" + playServicesVersion + "'\n";
        }
        if (playServicesGcm) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-gcm:" + playServicesVersion + "'\n";
        }
        if (playServicesDrive) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-drive:" + playServicesVersion + "'\n";
        }
        if (playServicesFit) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-fitness:" + playServicesVersion + "'\n";
        }
        if (playServicesLocation) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-location:" + playServicesVersion + "'\n";
        }
        if (playServicesMaps) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-maps:" + playServicesVersion + "'\n";
        }
        if (playServicesAds) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-ads:" + playServicesVersion + "'\n";
        }
        if (playServicesVision) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-vision:" + playServicesVersion + "'\n";
        }
        if (playServicesNearBy) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-nearby:" + playServicesVersion + "'\n";
        }
        if (playServicesSafetyPanorama) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-panaroma:" + playServicesVersion + "'\n";
        }
        if (playServicesGames) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-games:" + playServicesVersion + "'\n";
        }
        if (playServicesSafetyNet) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-safenet:" + playServicesVersion + "'\n";
        }
        if (playServicesWallet) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-wallet:" + playServicesVersion + "'\n";
        }
        if (playServicesWear) {
            additionalDependencies += " compile 'com.google.android.gms:play-services-wearable:" + playServicesVersion + "'\n";
        }
    }
    if (purchasePermissions) {
        additionalDependencies += " implementation 'com.android.billingclient:billing:4.0.0'\n";
    }
    String useLegacyApache = "";
    if (request.getArg("android.apacheLegacy", "false").equals("true")) {
        useLegacyApache = " useLibrary 'org.apache.http.legacy'\n";
    }
    String multidex = "";
    if (request.getArg("android.multidex", "false").equals("true")) {
        multidex = "        multiDexEnabled true\n";
        if (Integer.parseInt(minSDK) < 21) {
            if (useAndroidX) {
                if (!additionalDependencies.contains("androidx.multidex:multidex") && !request.getArg("android.gradleDep", "").contains("androidx.multidex:multidex")) {
                    additionalDependencies += " implementation 'androidx.multidex:multidex:1.0.3'\n";
                }
            } else {
                if (!additionalDependencies.contains("com.android.support:multidex") && !request.getArg("android.gradleDep", "").contains("com.android.support:multidex")) {
                    additionalDependencies += " implementation 'com.android.support:multidex:1.0.3'\n";
                }
            }
        }
    }
    String gradleDependency = "classpath 'com.android.tools.build:gradle:1.3.1'\n";
    if (gradleVersionInt < 3) {
        gradleDependency = "classpath 'com.android.tools.build:gradle:2.1.2'\n";
    } else {
        if (gradleVersionInt < 6) {
            if (useAndroidX) {
                gradleDependency = "classpath 'com.android.tools.build:gradle:3.2.0'\n";
            } else {
                gradleDependency = "classpath 'com.android.tools.build:gradle:3.0.1'\n";
            }
        } else {
            gradleDependency = "classpath 'com.android.tools.build:gradle:4.1.1'\n";
        }
    }
    gradleDependency += request.getArg("android.topDependency", "");
    String compileSdkVersion = "'android-21'";
    String quotedBuildToolsVersion = "'23.0.1'";
    compileSdkVersion = "'android-" + request.getArg("android.sdkVersion", "23") + "'";
    quotedBuildToolsVersion = "'" + buildToolsVersion + "'";
    String java8P2 = "";
    if (request.getArg("android.java8", "false").equals("true")) {
        java8P2 = "    compileOptions {\n" + "        sourceCompatibility JavaVersion.VERSION_1_8\n" + "        targetCompatibility JavaVersion.VERSION_1_8\n" + "    }\n";
    }
    String mavenCentral = "";
    if (request.getArg("android.includeMavenCentral", "false").equals("true")) {
        mavenCentral = "    mavenCentral()\n";
    }
    String jcenter = "        jcenter()\n";
    String injectRepo = request.getArg("android.repositories", "");
    if (injectRepo.length() > 0) {
        String[] repos = injectRepo.split(";");
        injectRepo = "";
        for (String s : repos) {
            injectRepo += "    " + s + "\n";
        }
    }
    Properties gradlePropertiesObject = new Properties();
    File gradlePropertiesFile = new File(projectDir, "gradle.properties");
    if (gradlePropertiesFile.exists()) {
        try {
            FileInputStream fi = new FileInputStream(gradlePropertiesFile);
            gradlePropertiesObject.load(fi);
            fi.close();
        } catch (IOException ex) {
            throw new BuildException("Failed to load gradle properties from properties file " + gradlePropertiesFile, ex);
        }
    }
    String supportV4Default = "    compile 'com.android.support:support-v4:23.+'";
    compileSdkVersion = maxPlatformVersion;
    String supportLibVersion = maxPlatformVersion;
    if (buildToolsVersion.startsWith("28")) {
        compileSdkVersion = "28";
        supportLibVersion = "28";
    }
    if (buildToolsVersion.startsWith("29")) {
        compileSdkVersion = "29";
        supportLibVersion = "28";
    }
    jcenter = "      google()\n" + "     jcenter()\n" + "     mavenLocal()\n" + "      mavenCentral()\n";
    injectRepo += "      google()\n" + "     mavenLocal()\n" + "      mavenCentral()\n";
    if (!androidAppBundle && gradleVersionInt < 6 && buildToolsVersionInt < 30) {
        gradlePropertiesObject.put("android.enableAapt2", "false");
    }
    if (!useAndroidX) {
        supportV4Default = "    compile 'com.android.support:support-v4:" + supportLibVersion + ".+'\n     implementation 'com.android.support:appcompat-v7:" + supportLibVersion + ".+'\n";
    } else {
        supportV4Default = "    implementation 'androidx.legacy:legacy-support-v4:1.0.0'\n     implementation 'androidx.appcompat:appcompat:1.0.0'\n";
    }
    String compile = "compile";
    if (useAndroidX) {
        compile = "implementation";
    }
    String gradleProps = "apply plugin: 'com.android.application'\n" + request.getArg("android.gradlePlugin", "") + "\n" + "buildscript {\n" + "    repositories {\n" + jcenter + injectRepo + "    }\n" + "    dependencies {\n" + gradleDependency + "    }\n" + "}\n" + "\n" + "android {\n" + request.getArg("android.gradle.androidx", "") + "\n" + "    compileSdkVersion " + compileSdkVersion + "\n" + "    buildToolsVersion " + quotedBuildToolsVersion + "\n" + useLegacyApache + "\n" + "    dexOptions {\n" + "        preDexLibraries = false\n" + "        incremental false\n" + "        jumboMode = true\n" + "        javaMaxHeapSize \"3g\"\n" + "    }\n" + "    defaultConfig {\n" + "        applicationId \"" + request.getPackageName() + "\"\n" + "        minSdkVersion " + minSDK + "\n" + "        targetSdkVersion " + targetNumber + "\n" + "        versionCode " + intVersion + "\n" + "        versionName \"" + version + "\"\n" + multidex + request.getArg("android.xgradle_default_config", "") + "    }\n" + java8P2 + "    sourceSets {\n" + "        main {\n" + "            aidl.srcDirs = ['src/main/java']\n" + "        }\n" + "    }\n" + "\n" + "    lintOptions {\n" + "        lintOptions {\n" + "        checkReleaseBuilds false\n" + "        abortOnError false\n" + "        }\n" + "    }\n" + "    signingConfigs {\n" + "        release {\n" + "            storeFile file(\"keyStore\")\n" + "            storePassword \"" + escape(request.getCertificatePassword(), "$\"") + "\"\n" + "            keyAlias \"" + escape(request.getKeystoreAlias(), "$\"") + "\"\n" + "            keyPassword \"" + escape(request.getCertificatePassword(), "$\"") + "\"\n" + "        }\n" + "    }\n" + "    buildTypes {\n" + "        release {\n" + gradleObfuscate + "            signingConfig signingConfigs.release\n" + "        }\n" + ((request.getCertificate() != null) ? ("        debug {\n" + "            signingConfig signingConfigs.release\n" + "        }\n") : "") + "    }\n" + "}\n" + "\n" + "repositories {\n" + "    google()\n" + "    jcenter()\n" + injectRepo + "    flatDir{\n" + "              dirs 'libs'\n" + "       }\n" + mavenCentral + "}\n" + "\n" + "dependencies {\n" + "    " + compile + " fileTree(dir: 'libs', include: ['*.jar'])\n" + request.getArg("android.supportv4Dep", supportV4Default) + "\n" + addNewlineIfMissing(additionalDependencies) + addNewlineIfMissing(request.getArg("android.gradleDep", "")) + addNewlineIfMissing(aarDependencies) + "}\n" + request.getArg("android.xgradle", "");
    debug("Gradle File start\n-------\n");
    debug(gradleProps);
    debug("-------\nGradle File end \n");
    File gradleFile = new File(projectDir, "build.gradle");
    try {
        OutputStream gradleStream = new FileOutputStream(gradleFile);
        gradleStream.write(gradleProps.getBytes());
        gradleStream.close();
    } catch (IOException ex) {
        throw new BuildException("Failed to write gradle properties to " + gradleFile, ex);
    }
    File settingsGradle = new File(studioProjectDir, "settings.gradle");
    try {
        replaceInFile(settingsGradle, "My Application2", request.getDisplayName());
    } catch (Exception ex) {
        throw new BuildException("Failed to update settingsGradle with display name", ex);
    }
    gradlePropertiesObject.setProperty("org.gradle.daemon", "true");
    if (request.getArg("android.forceJava8Builder", "false").equals("true")) {
        gradlePropertiesObject.setProperty("org.gradle.java.home", System.getProperty("java.home"));
    }
    gradlePropertiesObject.setProperty("org.gradle.jvmargs", "-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8");
    if (useAndroidX) {
        gradlePropertiesObject.setProperty("android.useAndroidX", "true");
        gradlePropertiesObject.setProperty("android.enableJetifier", "true");
    }
    try {
        FileOutputStream antPropertiesOutputStream = new FileOutputStream(gradlePropertiesFile);
        gradlePropertiesObject.store(antPropertiesOutputStream, "Gradle properties for android build generated by Codename One");
        antPropertiesOutputStream.close();
    } catch (IOException ex) {
        throw new BuildException("Failed to output gradle properties file " + gradlePropertiesFile);
    }
    try {
        migrateSourcesToAndroidX(projectDir);
    } catch (Exception ex) {
        throw new BuildException("Failed to migrate sources to AndroidX", ex);
    }
    if (request.getCertificate() != null) {
        try {
            createFile(new File(projectDir, "keyStore"), request.getCertificate());
        } catch (IOException ex) {
            throw new BuildException("Failed to create keyStore file", ex);
        }
    }
    return true;
}
Also used : Scanner(java.util.Scanner) DocumentBuilderFactory(javax.xml.parsers.DocumentBuilderFactory) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) TarOutputStream(org.xeustechnologies.jtar.TarOutputStream) Node(org.w3c.dom.Node) ArrayList(java.util.ArrayList) Image(java.awt.Image) BufferedImage(java.awt.image.BufferedImage) Document(org.w3c.dom.Document) Properties(java.util.Properties) URL(java.net.URL) BufferedImage(java.awt.image.BufferedImage) NamedNodeMap(org.w3c.dom.NamedNodeMap) ZipInputStream(java.util.zip.ZipInputStream) Color(java.awt.Color) NodeList(org.w3c.dom.NodeList) ZipException(net.lingala.zip4j.exception.ZipException) ZipException(net.lingala.zip4j.exception.ZipException) Graphics2D(java.awt.Graphics2D) StringTokenizer(java.util.StringTokenizer) ZipFile(net.lingala.zip4j.ZipFile) DocumentBuilder(javax.xml.parsers.DocumentBuilder) URLClassLoader(java.net.URLClassLoader) JSONParser(com.codename1.builders.util.JSONParser) ZipFile(net.lingala.zip4j.ZipFile) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map) NamedNodeMap(org.w3c.dom.NamedNodeMap)

Example 2 with Inject

use of com.codename1.rad.annotations.Inject in project CodenameOne by codenameone.

the class GenerateArchetypeFromTemplateMojo method processString.

/**
 * Processes a string with template instructions.  This string may have [dependencies], [css], or [properties]
 * sections with content that will be injected.
 *
 * WARNING:  This will make changes to the existing pom.xml file, and the src/main/css/theme.css file.
 *
 * @param contents String contents to be processed.
 * @throws MojoExecutionException
 */
private void processString(String contents, File projectDir) throws MojoExecutionException {
    File archetypeResourcesDir = new File(projectDir, path("src", "main", "resources", "archetype-resources"));
    try {
        File commonProjectDir = new File(archetypeResourcesDir, "common");
        File pomFile = new File(commonProjectDir, "pom.xml");
        File codenameoneSettingsProperties = new File(commonProjectDir, "codenameone_settings.properties");
        File themeCss = new File(commonProjectDir, "src" + File.separator + "main" + File.separator + "css");
        String pomContents = FileUtils.readFileToString(pomFile, "UTF-8");
        final String origPomContents = pomContents;
        String dependencies = extractDependencies(contents);
        if (!dependencies.isEmpty()) {
            getLog().info("Injecting dependencies:\n" + dependencies + " \ninto " + pomFile);
            String marker = "<!-- INJECT DEPENDENCIES -->";
            pomContents = pomContents.replace(marker, dependencies + "\n" + marker);
        }
        String properties = extractProperties(contents);
        if (!properties.isEmpty()) {
            SortedProperties props = new SortedProperties();
            props.load(new StringReader(properties));
            if (codenameoneSettingsProperties == null || !codenameoneSettingsProperties.exists()) {
                throw new MojoExecutionException("Cannot find codenameone_settings.properties");
            }
            SortedProperties cn1Props = new SortedProperties();
            cn1Props.load(new FileReader(codenameoneSettingsProperties));
            cn1Props.putAll(props);
            getLog().info("Injecting properties:\n" + props + "\n into " + codenameoneSettingsProperties);
            cn1Props.store(new FileWriter(codenameoneSettingsProperties), "Injected properties from template");
        }
        String css = extractCSS(contents);
        if (!css.isEmpty()) {
            if (!themeCss.exists()) {
                themeCss.getParentFile().mkdirs();
            }
            getLog().info("Adding CSS to " + themeCss);
            FileUtils.writeStringToFile(themeCss, css, "UTF-8");
        }
        // We change the codename1.template property to codename1.template.installed so that
        // this mojo won't operate on this project again. (Notice the check at the beginning
        // of execImpl() to return if it doesn't find the codename1.template property).
        pomContents = pomContents.replace("<codename1.template>", "<codename1.templated.installed>").replace("</codename1.template>", "</codename1.template.installed>");
        if (!pomContents.equals(origPomContents)) {
            getLog().info("Writing changes to " + pomFile);
            FileUtils.writeStringToFile(pomFile, pomContents, "UTF-8");
        }
        for (FileContent file : extractFiles(contents)) {
            File f = new File(commonProjectDir, file.path);
            f.getParentFile().mkdirs();
            FileUtils.writeStringToFile(f, file.content, "UTF-8");
        }
    } catch (TemplateParseException ex) {
        throw new MojoExecutionException("Syntax error in template file", ex);
    } catch (IOException ex) {
        throw new MojoExecutionException("Failed to process template file", ex);
    }
}
Also used : MojoExecutionException(org.apache.maven.plugin.MojoExecutionException) SortedProperties(com.codename1.ant.SortedProperties)

Example 3 with Inject

use of com.codename1.rad.annotations.Inject in project CodenameOne by codenameone.

the class BlackBerryOS5Implementation method captureVideo.

public void captureVideo(ActionListener response) {
    captureCallback = new EventDispatcher();
    captureCallback.addListener(response);
    UiApplication.getUiApplication().addFileSystemJournalListener(new FileSystemJournalListener() {

        private long lastUSN;

        private String videoPath;

        public void fileJournalChanged() {
            // next sequence number file system will use
            long USN = FileSystemJournal.getNextUSN();
            for (long i = USN - 1; i >= lastUSN && i < USN; --i) {
                FileSystemJournalEntry entry = FileSystemJournal.getEntry(i);
                if (entry == null) {
                    break;
                }
                String path = entry.getPath();
                if (entry.getEvent() == FileSystemJournalEntry.FILE_ADDED && videoPath == null) {
                    int index = path.indexOf(".3GP");
                    if (index != -1) {
                        videoPath = path;
                    }
                } else if (entry.getEvent() == FileSystemJournalEntry.FILE_RENAMED) {
                    if (path != null && path.equals(videoPath)) {
                        // close the camera
                        UiApplication.getUiApplication().removeFileSystemJournalListener(this);
                        try {
                            EventInjector.KeyEvent inject = new EventInjector.KeyEvent(EventInjector.KeyEvent.KEY_DOWN, Characters.ESCAPE, 0, 200);
                            inject.post();
                            inject.post();
                        } catch (Exception e) {
                        // try to close the camera
                        }
                        captureCallback.fireActionEvent(new ActionEvent("file://" + path));
                        captureCallback = null;
                        videoPath = null;
                        break;
                    }
                }
            }
            lastUSN = USN;
        }
    });
    app.setWaitingForReply(true);
    synchronized (UiApplication.getEventLock()) {
        Invoke.invokeApplication(Invoke.APP_TYPE_CAMERA, new CameraArguments(CameraArguments.ARG_VIDEO_RECORDER));
    }
}
Also used : FileSystemJournalEntry(net.rim.device.api.io.file.FileSystemJournalEntry) CameraArguments(net.rim.blackberry.api.invoke.CameraArguments) EventDispatcher(com.codename1.ui.util.EventDispatcher) FileSystemJournalListener(net.rim.device.api.io.file.FileSystemJournalListener) ActionEvent(com.codename1.ui.events.ActionEvent) DatabaseIOException(net.rim.device.api.database.DatabaseIOException) DatabasePathException(net.rim.device.api.database.DatabasePathException) IOException(java.io.IOException) EventInjector(net.rim.device.api.system.EventInjector)

Example 4 with Inject

use of com.codename1.rad.annotations.Inject in project CodenameOne by codenameone.

the class BlackBerryImplementation method capturePhoto.

public void capturePhoto(ActionListener response) {
    EventLog.getInstance().logInformationEvent("capturePhoto");
    captureCallback = new EventDispatcher();
    captureCallback.addListener(response);
    UiApplication.getUiApplication().addFileSystemJournalListener(new FileSystemJournalListener() {

        private long lastUSN;

        public void fileJournalChanged() {
            long USN = FileSystemJournal.getNextUSN();
            for (long i = USN - 1; i >= lastUSN; --i) {
                FileSystemJournalEntry entry = FileSystemJournal.getEntry(i);
                if (entry != null) {
                    if (entry.getEvent() == FileSystemJournalEntry.FILE_ADDED) {
                        if (entry.getPath().indexOf(".jpg") != -1) {
                            lastUSN = USN;
                            String path = entry.getPath();
                            // close the camera
                            UiApplication.getUiApplication().removeFileSystemJournalListener(this);
                            try {
                                EventInjector.KeyEvent inject = new EventInjector.KeyEvent(EventInjector.KeyEvent.KEY_DOWN, Characters.ESCAPE, 0, 200);
                                inject.post();
                                inject.post();
                            } catch (Exception e) {
                            // try to close the camera
                            }
                            EventLog.getInstance().logInformationEvent("path " + path);
                            captureCallback.fireActionEvent(new ActionEvent("file://" + path));
                            captureCallback = null;
                        }
                    }
                }
            }
            lastUSN = USN;
        }
    });
    app.setWaitingForReply(true);
    synchronized (UiApplication.getEventLock()) {
        Invoke.invokeApplication(Invoke.APP_TYPE_CAMERA, new CameraArguments());
    }
}
Also used : FileSystemJournalEntry(net.rim.device.api.io.file.FileSystemJournalEntry) CameraArguments(net.rim.blackberry.api.invoke.CameraArguments) EventDispatcher(com.codename1.ui.util.EventDispatcher) FileSystemJournalListener(net.rim.device.api.io.file.FileSystemJournalListener) ActionEvent(com.codename1.ui.events.ActionEvent) IOException(java.io.IOException) RecordStoreException(javax.microedition.rms.RecordStoreException) MediaException(javax.microedition.media.MediaException) ConnectionNotFoundException(javax.microedition.io.ConnectionNotFoundException)

Example 5 with Inject

use of com.codename1.rad.annotations.Inject in project CodenameOne by codenameone.

the class BlackBerryImplementation method captureAudio.

public void captureAudio(ActionListener response) {
    int h = CodeModuleManager.getModuleHandle("net_rim_bb_voicenotesrecorder");
    if (h == 0) {
        throw new RuntimeException("capture audio works only if Voice Notes is installed");
    }
    captureCallback = new EventDispatcher();
    captureCallback.addListener(response);
    UiApplication.getUiApplication().addFileSystemJournalListener(new FileSystemJournalListener() {

        private long lastUSN;

        public void fileJournalChanged() {
            long USN = FileSystemJournal.getNextUSN();
            for (long i = USN - 1; i >= lastUSN; --i) {
                FileSystemJournalEntry entry = FileSystemJournal.getEntry(i);
                if (entry != null) {
                    String path = entry.getPath();
                    if (entry.getEvent() == FileSystemJournalEntry.FILE_ADDED && path.endsWith(".amr")) {
                        UiApplication.getUiApplication().removeFileSystemJournalListener(this);
                        try {
                            EventInjector.KeyEvent inject = new EventInjector.KeyEvent(EventInjector.KeyEvent.KEY_DOWN, Characters.ESCAPE, 0, 200);
                            inject.post();
                        } catch (Exception e) {
                        // try to close the voicenotesrecorder
                        }
                        captureCallback.fireActionEvent(new ActionEvent("file://" + path));
                        captureCallback = null;
                        break;
                    }
                }
            }
            lastUSN = USN;
        }
    });
    app.setWaitingForReply(true);
    ApplicationDescriptor desc = new ApplicationDescriptor(CodeModuleManager.getApplicationDescriptors(h)[0], null);
    try {
        ApplicationManager.getApplicationManager().runApplication(desc, true);
    } catch (ApplicationManagerException e) {
        EventLog.getInstance().logErrorEvent("err " + e.getMessage());
        e.printStackTrace();
    }
}
Also used : FileSystemJournalListener(net.rim.device.api.io.file.FileSystemJournalListener) ActionEvent(com.codename1.ui.events.ActionEvent) IOException(java.io.IOException) RecordStoreException(javax.microedition.rms.RecordStoreException) MediaException(javax.microedition.media.MediaException) ConnectionNotFoundException(javax.microedition.io.ConnectionNotFoundException) FileSystemJournalEntry(net.rim.device.api.io.file.FileSystemJournalEntry) EventDispatcher(com.codename1.ui.util.EventDispatcher)

Aggregations

ActionEvent (com.codename1.ui.events.ActionEvent)3 EventDispatcher (com.codename1.ui.util.EventDispatcher)3 IOException (java.io.IOException)3 FileSystemJournalEntry (net.rim.device.api.io.file.FileSystemJournalEntry)3 FileSystemJournalListener (net.rim.device.api.io.file.FileSystemJournalListener)3 ConnectionNotFoundException (javax.microedition.io.ConnectionNotFoundException)2 MediaException (javax.microedition.media.MediaException)2 RecordStoreException (javax.microedition.rms.RecordStoreException)2 CameraArguments (net.rim.blackberry.api.invoke.CameraArguments)2 SortedProperties (com.codename1.ant.SortedProperties)1 JSONParser (com.codename1.builders.util.JSONParser)1 ActionNode (com.codename1.rad.nodes.ActionNode)1 BorderLayout (com.codename1.ui.layouts.BorderLayout)1 Color (java.awt.Color)1 Graphics2D (java.awt.Graphics2D)1 Image (java.awt.Image)1 BufferedImage (java.awt.image.BufferedImage)1 URL (java.net.URL)1 URLClassLoader (java.net.URLClassLoader)1 ArrayList (java.util.ArrayList)1