from io import BytesIO import os import shutil import subprocess import time from zipfile import ZipFile import base64 import requests from werkzeug.datastructures import FileStorage from PIL import Image from celery import Celery import project_config def vprint(*data): if project_config.verbose: print(*data) celery = Celery(__name__) celery.conf.broker_url = os.environ.get("CELERY_BROKER_URL", "redis://localhost:6379") celery.conf.result_backend = os.environ.get("CELERY_RESULT_BACKEND", "redis://localhost:6379") celery.conf.task_track_started = True celery.conf.task_time_limit = 1200 # celery.conf.task_serializer = 'pickle' # celery.conf.result_serializer = 'pickle' def create_folder(package_id): path = os.path.join(project_config.temp_folder, package_id) if not os.path.exists(path): os.mkdir(path) else: shutil.rmtree(path) os.mkdir(path) vprint(path) path_dest = os.path.join(path, project_config.base_project_name) if not os.path.exists(path_dest): shutil.copytree(project_config.base_project, path_dest) code_path = "app/src/main/java/" orig_code = "com/example/qmeralitesamplecode" path_package_id = package_id.replace(".", "/") c_code_path = os.path.join(path_dest, code_path, path_package_id) orig_code_path = os.path.join(path_dest, code_path, orig_code) if not os.path.exists(c_code_path): shutil.copytree(orig_code_path, c_code_path) shutil.rmtree(orig_code_path) return path_dest, c_code_path def change_acc(c_code_path, acc, enable_sms): main_activity = os.path.join(c_code_path, "MAB.java") with open(main_activity, "r") as f: file_source = f.read() replaced = file_source.replace("***REPLACE***WITH***YOUR***QMERA***ACCOUNT***", acc) if enable_sms == 1: replaced = replaced.replace("isEnabledSMS = false", "isEnabledSMS = true") with open(main_activity, "w") as f: f.write(replaced) def change_url(c_code_path, url): main_activity = os.path.join(c_code_path, "MAB.java") with open(main_activity, "r") as f: file_source = f.read() replaced = file_source.replace("https://www.google.com", url) with open(main_activity, "w") as f: f.write(replaced) def change_name(path_dest, name, enable_sms): manifest = os.path.join(path_dest, "app/src/main/AndroidManifest.xml") string_res = os.path.join(path_dest, "app/src/main/res/values/strings.xml") with open(manifest, "r") as f: lines = f.readlines() # file_source = f.read() with open(manifest, "w") as f: for line in lines: if "NexilisLite" in line: line = line.replace("NexilisLite", name) if enable_sms == 0: if "SMS" not in line: f.write(line) else: f.write(line) with open(string_res, "r") as f: file_source = f.read() replaced = file_source.replace("Nexilis Sport", name) with open(string_res, "w") as f: f.write(replaced) if enable_sms == 0: with open(manifest, "r") as f: lines = f.readlines() with open(manifest, "w") as f: counter = 0 for line in lines: if counter == 0: if "" in line: counter = 3 else: f.write(line) else: counter = counter - 1 if package.__contains__('digipos') or package.__contains__('digisales'): settings_gradle = os.path.join(path_dest, "settings.gradle") with open(settings_gradle, "r") as f: lines = f.readlines() with open(settings_gradle, "w") as f: counter = 0 for line in lines: if counter == 0: if "forallapps" in line: counter = 5 else: f.write(line) else: counter = counter - 1 mab_path = os.path.join(path_dest, code_path, "MAB.java") with open(mab_path, "r") as f: lines = f.readlines() with open(mab_path, "w") as f: counter1 = 0 counter2 = 0 for line in lines: if "fordigisales1" in line: counter1 = 132 elif "fordigisales2" in line: counter2 = 46 elif counter1 > 0: counter1 = counter1 - 1 elif counter2 > 0: counter2 = counter2 - 1 else: f.write(line) notif_center = os.path.join(path_dest, code_path, "notificationCenterTab/NotificationCenterFragment.java") with open(notif_center, "r") as f: lines = f.readlines() with open(notif_center, "w") as f: counter = 0 for line in lines: if counter == 0: if "fordigisales" in line: counter = 14 else: f.write(line) else: counter = counter - 1 else: settings_gradle = os.path.join(path_dest, "settings.gradle") with open(settings_gradle, "r") as f: lines = f.readlines() with open(settings_gradle, "w") as f: counter = 0 for line in lines: if counter == 0: if "fordigisales" in line: counter = 5 else: f.write(line) else: counter = counter - 1 colorValue = os.path.join(path_dest, "app/src/main/res/values/colors.xml") with open(colorValue, "r") as c: lines = c.readlines() with open(colorValue, "w") as c: for lineColor in lines: if "fordigisales" in lineColor: continue else: c.write(lineColor) if package == "com.nexilis.persija" or package == "io.newuniverse.GoToMalls" or package == "io.qmera.mylab": string_res = os.path.join(path_dest, "app/src/main/res/values/strings.xml") string_en_res = os.path.join(path_dest, "app/src/main/res/values-en/strings.xml") string_id_res = os.path.join(path_dest, "app/src/main/res/values-in/strings.xml") with open(string_res, "r") as f: file_source = f.read() replaced = file_source.replace("Nexilis", "Qmera") with open(string_res, "w") as f: f.write(replaced) with open(string_en_res, "r") as f: file_source = f.read() replaced = file_source.replace("Nexilis", "Qmera") with open(string_en_res, "w") as f: f.write(replaced) with open(string_id_res, "r") as f: file_source = f.read() replaced = file_source.replace("Nexilis", "Qmera") with open(string_id_res, "w") as f: f.write(replaced) old_img_powered = "app/src/main/res/drawable/pb_powered_button.png" old_img_powered = os.path.join(path_dest, old_img_powered) old_new_img_powered = "app/src/main/res/drawable/pb_powered_button_temp.png" old_new_img_powered = os.path.join(path_dest, old_new_img_powered) shutil.move(old_img_powered, old_new_img_powered) img_powered = "app/src/main/res/drawable/pb_powered_button1.png" img_powered = os.path.join(path_dest, img_powered) new_img_powered = "app/src/main/res/drawable/pb_powered_button.png" new_img_powered = os.path.join(path_dest, new_img_powered) shutil.move(img_powered, new_img_powered) if package == "com.telkomsel.smb": string_res = os.path.join(path_dest, "app/src/main/res/values/strings.xml") string_en_res = os.path.join(path_dest, "app/src/main/res/values-en/strings.xml") string_id_res = os.path.join(path_dest, "app/src/main/res/values-in/strings.xml") with open(string_res, "r") as f: file_source = f.read() replaced = file_source.replace("Nexilis", "Telkomsel") with open(string_res, "w") as f: f.write(replaced) with open(string_en_res, "r") as f: file_source = f.read() replaced = file_source.replace("Nexilis", "Telkomsel") with open(string_en_res, "w") as f: f.write(replaced) with open(string_id_res, "r") as f: file_source = f.read() replaced = file_source.replace("Nexilis", "Telkomsel") with open(string_id_res, "w") as f: f.write(replaced) old_img_powered = "app/src/main/res/drawable/pb_powered_button.png" old_img_powered = os.path.join(path_dest, old_img_powered) old_new_img_powered = "app/src/main/res/drawable/pb_powered_button_temp.png" old_new_img_powered = os.path.join(path_dest, old_new_img_powered) shutil.move(old_img_powered, old_new_img_powered) img_powered = "app/src/main/res/drawable/pb_powered_button2.png" img_powered = os.path.join(path_dest, img_powered) new_img_powered = "app/src/main/res/drawable/pb_powered_button.png" new_img_powered = os.path.join(path_dest, new_img_powered) shutil.move(img_powered, new_img_powered) def change_logo(path_dest, logo, logo_float=None): img_path = "app/src/main/res/drawable/ic_launcher.png" img_path = os.path.join(path_dest, img_path) img_notif = "app/src/main/res/drawable/pb_ball.png" img_notif = os.path.join(path_dest, img_notif) img_path_float = "app/src/main/res/drawable/pb_button.png" img_path_float = os.path.join(path_dest, img_path_float) if isinstance(logo, str): logo = requests.get('https://digixplatform.com/dashboardv2/uploads/logo/{}'.format(logo), verify=False) with open(img_path, "wb") as f: f.write(logo.content) with open(img_path, "rb") as f: logo = Image.open(f) logo = logo.resize((512, 512)) if logo_float: logo_float = requests.get('https://digixplatform.com/dashboardv2/uploads/logofloat/{}'.format(logo_float), verify=False) with open(img_path_float, "wb") as f: f.write(logo_float.content) with open(img_path_float, "rb") as f: logo_float = Image.open(f) logo_float = logo_float.resize((150, 150)) else: logo = Image.open(BytesIO(logo)) logo = logo.resize((512, 512)) if logo_float: logo_float = Image.open(BytesIO(logo_float)) logo_float = logo_float.resize((150, 150)) logo.save(img_path, "PNG") if logo_float: logo_float.save(img_path_float, "PNG") logo_float.save(img_notif, "PNG") def change_tab(path_dest, tabs, tab_icon, package, tab3_mode, tab_amount): default_tab_icon = ["tab{}.png".format(x) for x in tabs] for i, icon in enumerate(default_tab_icon): if not tab_icon[i]: continue img_path = "app/src/main/res/drawable" img_path = os.path.join(path_dest, img_path, icon) if isinstance(tab_icon[i], str): logo = requests.get('https://digixplatform.com/dashboardv2/uploads/tab_icon/{}'.format(tab_icon[i]), verify=False) with open(img_path, "wb") as f: f.write(logo.content) with open(img_path, "rb") as f: logo = Image.open(f) logo = logo.resize((150, 150)) logo.save(img_path, "PNG") else: logo = Image.open(BytesIO(tab_icon[i])) logo = logo.resize((150, 150)) logo.save(img_path, "PNG") path_package_id = package.replace(".", "/") code_path = "app/src/main/java/" sobj_code_path = os.path.join(path_dest, code_path, path_package_id, "SObj.java") print(sobj_code_path) with open(sobj_code_path, "r") as f: file_source = f.read() replaced = file_source.replace("1,2,3,4", ",".join(tabs)) with open(sobj_code_path, "w") as f: f.write(replaced) main_code_path = os.path.join(path_dest, code_path, path_package_id, "MAB.java") with open(main_code_path, "r") as f: file_source = f.read() replaced = file_source.replace('tab3 = "0"', 'tab3 = "{}"'.format(tab3_mode)) with open(main_code_path, "w") as f: f.write(replaced) prefs_code_path = os.path.join(path_dest, code_path, path_package_id, "util/PrefsUtil.java") with open(prefs_code_path, "r") as f: file_source = f.read() replaced = file_source.replace('DEFAULT_TAB_AMOUNT = "4"', 'DEFAULT_TAB_AMOUNT = "{}"'.format(tab_amount)) with open(prefs_code_path, "w") as f: f.write(replaced) def change_fb(path_dest, fb_order, package, fb_icon): fb_order_list = [int(x) for x in fb_order.split(",")] default_fb_icon = ["pb_button_chat.png", "pb_button_call.png", "pb_button_cc.png", "pb_button_stream.png", "nexilis_fb_04.png"] for x, i in enumerate(fb_order_list): if not fb_icon[x]: continue img_path = "app/src/main/res/drawable-nodpi" if i - 1 in range(-len(default_fb_icon), len(default_fb_icon)): img_path = os.path.join(path_dest, img_path, default_fb_icon[i - 1]) if isinstance(fb_icon[x], str): logo = requests.get('https://digixplatform.com/dashboardv2/uploads/fb_icon/{}'.format(fb_icon[x]), verify=False) with open(img_path, "wb") as f: f.write(logo.content) with open(img_path, "rb") as f: logo = Image.open(f) logo = logo.resize((150, 150)) logo.save(img_path, "PNG") else: logo = Image.open(BytesIO(fb_icon[x])) logo = logo.resize((150, 150)) logo.save(img_path, "PNG") path_package_id = package.replace(".", "/") code_path = "app/src/main/java/" main_code_path = os.path.join(path_dest, code_path, path_package_id, "MAB.java") with open(main_code_path, "r") as f: file_source = f.read() replaced = file_source.replace('dockedPlacement = "1,2,3,4,5"', 'dockedPlacement = "{}"'.format(fb_order)) with open(main_code_path, "w") as f: f.write(replaced) def change_background(path_dest, background): # if isinstance(background, str): # background = background.split(",") # for i,b in enumerate(background): # n = i+1 # img_path = "app/src/main/res/drawable-nodpi/pb_lbackground_{}.png".format(n) # img_path = os.path.join(path_dest, img_path) # logo = requests.get('https://digixplatform.com/dashboardv2/uploads/background/{}'.format(b), verify=False) # with open(img_path, "wb") as f: # f.write(logo.content) # with open(img_path, "rb") as f: # logo = Image.open(f) # logo = logo.resize((600, 1250)) # logo.save(img_path, "PNG") # else: # img_path = "app/src/main/res/drawable-nodpi/pb_lbackground_1.png" # logo = Image.open(BytesIO(background)) # logo = logo.resize((600, 1250)) # logo.save(img_path, "PNG") pass def change_access(path_dest, access_model, package): access = ["CPAAS_MODE_FLOATING", "CPAAS_MODE_DOCKED", "CPAAS_MODE_BURGER"] path_package_id = package.replace(".", "/") code_path = "app/src/main/java/" code_path = os.path.join(path_dest, code_path, path_package_id, "util", "PrefsUtil.java") print(code_path) with open(code_path, "r") as f: file_source = f.read() replaced = file_source.replace("= CPAAS_MODE_DOCKED", "= {}".format(access[access_model])) with open(code_path, "w") as f: f.write(replaced) def change_certificate(path_dest, key, keyfile, keytool): keyfile_name = "{}.keystore".format(key["alias"]) keyfile_path = os.path.join(path_dest, keyfile_name) if keyfile: if isinstance(keyfile, FileStorage): keyfile.save(keyfile_path) else: with open(keyfile_path, 'wb') as f: f.write(base64.b64decode(bytes(keyfile, 'ascii'))) else: vprint("keytool run") os.chdir(path_dest) vprint("current working directory: ", os.getcwd()) dname = "CN={}, OU={}, O={}, L={}, S={}, C={}".format(key["common_name"], key["organization_unit"], key["organization_name"], key["locality_name"], key["state_name"], key["country"]) cmd = [keytool, "-genkey", "-v", "-storetype", "JKS", "-keystore", keyfile_path, "-alias", key["alias"], "-keyalg", "RSA", "-keysize", "2048", "-validity", "10000", "-dname", dname, "-storepass", key["store_password"], "-keypass", key["key_password"]] vprint(cmd) subprocess.run(cmd) vprint("keytool end") build_gradle = os.path.join(path_dest, 'app/build.gradle') with open(build_gradle, "r") as f: file_source = f.read() replaced = file_source.replace("allyourbase", key["store_password"]) replaced = replaced.replace("arebelongto", key["key_password"]) replaced = replaced.replace("key-qmeralite", key["alias"]) with open(build_gradle, "w") as f: f.write(replaced) def change_huawei_file(path_dest, huawei_file, package_id): huaweifile_name = "agconnect-services.json" huaweifile_path = os.path.join(path_dest, "app/{}".format(huaweifile_name)) if huawei_file: if isinstance(huawei_file, FileStorage): huawei_file.save(huaweifile_path) else: with open(huaweifile_path, 'wb') as f: f.write(base64.b64decode(bytes(huawei_file, 'ascii'))) path_package_id = package_id.replace(".", "/") code_path = "app/src/main/java/" main_code_path = os.path.join(path_dest, code_path, path_package_id, "MAB.java") with open(main_code_path, "r") as f: file_source = f.read() replaced = file_source.replace('isHMSEnabled = false', 'isHMSEnabled = true') with open(main_code_path, "w") as f: f.write(replaced) def change_fms_file(path_dest, fms_enable): build_gradle = os.path.join(path_dest, 'app/build.gradle') with open(build_gradle, "r") as f: lines = f.readlines() with open(build_gradle, "w") as f: if fms_enable == 0: for line in lines: if "withfcm" in line: continue else: f.write(line) else: for line in lines: if "-nofcm" in line: continue else: f.write(line) def change_adblock_file(path_dest, package, use_adblock): if use_adblock == 0: build_gradle = os.path.join(path_dest, 'app/build.gradle') with open(build_gradle, "r") as f: lines = f.readlines() with open(build_gradle, "w") as f: for line in lines: if "removeAdblock" in line: continue else: f.write(line) path_package_id = package.replace(".", "/") code_path = "app/src/main/java/" code_path = os.path.join(path_dest, code_path, path_package_id) javas = [os.path.join(dp, f) for dp, dn, filenames in os.walk(code_path) for f in filenames] for j in javas: print(j) with open(j, "r") as f: lines = f.readlines() with open(j, "w") as f: counter = 0 for line in lines: if counter == 0: if "removeAdblock" in line: continue elif "import org.adblockplus.libadblockplus.android" in line: continue elif "removeAdbMultiLine" in line: counter = 11 elif "extends AdblockWebView" in line: replaced = line.replace("extends AdblockWebView", "extends WebView") f.write(replaced) else: f.write(line) else: counter = counter - 1 def run_build(path_dest): gradlew = os.path.join(path_dest, "gradlew") error = "" ret = subprocess.run([gradlew, 'clean', 'assembleRelease'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if ret.returncode == 0: ret = subprocess.run([gradlew, 'bundleRelease'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if ret.returncode != 0: error = "{}\n".format(ret.stderr.decode()) vprint(error) else: error = "{}\n".format(ret.stderr.decode()) vprint(error) return ret.returncode, error def deliver_apk(path_dest, package_id, key, key_exists): apk_dir = os.path.join(path_dest, 'app/build/outputs/apk/release/app-release.apk') aab_dir = os.path.join(path_dest, 'app/build/outputs/bundle/release/app-release.aab') keystore_name = '{}.keystore'.format(key["alias"]) keystore_dir = os.path.join(path_dest, keystore_name) timenow = time.time() apk_name = "{}{}.apk".format(package_id, timenow) aab_name = "{}{}.aab".format(package_id, timenow) zip_name = "{}{}.zip".format(package_id, timenow) new_apk_dir = os.path.join(project_config.apk_folder, apk_name) new_aab_dir = os.path.join(project_config.apk_folder, aab_name) new_dir = os.path.join(project_config.apk_folder, zip_name) vprint(apk_dir) shutil.move(apk_dir, new_apk_dir) try: shutil.move(aab_dir, new_aab_dir) with ZipFile(new_dir, 'w') as zip_file: zip_file.write(new_apk_dir, os.path.basename(new_apk_dir)) zip_file.write(new_aab_dir, os.path.basename(new_aab_dir)) if not key_exists: zip_file.write(keystore_dir, os.path.basename(keystore_dir)) os.remove(new_apk_dir) os.remove(new_aab_dir) project_path = os.path.join(project_config.temp_folder, package_id) shutil.rmtree(project_path) return {"status": "0", "name": zip_name} except Exception as e: return {"status": "4", "message": "Deliver APK & AAB failed"} def change_version(path_dest, version_code, version_name): build_gradle = os.path.join(path_dest, 'app/build.gradle') with open(build_gradle, "r") as f: file_source = f.read() replaced = file_source.replace("versionCode 3", "versionCode {}".format(version_code)) replaced = replaced.replace('versionName "3.0"', 'versionName "{}"'.format(version_name)) with open(build_gradle, "w") as f: f.write(replaced) @celery.task(name="create_task") def create_task(package_id, enable_sms, app_name, key, keystore, access_model, version_code, version_name, enable_location, font, fb_order, fb_icon, tabs, tab_icon, tab3_mode, tab_amount, huawei_file, fms_enable, use_adblock, key_exists, acc=None, logo=None, logo_float=None, background=None, url=None): path_dest, c_code_path = create_folder(package_id) vprint("path_dest: " + path_dest) vprint("c_code_path: " + c_code_path) if acc: change_acc(c_code_path, acc, enable_sms) if url: change_url(c_code_path, url) change_name(path_dest, app_name, enable_sms) change_certificate(path_dest, key, keystore, project_config.keytool) change_package(path_dest, package_id, enable_location) change_version(path_dest, version_code, version_name) change_font(path_dest, font, package_id) if logo: change_logo(path_dest, logo, logo_float) if background: change_background(path_dest, background) change_fb(path_dest, fb_order, package_id, fb_icon) change_access(path_dest, access_model, package_id) change_tab(path_dest, tabs, tab_icon, package_id, tab3_mode, tab_amount) change_huawei_file(path_dest, huawei_file, package_id) change_fms_file(path_dest, fms_enable) change_adblock_file(path_dest, package_id, use_adblock) os.chdir(path_dest) return_code, error = run_build(path_dest) if return_code == 0: return deliver_apk(path_dest, package_id, key, key_exists) return {"status": "3", "message": "Build failed"}