// This comment exists for a reason, do not delete apply plugin: 'com.android.application' apply plugin: 'kotlin-android' // kotlin apply plugin: 'kotlin-parcelize' apply plugin: 'kotlin-kapt' apply plugin: 'AndResGuard' import groovy.xml.XmlUtil android { String CONFIG_ID = "" String FIRST_LAUNCH = "" int ACTIVATE_REPORTING_RATIO = 0 buildFeatures { viewBinding true dataBinding true } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = "1.8" } dexOptions { // jumboMode = true javaMaxHeapSize "4g" preDexLibraries true maxProcessCount 8 } aaptOptions { additionalParameters "--no-version-vectors" } kapt { useBuildCache = true javacOptions { // 增加注解处理器的最大错误次数,默认为 100 option("-Xmaxerrs", 500) } } defaultConfig { vectorDrawables.useSupportLibrary = true multiDexEnabled true ndk { // x86 本来是为了模拟器用户使用 RenderScript 用的,但是其实用到 RenderScript 的人本来就不多 (一天不到 100),用模拟器的人就更少了 // 加了 x86 反而会导致用户没法使用微博登录,因为微博没有提供 x86 的 SO ... // 还会增大 APK 体积,所以还是去掉吧。数据可见 https://sentry.shanqu.cc/organizations/lightgame/issues/144232/?project=22 abiFilters "armeabi-v7a", "arm64-v8a" } renderscriptTargetApi 18 renderscriptSupportModeEnabled true // 由于app只针对中文用户,所以仅保留zh资源,其他删掉 resConfigs "zh" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode rootProject.ext.versionCode versionName rootProject.ext.versionName applicationId rootProject.ext.applicationId proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-fresco.txt' // 推广用的配置 id buildConfigField "String", "CONFIG_ID", "\"${CONFIG_ID}\"" // 首次启动的跳转配置 buildConfigField "String", "FIRST_LAUNCH", "\"${FIRST_LAUNCH}\"" buildConfigField "int", "ACTIVATE_REPORTING_RATIO", "${ACTIVATE_REPORTING_RATIO}" // All third-party appid/appkey buildConfigField "String", "API_HOST", "\"${API_HOST}\"" buildConfigField "String", "NEW_API_HOST", "\"${NEW_API_HOST}\"" buildConfigField "String", "VAPI_HOST", "\"${VAPI_HOST}\"" buildConfigField "String", "WECHAT_APPID", "\"${WECHAT_APPID}\"" buildConfigField "String", "WECHAT_SECRET", "\"${WECHAT_SECRET}\"" buildConfigField "String", "TENCENT_APPID", "\"${TENCENT_APPID}\"" buildConfigField "String", "WEIBO_APPKEY", "\"${WEIBO_APPKEY}\"" } // gradle 2.2以上默认同时启用v1和v2(优先用于Android N) signingConfigs { debug { v1SigningEnabled true v2SigningEnabled true } release { v1SigningEnabled true v2SigningEnabled true } } kapt { arguments { arg("AROUTER_MODULE_NAME", project.name) } } buildTypes { debug { debuggable true minifyEnabled false zipAlignEnabled false signingConfig signingConfigs.debug buildConfigField "String", "EXPOSURE_REPO", "\"test\"" buildConfigField "String", "EXPOSURE_VERSION", "\"E4\"" multiDexKeepProguard file("tinker_multidexkeep.pro") } release { debuggable false minifyEnabled true zipAlignEnabled true shrinkResources true signingConfig signingConfigs.release buildConfigField "String", "EXPOSURE_REPO", "\"exposure\"" buildConfigField "String", "EXPOSURE_VERSION", "\"E4\"" multiDexKeepProguard file("tinker_multidexkeep.pro") } } // Ignore useless variant variantFilter { variant -> def names = variant.flavors*.name def isDebugType = variant.buildType.name == "debug" if ((names.contains("tea") || name.contains("kuaishou") || name.contains("gdt")) && isDebugType) { setIgnore(true) } } flavorDimensions("env") sourceSets { publish { java.srcDirs = ['src/main/java', "src/default/java"] } internal { java.srcDirs = ['src/main/java', "src/default/java"] } tea { java.srcDirs = ['src/main/java', 'src/tea/java'] } kuaishou { java.srcDirs = ['src/main/java', 'src/kuaishou/java'] } gdt { java.srcDirs = ['src/main/java', 'src/gdt/java'] } } productFlavors { // internal, 内部测试包使用的 flavor,接口包含包括测试和正式环境 internal { dimension "env" versionNameSuffix "-debug" buildConfigField "String", "DEV_API_HOST", "\"${DEV_API_HOST}\"" buildConfigField "String", "NEW_DEV_API_HOST", "\"${NEW_DEV_API_HOST}\"" buildConfigField "String", "DEV_VAPI_HOST", "\"${DEV_VAPI_HOST}\"" buildConfigField "String", "QUICK_LOGIN_APPID", "\"${DEV_QUICK_LOGIN_APPID}\"" buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${DEV_QUICK_LOGIN_APPKEY}\"" } // publish, 发布时候使用的 flavor,接口仅包含正式环境 publish { dimension "env" buildConfigField "String", "DEV_API_HOST", "\"${API_HOST}\"" buildConfigField "String", "NEW_DEV_API_HOST", "\"${NEW_API_HOST}\"" buildConfigField "String", "DEV_VAPI_HOST", "\"${VAPI_HOST}\"" buildConfigField "String", "QUICK_LOGIN_APPID", "\"${QUICK_LOGIN_APPID}\"" buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${QUICK_LOGIN_APPKEY}\"" } tea { dimension "env" buildConfigField "String", "DEV_API_HOST", "\"${API_HOST}\"" buildConfigField "String", "NEW_DEV_API_HOST", "\"${NEW_API_HOST}\"" buildConfigField "String", "DEV_VAPI_HOST", "\"${VAPI_HOST}\"" buildConfigField "String", "QUICK_LOGIN_APPID", "\"${QUICK_LOGIN_APPID}\"" buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${QUICK_LOGIN_APPKEY}\"" manifestPlaceholders.put("APPLOG_SCHEME", "rangersapplog.byAx6uYt".toLowerCase()) } kuaishou { dimension "env" String KUAI_SHOU_APP_ID = "" String KUAI_SHOU_APP_NAME = "" buildConfigField "String", "DEV_API_HOST", "\"${API_HOST}\"" buildConfigField "String", "NEW_DEV_API_HOST", "\"${NEW_API_HOST}\"" buildConfigField "String", "DEV_VAPI_HOST", "\"${VAPI_HOST}\"" buildConfigField "String", "QUICK_LOGIN_APPID", "\"${QUICK_LOGIN_APPID}\"" buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${QUICK_LOGIN_APPKEY}\"" buildConfigField "String", "KUAI_SHOU_APP_ID", "\"${KUAI_SHOU_APP_ID}\"" buildConfigField "String", "KUAI_SHOU_APP_NAME", "\"${KUAI_SHOU_APP_NAME}\"" } gdt { dimension "env" buildConfigField "String", "DEV_API_HOST", "\"${API_HOST}\"" buildConfigField "String", "NEW_DEV_API_HOST", "\"${NEW_API_HOST}\"" buildConfigField "String", "DEV_VAPI_HOST", "\"${VAPI_HOST}\"" buildConfigField "String", "QUICK_LOGIN_APPID", "\"${QUICK_LOGIN_APPID}\"" buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${QUICK_LOGIN_APPKEY}\"" } } } repositories { flatDir { dirs 'libs', 'libs/aars' } } dependencies { implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs') teaImplementation fileTree(include: ['*.jar', '*.aar'], dir: 'src/tea/libs') kuaishouImplementation fileTree(include: ['*.jar', '*.aar'], dir: 'src/kuaishou/libs') gdtImplementation fileTree(include: ['*.jar', '*.aar'], dir: 'src/gdt/libs') testImplementation 'junit:junit:4.12' debugImplementation "com.squareup.leakcanary:leakcanary-android:${leakcanary}" debugImplementation "com.squareup.leakcanary:leakcanary-android-process:${leakcanary}" // debugImplementation "com.gu.android:toolargetool:${toolargetool}" // 需要使用调试时才启用 debugImplementation "com.github.nichbar:WhatTheStack:${whatTheStack}" // debugImplementation "io.github.didi.dokit:dokitx:${dokit}" implementation "androidx.multidex:multidex:${multiDex}" implementation "androidx.fragment:fragment-ktx:${fragment}" implementation "androidx.annotation:annotation:${annotation}" implementation "androidx.viewpager2:viewpager2:${viewpager2}" kapt "androidx.room:room-compiler:${room}" implementation "com.kyleduo.switchbutton:library:${switchButton}" implementation "com.leon.channel:helper:${apkChannelPackage}" implementation "com.j256.ormlite:ormlite-android:${ormlite}" implementation "com.j256.ormlite:ormlite-core:${ormlite}" implementation "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "com.contrarywind:Android-PickerView:${pickerView}" implementation "net.cachapa.expandablelayout:expandablelayout:${expandableLayout}" // 用于比较 versionName 是大于小于或等于 implementation "com.g00fy2:versioncompare:${versioncompare}" // for video streaming implementation("com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-java:$gsyVideo", { exclude module: "gsyvideoplayer-androidvideocache" exclude group: "tv.danmaku.ijk.media" }) implementation "com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-exo_player2:$gsyVideo" // implementation "androidx.work:work-runtime:${workManager}" implementation "com.llew.huawei:verifier:${verifier}" teaImplementation "com.bytedance.applog:RangersAppLog-Lite-cn:${bytedanceApplog}" teaImplementation "com.bytedance.applog:RangersAppLog-All-convert:${bytedanceApplog}" implementation "net.lingala.zip4j:zip4j:${zip4j}" implementation "com.github.hsiafan:apk-parser:${apkParser}" implementation "org.nanohttpd:nanohttpd:${nanohttpd}" implementation "com.lg:easyfloat:${easyFloat}" implementation "io.github.florent37:shapeofview:${shapeOfView}" implementation "com.lg:apksig:${apksig}" implementation "com.lg:gid:${gid}" implementation "com.lg:shortcut:${shortcut}" kapt "com.alibaba:arouter-compiler:$arouterVersion" implementation project(':ndownload') implementation project(':vspace-bridge:vspace') implementation (project(':module_common')) { exclude group: 'androidx.swiperefreshlayout' } implementation(project(':module_login')) { exclude group: 'androidx.swiperefreshlayout' } implementation(project(':module_setting')) { exclude group: 'androidx.swiperefreshlayout' } // implementation(project(':module_setting_compose')) { // exclude group: 'androidx.swiperefreshlayout' // } implementation(project(':module_core_feature')) { exclude group: 'androidx.swiperefreshlayout' } implementation(project(':module_feedback')) { exclude group: 'androidx.swiperefreshlayout' } implementation(project(':module_sensors_data')) { exclude group: 'androidx.swiperefreshlayout' } // implementation(project(':feature:vpn')) implementation(project(':feature:pkg')) implementation(project(':feature:floating-window')) implementation(project(':feature:beizi_startup_ad')) } File propFile = file('sign.properties') if (propFile.exists()) { Properties props = new Properties() props.load(new FileInputStream(propFile)) if (props.containsKey('keyAlias') && props.containsKey('keyPassword') && props.containsKey('storeFile') && props.containsKey('storePassword')) { android.signingConfigs { // debug 不要使用正式签名,这样tinker才不会打补丁。 debug { keyAlias props.get('keyAlias') keyPassword props.get('keyPassword') storeFile file(props.get('storeFile')) storePassword props.get('storePassword') } release { keyAlias props.get('keyAlias') keyPassword props.get('keyPassword') storeFile file(props.get('storeFile')) storePassword props.get('storePassword') } } } else { android.buildTypes.release.signingConfig = null } } else { android.buildTypes.release.signingConfig = null } // 用于测试读取 META-INF 里的文件 //task generateMetaJson { // def resDir = new File(buildDir, 'generated/FILES_FOR_META_INF/') // def destDir = new File(resDir, 'META-INF/') // // Add resDir as a resource directory so that it is automatically included in the APK. // android { // sourceSets { // main.resources { // srcDir resDir // } // } // } // // doLast { // if (!destDir.exists()) destDir.mkdirs() // copy { // into destDir // from new File('generated/FILES_FOR_META_INF/META-INF/halo_skip.json') // } // } //} //// Specify when put_files_in_META_INF should run //project.afterEvaluate { // tasks.findAll { task -> // task.name.startsWith('merge') && task.name.endsWith('Resources') // }.each { t -> t.dependsOn generateMetaJson } //} andResGuard { mappingFile = null use7zip = true useSign = true // 打开这个开关,会keep住所有资源的原始路径,只混淆资源的名字 keepRoot = false // 设置这个值,会把arsc name列混淆成相同的名字,减少string常量池的大小 fixedResName = "arg" // 打开这个开关会合并所有哈希值相同的资源,但请不要过度依赖这个功能去除去冗余资源 mergeDuplicatedRes = true whiteList = [ "R.drawable.icon", "R.drawable.ic_bar_back", "R.drawable.toolbar_search_icon", "R.drawable.bg_notification_answer_style_1", "R.drawable.bg_notification_answer_style_2", "R.drawable.bg_notification_article_style_1", "R.drawable.bg_notification_article_style_2", "R.drawable.bg_notification_feedback_style_1", "R.drawable.bg_notification_feedback_style_2", "R.drawable.bg_notification_gift_style_1", "R.drawable.bg_notification_gift_style_2", "R.drawable.bg_notification_login_style_1", "R.drawable.bg_notification_login_style_2", "R.drawable.bg_notification_question_style_1", "R.drawable.bg_notification_question_style_2", "R.drawable.bg_notification_rating_style_1", "R.drawable.bg_notification_rating_style_2", "R.drawable.bg_notification_reserve_game_style_1", "R.drawable.bg_notification_reserve_game_style_2", "R.drawable.bg_notification_video_style_1", "R.drawable.bg_notification_video_style_2", "R.drawable.ic_recommend_activity", "R.drawable.ic_recommend_discount", "R.drawable.ic_recommend_function", "R.drawable.ic_recommend_gift", "R.drawable.ic_recommend_role", "R.drawable.download_button_normal_style", "R.drawable.ic_selector_selected", "R.drawable.ic_selector_default", "R.id.download_speed", "R.id.download_percentage", "R.id.comment", "R.id.vote", "R.id.watermark_hint", "R.id.watermark_sb", "R.id.bottomShareIv", "R.id.bottomShareTv", "R.id.recommendStarPref", "R.id.recommendStar", "R.id.iv_vmode_badge", "R.id.tv_vmode", "R.id.iv_vmode", "R.drawable.help_search_delete", "R.drawable.suggest_type_normal", "R.drawable.suggest_type_crash", "R.drawable.suggest_type_game_question", "R.drawable.suggest_type_game_collect", "R.drawable.suggest_type_function_suggest", "R.drawable.suggest_type_article_collect", "R.drawable.suggest_type_copyright", "R.drawable.news_comment_detail_read", "R.drawable.news_comment_detail_comment", "R.drawable.news_comment_detail_share", "R.drawable.ic_libao", "R.drawable.ic_link", "R.drawable.concern_message_icon", "R.drawable.reuse_blank_hint", "R.drawable.ic_concern", "R.drawable.concern_down", "R.drawable.concern_up", "R.drawable.ic_libao_more", "R.drawable.ic_libao_delete", "R.drawable.ic_dialog_close", "R.drawable.occupy2", "R.drawable.kc_checkbox_unselect", "R.drawable.kc_checkbox_select", "R.drawable.ic_type_unselect", "R.drawable.ic_type_selected", "R.drawable.suggest_add_pic_icon", "R.drawable.icon_pic_add", "R.drawable.ask_search_input_delete", "R.drawable.suggest_pic_delete", "R.id.cardIv", "R.id.cardMask", "R.id.cardGradientMask", "R.id.gameIconIv", "R.id.titleContainer" ] compressFilePattern = [ "*.png", "*.jpg", "*.jpeg", "*.gif", ] sevenzip { artifact = 'io.github.leon406:SevenZip:1.2.22.5' } } project.afterEvaluate { def variants = null try { variants = android.applicationVariants } catch (Throwable t) { t.printStackTrace() try { variants = android.libraryVariants } catch (Throwable tt) { tt.printStackTrace() } } if (variants != null) { variants.all { variant -> variant.outputs.each { output -> def task = output.processManifestProvider.get() if (task == null) { return } /** * 为 Manifest 的 Activity 的 configChanges 添加自己手动处理 configurationChanges 配置 [https://developer.android.com/guide/topics/resources/runtime-changes] * AGP 4.1.0 从 ProcessManifest task 里拿 manifest 的 API 变更调整可以参考这里 [https://github.com/Tencent/tinker/pull/1476/commits/d71645729b13d545ca4ba6826f93fbf558751434] * (搞半天还是不会抽离方法,有空再把 gradle 改成用 kotlin 实现吧) */ task.doLast { def manifestFile = new File(multiApkManifestOutputDirectory.get().asFile, "AndroidManifest.xml") if (manifestFile == null || !manifestFile.exists()) { return } String[] configChanges = [ "density", "fontScale", "keyboard", "keyboardHidden", "layoutDirection", "locale", "mcc", "mnc", "navigation", "orientation", "screenLayout", "screenSize", "smallestScreenSize", "touchscreen", "uiMode"] def parser = new XmlSlurper(false, true) def manifest = parser.parse(manifestFile) def app = manifest.'application'[0] app.'activity'.each { act -> String value = act.attributes()['android:configChanges'] if (value == null || value.isEmpty()) { if (value == null) value = "" configChanges.eachWithIndex { config, index -> if (index != configChanges.length - 1) { value += config + "|" } else { value += config } } act.attributes()['androidconfigChanges'] = value } else { String[] valueSplit = value.split("\\|") println configChanges configChanges.eachWithIndex { config, index -> if (!valueSplit.contains(config)) { value += ("|" + config) } } act.attributes()['android:configChanges'] = value } } def tmpManifest = XmlUtil.serialize(manifest).replaceAll("androidconfigChanges", "android:configChanges") manifest = parser.parseText(tmpManifest) manifestFile.setText(XmlUtil.serialize(manifest), "utf-8") } } } } }