Replace the custom WebRTC implementation with LiveKit for improved stability and feature support. - Remove `react-native-webrtc` and custom `WebRTCManager` - Implement `LiveKitService` for room and track management - Update `callStore` to handle LiveKit events and connection states - Refactor `CallScreen` (mobile and web) to use `@livekit/react-native` and `VideoView` - Update WebSocket protocol to use `call_ready` instead of manual SDP/ICE exchanges - Add necessary camera and microphone permissions to `app.json` - Update Metro and Babel configurations for LiveKit compatibility
202 lines
7.8 KiB
JavaScript
202 lines
7.8 KiB
JavaScript
const {
|
|
withAppBuildGradle,
|
|
withProjectBuildGradle,
|
|
withSettingsGradle,
|
|
withDangerousMod,
|
|
withAndroidManifest,
|
|
} = require('@expo/config-plugins');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const withHuaweiPush = (config, options = {}) => {
|
|
const { jpushVersion = '5.8.0' } = options;
|
|
|
|
config = withSettingsGradle(config, (config) => {
|
|
const contents = config.modResults.contents;
|
|
|
|
// Add Huawei Maven repo to pluginManagement.repositories for plugin resolution
|
|
if (!contents.includes('developer.huawei.com/repo')) {
|
|
if (contents.includes('pluginManagement')) {
|
|
config.modResults.contents = contents.replace(
|
|
/(pluginManagement\s*\{[\s\S]*?repositories\s*\{)/,
|
|
`$1\n maven { url 'https://developer.huawei.com/repo/' }`
|
|
);
|
|
} else {
|
|
// No pluginManagement block at all — create one with the Huawei repo
|
|
config.modResults.contents =
|
|
`pluginManagement {\n repositories {\n maven { url 'https://developer.huawei.com/repo/' }\n }\n}\n\n` +
|
|
config.modResults.contents;
|
|
}
|
|
}
|
|
|
|
// Map the plugin ID to its Maven artifact via resolutionStrategy.
|
|
// AGConnect is not published as a standard Gradle plugin marker, so Gradle 9+
|
|
// cannot resolve it by plugin ID alone. This mapping tells Gradle where to find it.
|
|
if (!config.modResults.contents.includes('com.huawei.agconnect:agcp')) {
|
|
const pmMatch = config.modResults.contents.match(/pluginManagement\s*\{/);
|
|
if (pmMatch) {
|
|
config.modResults.contents = config.modResults.contents.replace(
|
|
/(pluginManagement\s*\{)/,
|
|
`$1\n resolutionStrategy {\n eachPlugin {\n if (requested.id.id == 'com.huawei.agconnect') {\n useModule('com.huawei.agconnect:agcp:1.9.1.301')\n }\n }\n }`
|
|
);
|
|
}
|
|
}
|
|
|
|
// Ensure Huawei Maven repo also appears in dependencyResolutionManagement (Gradle 9+)
|
|
if (!config.modResults.contents.match(/dependencyResolutionManagement[\s\S]*developer\.huawei\.com\/repo/)) {
|
|
const depMgmtPattern = /dependencyResolutionManagement\s*\{[\s\S]*?repositories\s*\{/;
|
|
if (depMgmtPattern.test(config.modResults.contents)) {
|
|
config.modResults.contents = config.modResults.contents.replace(
|
|
depMgmtPattern,
|
|
`$&\n maven { url 'https://developer.huawei.com/repo/' }`
|
|
);
|
|
}
|
|
}
|
|
|
|
// Declare the plugin in a top-level plugins {} block (outside pluginManagement).
|
|
// In Gradle 9+, plugins declared with `apply false` in settings.gradle are
|
|
// available to all subprojects via `apply plugin:` or the plugins DSL.
|
|
if (!config.modResults.contents.includes("id 'com.huawei.agconnect'")) {
|
|
const pluginsBlockPattern = /^plugins\s*\{/m;
|
|
if (pluginsBlockPattern.test(config.modResults.contents)) {
|
|
// Add to existing top-level plugins block
|
|
config.modResults.contents = config.modResults.contents.replace(
|
|
/^plugins\s*\{/m,
|
|
`plugins {\n id 'com.huawei.agconnect' version '1.9.1.301' apply false`
|
|
);
|
|
} else {
|
|
// Insert a top-level plugins block after pluginManagement closing brace,
|
|
// or at the very beginning if no pluginManagement block exists.
|
|
const pmClosePattern = /\n\}\n/;
|
|
if (pmClosePattern.test(config.modResults.contents)) {
|
|
const firstClosing = config.modResults.contents.indexOf('}', config.modResults.contents.indexOf('pluginManagement')) + 1;
|
|
if (firstClosing > 0) {
|
|
const insertPos = firstClosing;
|
|
config.modResults.contents =
|
|
config.modResults.contents.slice(0, insertPos) +
|
|
`\n\nplugins {\n id 'com.huawei.agconnect' version '1.9.1.301' apply false\n}` +
|
|
config.modResults.contents.slice(insertPos);
|
|
}
|
|
} else {
|
|
// Fallback: prepend after pluginManagement block
|
|
config.modResults.contents =
|
|
config.modResults.contents.replace(
|
|
/(pluginManagement\s*\{[\s\S]*?\})/,
|
|
`$1\n\nplugins {\n id 'com.huawei.agconnect' version '1.9.1.301' apply false\n}`
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return config;
|
|
});
|
|
|
|
config = withProjectBuildGradle(config, (config) => {
|
|
const contents = config.modResults.contents;
|
|
|
|
if (!contents.includes('developer.huawei.com/repo')) {
|
|
config.modResults.contents = contents.replace(
|
|
/(repositories\s*\{)/g,
|
|
(match) => {
|
|
return match + `\n maven { url 'https://developer.huawei.com/repo/' }`;
|
|
}
|
|
);
|
|
}
|
|
|
|
return config;
|
|
});
|
|
|
|
config = withProjectBuildGradle(config, (config) => {
|
|
const contents = config.modResults.contents;
|
|
|
|
if (!contents.includes('com.huawei.agconnect:agcp')) {
|
|
if (contents.includes('buildscript')) {
|
|
const depsPattern = /(buildscript\s*\{[\s\S]*?dependencies\s*\{)/;
|
|
if (depsPattern.test(contents)) {
|
|
config.modResults.contents = contents.replace(
|
|
depsPattern,
|
|
`$1\n classpath 'com.huawei.agconnect:agcp:1.9.1.301'`
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!config.modResults.contents.includes('developer.huawei.com/repo')) {
|
|
const bsRepoPattern = /(buildscript\s*\{[\s\S]*?repositories\s*\{)/;
|
|
if (bsRepoPattern.test(config.modResults.contents)) {
|
|
config.modResults.contents = config.modResults.contents.replace(
|
|
bsRepoPattern,
|
|
`$1\n maven { url 'https://developer.huawei.com/repo/' }`
|
|
);
|
|
}
|
|
}
|
|
|
|
return config;
|
|
});
|
|
|
|
config = withAppBuildGradle(config, (config) => {
|
|
const contents = config.modResults.contents;
|
|
|
|
if (!contents.includes('com.huawei.agconnect')) {
|
|
const applyPluginPattern = /apply plugin:\s*['"]com\.google\.gms\.google-services['"]/;
|
|
if (applyPluginPattern.test(contents)) {
|
|
config.modResults.contents = contents.replace(
|
|
applyPluginPattern,
|
|
`apply plugin: 'com.google.gms.google-services'\napply plugin: 'com.huawei.agconnect'`
|
|
);
|
|
} else {
|
|
const lastApplyPlugin = /apply plugin:[^\n]*(?:\n)/g;
|
|
const matches = contents.match(lastApplyPlugin);
|
|
if (matches && matches.length > 0) {
|
|
const lastMatch = matches[matches.length - 1];
|
|
const lastIndex = contents.lastIndexOf(lastMatch);
|
|
config.modResults.contents =
|
|
contents.slice(0, lastIndex + lastMatch.length) +
|
|
"apply plugin: 'com.huawei.agconnect'\n" +
|
|
contents.slice(lastIndex + lastMatch.length);
|
|
} else {
|
|
config.modResults.contents = contents + "\napply plugin: 'com.huawei.agconnect'\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!contents.includes('cn.jiguang.sdk.plugin:huawei')) {
|
|
const depsPattern = /dependencies\s*\{/;
|
|
if (depsPattern.test(config.modResults.contents)) {
|
|
config.modResults.contents = config.modResults.contents.replace(
|
|
depsPattern,
|
|
`dependencies {\n implementation 'cn.jiguang.sdk.plugin:huawei:${jpushVersion}'`
|
|
);
|
|
}
|
|
}
|
|
|
|
return config;
|
|
});
|
|
|
|
config = withDangerousMod(config, [
|
|
'android',
|
|
async (config) => {
|
|
const appDir = path.join(config.modRequest.platformProjectRoot, 'app');
|
|
const srcFile = path.join(config.modRequest.projectRoot, 'plugins', 'agconnect-services.json');
|
|
const destFile = path.join(appDir, 'agconnect-services.json');
|
|
|
|
if (fs.existsSync(srcFile)) {
|
|
fs.copyFileSync(srcFile, destFile);
|
|
console.log('[withHuaweiPush] copied agconnect-services.json to android/app/');
|
|
} else {
|
|
console.warn('[withHuaweiPush] agconnect-services.json not found at project root');
|
|
}
|
|
|
|
return config;
|
|
},
|
|
]);
|
|
|
|
config = withAndroidManifest(config, (config) => {
|
|
return config;
|
|
});
|
|
|
|
return config;
|
|
};
|
|
|
|
module.exports = withHuaweiPush;
|