const { withDangerousMod } = require('@expo/config-plugins'); const fs = require('fs'); const path = require('path'); // OPPO 厂商推送通道的 Expo config plugin. // // 客户端职责(依据极光官方文档 https://docs.jiguang.cn/jpush/client/Android/android_3rd_guide): // 1) 添加 cn.jiguang.sdk.plugin:oppo 依赖(5.9.0+ 自动包含 heytap 官方 aar) // 2) 注入 manifestPlaceholders: OPPO_APPKEY / OPPO_APPID / OPPO_APPSECRET(带 OP- 前缀) // 3) 添加 Proguard 规则 (coloros.mcsdk / heytap / mcs 三个包保留) // // 注意事项: // - 极光 JPUSH_CHANNEL 是「APK 分发渠道」统计字段,不是厂商通道开关,**不应覆盖**。 // - OPPO 与小米/vivo/荣耀不同:客户端需要 3 个占位符(含 AppSecret),且必须带 `OP-` 前缀。 // - OPPO AAR 在 JPush 5.9.0+ 通过 maven 自动拉取,不需要手动下载 aar 文件。 // - OPPO SDK 3.1.0+ 依赖 gson 2.6.2 和 androidx.annotation 1.1.0,5.9.0 maven 已自动包含。 // // 实现说明:用 withDangerousMod 直接读写磁盘文件。 // 原因:Expo SDK 56+ 的 withAppBuildGradle hook 链中,第二个以后的 plugin 写回 // modResults.contents 会被静默忽略,导致多个 vendor plugin 只能写入第一个的修改。 const withOppoPush = (config, options = {}) => { const { jpushVersion = '6.1.0', appId = '37219099', appKey = '4eb532fcddd147c0bf7c356a149b2058', appSecret = '9a0c290b2b8049e1893246ed7b9367d0', } = options; config = withDangerousMod(config, [ 'android', async (config) => { const appBuildGradlePath = path.join( config.modRequest.platformProjectRoot, 'app', 'build.gradle' ); const proguardFilePath = path.join( config.modRequest.platformProjectRoot, 'app', 'proguard-rules.pro' ); // --- 1. 处理 build.gradle --- if (fs.existsSync(appBuildGradlePath)) { let gradle = fs.readFileSync(appBuildGradlePath, 'utf-8'); let changed = false; // 1a. 注入 AAR 依赖 if (!gradle.includes('cn.jiguang.sdk.plugin:oppo')) { const depsPattern = /dependencies\s*\{/; if (depsPattern.test(gradle)) { gradle = gradle.replace( depsPattern, `dependencies {\n implementation 'cn.jiguang.sdk.plugin:oppo:${jpushVersion}'` ); changed = true; } } // 1b. 注入 manifestPlaceholders(带 OP- 前缀) const manifestPlaceholderRegex = /manifestPlaceholders\s*=\s*\[([\s\S]*?)\]/; const match = gradle.match(manifestPlaceholderRegex); // OPPO 官方要求占位符值必须以 "OP-" 前缀开头 const requiredPlaceholders = { OPPO_APPKEY: `OP-${appKey}`, OPPO_APPID: `OP-${appId}`, OPPO_APPSECRET: `OP-${appSecret}`, }; if (match) { const existingBlock = match[1]; const existingEntries = {}; const entryRegex = /(\w+)\s*:\s*"([^"]*)"/g; let entryMatch; while ((entryMatch = entryRegex.exec(existingBlock)) !== null) { existingEntries[entryMatch[1]] = entryMatch[2]; } let placeholdersChanged = false; for (const [key, value] of Object.entries(requiredPlaceholders)) { if (existingEntries[key] !== value) { existingEntries[key] = value; placeholdersChanged = true; } } if (placeholdersChanged) { const entriesStr = Object.entries(existingEntries) .map(([k, v]) => `${k}: "${v}"`) .join(',\n '); const newBlock = `\n manifestPlaceholders = [\n ${entriesStr}\n ]`; gradle = gradle.replace(manifestPlaceholderRegex, newBlock.trim()); changed = true; } } else if (gradle.includes('REACT_NATIVE_RELEASE_LEVEL')) { // 没有 manifestPlaceholders block 时新建 const entriesStr = Object.entries(requiredPlaceholders) .map(([k, v]) => `${k}: "${v}"`) .join(',\n '); const block = `\n manifestPlaceholders = [\n ${entriesStr}\n ]\n`; const lines = gradle.split('\n'); for (let i = 0; i < lines.length; i++) { if (lines[i].includes('REACT_NATIVE_RELEASE_LEVEL')) { lines.splice(i + 1, 0, block); break; } } gradle = lines.join('\n'); changed = true; } if (changed) { fs.writeFileSync(appBuildGradlePath, gradle); console.log('[withOppoPush] updated app/build.gradle (AAR + manifestPlaceholders)'); } } // --- 2. 处理 proguard-rules.pro --- if (fs.existsSync(proguardFilePath)) { let proguard = fs.readFileSync(proguardFilePath, 'utf-8'); if (!proguard.includes('-keep class com.heytap.**')) { const oppoRules = ` # JPush OPPO vendor channel — keep rules (https://docs.jiguang.cn/jpush/client/Android/android_3rd_guide) -dontwarn com.coloros.mcsdk.** -dontwarn com.heytap.** -dontwarn com.mcs.** -keep class com.coloros.mcsdk.** { *; } -keep class com.heytap.** { *; } -keep class com.mcs.** { *; } `; fs.writeFileSync(proguardFilePath, proguard + oppoRules); console.log('[withOppoPush] appended OPPO Proguard rules to proguard-rules.pro'); } } return config; }, ]); return config; }; module.exports = withOppoPush;