codeplay-common 4.0.4 → 4.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,6 +11,20 @@ Make it to ionic project
11
11
  Donate to get full code including many common functions
12
12
  https://ko-fi.com/codeplay
13
13
 
14
+
15
+ Version 4.0.6
16
+ IAP related some automatic variable creation and validation
17
+ Fetch from vite.config.json file
18
+ 'import.meta.env.VITE_APP_UNIQUE_ID': JSON.stringify(APP_UNIQUE_ID),
19
+ 'import.meta.env.VITE_ANDROID_ANALYTICS_ENABLED': JSON.stringify(ANDROID_ANALYTICS_ENABLED),
20
+ 'import.meta.env.VITE_IOS_ANALYTICS_ENABLED': JSON.stringify(IOS_ANALYTICS_ENABLED),
21
+ 'import.meta.env.VITE_IAP_DESIGN': JSON.stringify(IAP_DESIGN),
22
+
23
+
24
+ Version 4.0.5
25
+ In "files\buildCodeplay\packageidBaseModification-1.4.js" SCHEDULE_EXACT_ALARM permission added for the app unique id 2 (resume generator app)
26
+
27
+
14
28
  Version 4.0.4
15
29
  patchFile(); method is disabled, Due to "@capacitor/android:8.4.2" this update. I think it is realted to safearea issue fixed in this latest version, file in "SystemBars.java"
16
30
 
@@ -272,16 +272,68 @@ function compareWithBeta(installedVersion, minVersion, isBeta) {
272
272
 
273
273
 
274
274
 
275
- const checkAppUniqueId=()=>{
276
-
277
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
278
-
279
- const appUniqueId = config.android?.APP_UNIQUE_ID;
280
- const RESIZEABLE_ACTIVITY = config.android?.RESIZEABLE_ACTIVITY;
281
- const orientation = config.android?.ORIENTATION;
282
-
283
-
284
- let logErrorMessage="";
275
+ const ensureAnalyticsFlagsInConfig = (config) => {
276
+ const analyticsFlagKey = 'ANALYTICS_ENABLED';
277
+ const legacyAnalyticsFlagKey = ['ANALY', 'STICS_ENABLED'].join('');
278
+ let updated = false;
279
+
280
+ if (!config.android) {
281
+ config.android = {};
282
+ updated = true;
283
+ }
284
+
285
+ if (!config.ios) {
286
+ config.ios = {};
287
+ updated = true;
288
+ }
289
+
290
+ const syncPlatformFlag = (platformName, platformConfig) => {
291
+ const existingValue = platformConfig[analyticsFlagKey];
292
+ const legacyValue = platformConfig[legacyAnalyticsFlagKey];
293
+ let nextValue = false;
294
+
295
+ if (typeof existingValue === 'boolean') {
296
+ nextValue = existingValue;
297
+ } else if (typeof legacyValue === 'boolean') {
298
+ nextValue = legacyValue;
299
+ } else if (existingValue !== undefined) {
300
+ console.warn(`⚠️ ${platformName}.ANALYTICS_ENABLED should be true or false. Automatically setting it to false.`);
301
+ }
302
+
303
+ if (platformConfig[analyticsFlagKey] !== nextValue) {
304
+ platformConfig[analyticsFlagKey] = nextValue;
305
+ updated = true;
306
+ console.warn(`⚠️ ${platformName}.ANALYTICS_ENABLED added/updated as ${nextValue}.`);
307
+ }
308
+
309
+ if (legacyAnalyticsFlagKey in platformConfig) {
310
+ delete platformConfig[legacyAnalyticsFlagKey];
311
+ updated = true;
312
+ console.warn(`⚠️ ${platformName}.ANALYTICS_ENABLED spelling corrected.`);
313
+ }
314
+
315
+ if (nextValue === false) {
316
+ console.warn(`⚠️ Google Analytics is disabled for ${platformName}. Mostly recommended to enable it when IAP is available.`);
317
+ }
318
+ };
319
+
320
+ syncPlatformFlag('android', config.android);
321
+ syncPlatformFlag('ios', config.ios);
322
+
323
+ return updated;
324
+ };
325
+
326
+ const checkAppUniqueId=()=>{
327
+
328
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
329
+ const analyticsFlagsUpdated = ensureAnalyticsFlagsInConfig(config);
330
+
331
+ const appUniqueId = config.android?.APP_UNIQUE_ID;
332
+ const RESIZEABLE_ACTIVITY = config.android?.RESIZEABLE_ACTIVITY;
333
+ const orientation = config.android?.ORIENTATION;
334
+
335
+
336
+ let logErrorMessage="";
285
337
 
286
338
  // 1️⃣ Check if it’s missing
287
339
  if (RESIZEABLE_ACTIVITY === undefined) {
@@ -309,22 +361,30 @@ const checkAppUniqueId=()=>{
309
361
  logErrorMessage+='❌ APP_UNIQUE_ID is missing in capacitor.config.json.';
310
362
  }
311
363
 
312
- else if (!Number.isInteger(appUniqueId)) {
313
- logErrorMessage+='❌ APP_UNIQUE_ID must be an integer. Example: 1, 2, 3, etc.';
314
- }
315
-
316
-
317
-
318
- if(logErrorMessage!="")
319
- {
320
- console.error(logErrorMessage);
321
- process.exit(1)
322
- }
323
-
324
-
325
- console.log(`✅ APP_UNIQUE_ID is valid: ${appUniqueId}`);
326
-
327
- }
364
+ else if (!Number.isInteger(appUniqueId)) {
365
+ logErrorMessage+='❌ APP_UNIQUE_ID must be an integer. Example: 1, 2, 3, etc.';
366
+ }
367
+
368
+ if(logErrorMessage!="")
369
+ {
370
+ if (analyticsFlagsUpdated) {
371
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
372
+ console.log('✅ capacitor.config.json analytics flags synced.');
373
+ }
374
+
375
+ console.error(logErrorMessage);
376
+ process.exit(1)
377
+ }
378
+
379
+ if (analyticsFlagsUpdated) {
380
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
381
+ console.log('✅ capacitor.config.json analytics flags synced.');
382
+ }
383
+
384
+
385
+ console.log(`✅ APP_UNIQUE_ID is valid: ${appUniqueId}`);
386
+
387
+ }
328
388
 
329
389
  checkAppUniqueId();
330
390
 
@@ -2456,7 +2516,7 @@ const checkAndupdateDropInViteConfig = () => {
2456
2516
  console.log("✅ vite.config.(m)js Updated successfully.");
2457
2517
  };
2458
2518
 
2459
- const syncAdmobAliasInViteConfig = () => {
2519
+ const syncAdmobAliasInViteConfig = () => {
2460
2520
  const possibleFiles = [
2461
2521
  "vite.config.mjs",
2462
2522
  "vite.config.js"
@@ -2517,15 +2577,607 @@ const syncAdmobAliasInViteConfig = () => {
2517
2577
  viteContent = viteContent.replace(aliasLineRegex, nextGenAliasLine);
2518
2578
  fs.writeFileSync(viteConfigPath, viteContent, "utf8");
2519
2579
  console.log(`✅ @admob alias updated to ${nextGenRelativePath}`);
2520
- };
2521
-
2522
-
2523
-
2524
-
2525
-
2526
-
2527
-
2528
-
2580
+ };
2581
+
2582
+
2583
+ const getViteConfigPath = () => {
2584
+ const possibleFiles = [
2585
+ "vite.config.mjs",
2586
+ "vite.config.js"
2587
+ ];
2588
+
2589
+ return possibleFiles
2590
+ .map(file => path.join(process.cwd(), file))
2591
+ .find(filePath => fs.existsSync(filePath));
2592
+ };
2593
+
2594
+ const getLatestIapDir = () => {
2595
+ const adsDir = path.join(process.cwd(), "src", "js", "Ads");
2596
+
2597
+ if (!fs.existsSync(adsDir)) return null;
2598
+
2599
+ const iapDirs = fs.readdirSync(adsDir, { withFileTypes: true })
2600
+ .filter(entry => entry.isDirectory() && /^IAP-\d+(\.\d+)*$/.test(entry.name))
2601
+ .map(entry => entry.name)
2602
+ .sort((a, b) => {
2603
+ const versionA = a.match(/(\d+(?:\.\d+)*)/)[1].split(".").map(Number);
2604
+ const versionB = b.match(/(\d+(?:\.\d+)*)/)[1].split(".").map(Number);
2605
+
2606
+ for (let i = 0; i < Math.max(versionA.length, versionB.length); i++) {
2607
+ const diff = (versionB[i] || 0) - (versionA[i] || 0);
2608
+ if (diff !== 0) return diff;
2609
+ }
2610
+
2611
+ return 0;
2612
+ });
2613
+
2614
+ return iapDirs.length > 0 ? path.join(adsDir, iapDirs[0]) : null;
2615
+ };
2616
+
2617
+ const getAnalyticsConfig = () => {
2618
+ const config = fs.existsSync(configPath)
2619
+ ? JSON.parse(fs.readFileSync(configPath, "utf8"))
2620
+ : {};
2621
+
2622
+ const analyticsFlagKey = "ANALYTICS_ENABLED";
2623
+ const androidEnabled = config.android?.[analyticsFlagKey] ?? false;
2624
+ const iosEnabled = config.ios?.[analyticsFlagKey] ?? false;
2625
+
2626
+ return {
2627
+ androidEnabled: androidEnabled === true,
2628
+ iosEnabled: iosEnabled === true,
2629
+ enabled: androidEnabled === true || iosEnabled === true
2630
+ };
2631
+ };
2632
+
2633
+ const isPackageAvailable = (packageName) => {
2634
+ if (!fs.existsSync(packageJsonPath)) return false;
2635
+
2636
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
2637
+
2638
+ return Boolean(
2639
+ packageJson.dependencies?.[packageName] ||
2640
+ packageJson.devDependencies?.[packageName] ||
2641
+ fs.existsSync(path.join(process.cwd(), "node_modules", packageName, "package.json"))
2642
+ );
2643
+ };
2644
+
2645
+ const sourceImportsPackage = (packageName, ignoredFilePath) => {
2646
+ const sourceDir = path.join(process.cwd(), "src");
2647
+ if (!fs.existsSync(sourceDir)) return false;
2648
+
2649
+ const escapedPackage = packageName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2650
+ const importRegex = new RegExp(`(?:from\\s*['"]${escapedPackage}(?:/[^'"]*)?['"]|import\\s*['"]${escapedPackage}(?:/[^'"]*)?['"]|import\\s*\\(\\s*['"]${escapedPackage}(?:/[^'"]*)?['"]\\s*\\)|require\\s*\\(\\s*['"]${escapedPackage}(?:/[^'"]*)?['"]\\s*\\))`);
2651
+
2652
+ const scan = (dir) => {
2653
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
2654
+ const fullPath = path.join(dir, entry.name);
2655
+
2656
+ if (ignoredFilePath && path.resolve(fullPath) === path.resolve(ignoredFilePath)) {
2657
+ continue;
2658
+ }
2659
+
2660
+ if (entry.isDirectory()) {
2661
+ if (scan(fullPath)) return true;
2662
+ continue;
2663
+ }
2664
+
2665
+ if (!/\.(js|mjs|cjs|ts|tsx|jsx|f7)$/.test(entry.name)) continue;
2666
+
2667
+ const content = fs.readFileSync(fullPath, "utf8");
2668
+ if (importRegex.test(content)) return true;
2669
+ }
2670
+
2671
+ return false;
2672
+ };
2673
+
2674
+ return scan(sourceDir);
2675
+ };
2676
+
2677
+ const runNpmCommand = (command) => {
2678
+ console.log(`🚀 ${command}`);
2679
+ execSync(command, { stdio: "inherit" });
2680
+ };
2681
+
2682
+ const ensureFirebaseAnalyticsWrapper = (analyticsEnabled) => {
2683
+ const analyticsDir = getLatestIapDir();
2684
+ const legacyAnalyticsDir = path.join(process.cwd(), "src", "js", "analytics");
2685
+ const legacyAnalyticsFile = path.join(legacyAnalyticsDir, "firebase-analytics.js");
2686
+
2687
+ if (!analyticsDir) {
2688
+ console.warn("⚠️ No IAP-x.x folder found. Skipping Firebase analytics wrapper sync.");
2689
+ return null;
2690
+ }
2691
+
2692
+ const analyticsFile = path.join(analyticsDir, "firebase-analytics.js");
2693
+
2694
+ if (!fs.existsSync(analyticsDir)) {
2695
+ fs.mkdirSync(analyticsDir, { recursive: true });
2696
+ }
2697
+
2698
+ const activeWrapper = `import { Capacitor, registerPlugin } from '@capacitor/core';
2699
+
2700
+ const FirebaseAnalytics = registerPlugin('FirebaseAnalytics');
2701
+
2702
+ const isEnabledValue = (value) => value === true || value === 'true';
2703
+
2704
+ const canUseNativeAnalytics = () => {
2705
+ if (!Capacitor.isNativePlatform()) return false;
2706
+
2707
+ const platform = Capacitor.getPlatform();
2708
+
2709
+ if (platform === 'android') {
2710
+ return isEnabledValue(import.meta.env.VITE_ANDROID_ANALYTICS_ENABLED);
2711
+ }
2712
+
2713
+ if (platform === 'ios') {
2714
+ return isEnabledValue(import.meta.env.VITE_IOS_ANALYTICS_ENABLED);
2715
+ }
2716
+
2717
+ return false;
2718
+ };
2719
+
2720
+ const cleanParams = (params = {}) => {
2721
+ return Object.entries(params).reduce((result, [key, value]) => {
2722
+ if (value === undefined || value === null || value === '') return result;
2723
+ if (typeof value === 'number' && Number.isNaN(value)) return result;
2724
+
2725
+ result[key] = value;
2726
+ return result;
2727
+ }, {});
2728
+ };
2729
+
2730
+ const trackEvent = async (name, params = {}) => {
2731
+ if (!canUseNativeAnalytics()) return;
2732
+
2733
+ try {
2734
+ await FirebaseAnalytics.logEvent({
2735
+ name,
2736
+ params: cleanParams(params),
2737
+ });
2738
+ } catch (err) {
2739
+ console.warn(\`Firebase analytics event failed: \${name}\`, err);
2740
+ }
2741
+ };
2742
+
2743
+ export const trackSubscriptionScreenViewed = (params) => {
2744
+ trackEvent('subscription_screen_view', params);
2745
+ };
2746
+
2747
+ export const trackSubscriptionCheckoutStarted = (params) => {
2748
+ trackEvent('subscription_checkout_start', params);
2749
+ };
2750
+
2751
+ export const trackSubscriptionPurchased = (params) => {
2752
+ trackEvent('subscription_purchase', params);
2753
+ };
2754
+
2755
+ export const trackConsumablePurchased = (params) => {
2756
+ trackEvent('consumable_purchase', params);
2757
+ };
2758
+ `;
2759
+
2760
+ const noopWrapper = `const trackEvent = () => {};
2761
+
2762
+ export const trackSubscriptionScreenViewed = trackEvent;
2763
+ export const trackSubscriptionCheckoutStarted = trackEvent;
2764
+ export const trackSubscriptionPurchased = trackEvent;
2765
+ export const trackConsumablePurchased = trackEvent;
2766
+ `;
2767
+
2768
+ const nextContent = analyticsEnabled ? activeWrapper : noopWrapper;
2769
+ const currentContent = fs.existsSync(analyticsFile) ? fs.readFileSync(analyticsFile, "utf8") : "";
2770
+
2771
+ if (currentContent !== nextContent) {
2772
+ fs.writeFileSync(analyticsFile, nextContent, "utf8");
2773
+ console.log(`✅ Firebase analytics wrapper ${analyticsEnabled ? "enabled" : "disabled"}: ${path.relative(process.cwd(), analyticsFile)}`);
2774
+ } else {
2775
+ console.log(`ℹ️ Firebase analytics wrapper already ${analyticsEnabled ? "enabled" : "disabled"}.`);
2776
+ }
2777
+
2778
+ if (fs.existsSync(legacyAnalyticsFile)) {
2779
+ fs.unlinkSync(legacyAnalyticsFile);
2780
+ console.log("✅ Removed old Firebase analytics wrapper: src/js/analytics/firebase-analytics.js");
2781
+ }
2782
+
2783
+ if (fs.existsSync(legacyAnalyticsDir) && fs.readdirSync(legacyAnalyticsDir).length === 0) {
2784
+ fs.rmdirSync(legacyAnalyticsDir);
2785
+ }
2786
+
2787
+ return analyticsFile;
2788
+ };
2789
+
2790
+ const syncFirebaseAnalyticsPackages = () => {
2791
+ const analyticsConfig = getAnalyticsConfig();
2792
+
2793
+ if (analyticsConfig.enabled) {
2794
+ if (!isPackageAvailable("@capacitor-firebase/analytics") || !isPackageAvailable("firebase")) {
2795
+ runNpmCommand("npm install @capacitor-firebase/analytics firebase");
2796
+ } else {
2797
+ console.log("ℹ️ Firebase Analytics packages already installed.");
2798
+ }
2799
+
2800
+ ensureFirebaseAnalyticsWrapper(true);
2801
+ return;
2802
+ }
2803
+
2804
+ const analyticsFile = ensureFirebaseAnalyticsWrapper(false);
2805
+
2806
+ if (isPackageAvailable("@capacitor-firebase/analytics")) {
2807
+ runNpmCommand("npm uninstall @capacitor-firebase/analytics");
2808
+ } else {
2809
+ console.log("ℹ️ @capacitor-firebase/analytics is not installed.");
2810
+ }
2811
+
2812
+ if (isPackageAvailable("firebase")) {
2813
+ if (sourceImportsPackage("firebase", analyticsFile)) {
2814
+ console.log("ℹ️ Keeping firebase package because other source files import it.");
2815
+ } else {
2816
+ runNpmCommand("npm uninstall firebase");
2817
+ }
2818
+ }
2819
+ };
2820
+
2821
+ const syncFirebaseAnalyticsPodfile = () => {
2822
+ const podfilePath = path.join(process.cwd(), "ios", "App", "Podfile");
2823
+ if (!fs.existsSync(podfilePath)) return;
2824
+
2825
+ const { iosEnabled } = getAnalyticsConfig();
2826
+ const basePodLine = " pod 'CapacitorFirebaseAnalytics', :path => '../../node_modules/@capacitor-firebase/analytics'";
2827
+ const analyticsPodLine = " pod 'CapacitorFirebaseAnalytics/Analytics', :path => '../../node_modules/@capacitor-firebase/analytics'";
2828
+
2829
+ let content = fs.readFileSync(podfilePath, "utf8");
2830
+ let nextContent = content;
2831
+
2832
+ if (iosEnabled) {
2833
+ if (!nextContent.includes(basePodLine)) {
2834
+ nextContent = nextContent.replace(
2835
+ /(pod 'CapacitorCommunitySafeArea'[^\n]*\r?\n)/,
2836
+ `$1${basePodLine}\n`
2837
+ );
2838
+ }
2839
+
2840
+ if (!nextContent.includes(analyticsPodLine)) {
2841
+ nextContent = nextContent.replace(
2842
+ /(# Add your Pods here\r?\n)/,
2843
+ `$1${analyticsPodLine}\n`
2844
+ );
2845
+ }
2846
+ } else {
2847
+ nextContent = nextContent
2848
+ .split(/\r?\n/)
2849
+ .filter(line => !line.includes("CapacitorFirebaseAnalytics"))
2850
+ .join("\n");
2851
+ }
2852
+
2853
+ if (nextContent !== content) {
2854
+ fs.writeFileSync(podfilePath, nextContent, "utf8");
2855
+ console.log(`✅ iOS Firebase Analytics Podfile entries ${iosEnabled ? "enabled" : "removed"}.`);
2856
+ } else {
2857
+ console.log(`ℹ️ iOS Firebase Analytics Podfile entries already ${iosEnabled ? "enabled" : "disabled"}.`);
2858
+ }
2859
+ };
2860
+
2861
+ const ensureImportOrRequire = (content, moduleName, esmLine, cjsLine) => {
2862
+ const escaped = moduleName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2863
+ if (new RegExp(`from\\s*['"]${escaped}['"]|require\\s*\\(\\s*['"]${escaped}['"]\\s*\\)`).test(content)) {
2864
+ return content;
2865
+ }
2866
+
2867
+ const isCommonJs = /module\.exports|require\s*\(/.test(content);
2868
+ const line = isCommonJs ? cjsLine : esmLine;
2869
+
2870
+ return `${line}\n${content}`;
2871
+ };
2872
+
2873
+ const ensureViteDefineEntry = (viteContent, key, valueExpression) => {
2874
+ const entry = ` '${key}': JSON.stringify(${valueExpression}),`;
2875
+
2876
+ if (viteContent.includes(`'${key}'`) || viteContent.includes(`"${key}"`)) {
2877
+ return viteContent;
2878
+ }
2879
+
2880
+ if (/define\s*:\s*{/.test(viteContent)) {
2881
+ return viteContent.replace(/define\s*:\s*{\s*/, match => `${match}\n${entry}\n`);
2882
+ }
2883
+
2884
+ const defineBlock = ` define: {
2885
+ '${key}': JSON.stringify(${valueExpression}),
2886
+ },`;
2887
+
2888
+ if (/\n\s*publicDir\s*:\s*[^,\n]+,?/.test(viteContent)) {
2889
+ return viteContent.replace(
2890
+ /(\n\s*publicDir\s*:\s*[^,\n]+,?)/,
2891
+ `$1\n${defineBlock}`
2892
+ );
2893
+ }
2894
+
2895
+ return viteContent.replace(
2896
+ /(return\s+{)/,
2897
+ `$1\n${defineBlock}`
2898
+ );
2899
+ };
2900
+
2901
+ const ensurePrivacyAndAnalyticsViteConfig = () => {
2902
+ const viteConfigPath = getViteConfigPath();
2903
+
2904
+ if (!viteConfigPath) {
2905
+ console.warn("⚠️ No vite config found. Skipping privacy/analytics Vite sync.");
2906
+ return;
2907
+ }
2908
+
2909
+ let viteContent = fs.readFileSync(viteConfigPath, "utf8");
2910
+ const originalContent = viteContent;
2911
+
2912
+ viteContent = ensureImportOrRequire(
2913
+ viteContent,
2914
+ "fs",
2915
+ "import fs from 'fs';",
2916
+ "const fs = require('fs');"
2917
+ );
2918
+
2919
+ viteContent = ensureImportOrRequire(
2920
+ viteContent,
2921
+ "path",
2922
+ "import path from 'path';",
2923
+ "const path = require('path');"
2924
+ );
2925
+
2926
+ if (!/\bcapacitorConfig\b/.test(viteContent)) {
2927
+ viteContent = viteContent.replace(
2928
+ /(\r?\nexport\s+default|\r?\nmodule\.exports)/,
2929
+ `\nconst capacitorConfig = JSON.parse(fs.readFileSync(path.resolve(__dirname, './capacitor.config.json'), 'utf8'));\n$1`
2930
+ );
2931
+ }
2932
+
2933
+ if (!/\bAPP_UNIQUE_ID\b/.test(viteContent)) {
2934
+ viteContent = viteContent.replace(
2935
+ /(const capacitorConfig = JSON\.parse\(fs\.readFileSync\(path\.resolve\(__dirname,\s*['"]\.\/capacitor\.config\.json['"]\),\s*['"]utf8['"]\)\);\r?\n)/,
2936
+ `$1const APP_UNIQUE_ID = capacitorConfig.android?.APP_UNIQUE_ID ?? '';\n`
2937
+ );
2938
+ }
2939
+
2940
+ if (!/\bANDROID_ANALYTICS_ENABLED\b/.test(viteContent)) {
2941
+ viteContent = viteContent.replace(
2942
+ /(const APP_UNIQUE_ID = capacitorConfig\.android\?\.APP_UNIQUE_ID \?\? ['"]['"];\r?\n)/,
2943
+ `$1const ANDROID_ANALYTICS_ENABLED = capacitorConfig.android?.ANALYTICS_ENABLED ?? false;\n`
2944
+ );
2945
+ }
2946
+
2947
+ if (!/\bIOS_ANALYTICS_ENABLED\b/.test(viteContent)) {
2948
+ viteContent = viteContent.replace(
2949
+ /(const ANDROID_ANALYTICS_ENABLED = capacitorConfig\.android\?\.ANALYTICS_ENABLED \?\? false;\r?\n)/,
2950
+ `$1const IOS_ANALYTICS_ENABLED = capacitorConfig.ios?.ANALYTICS_ENABLED ?? false;\n`
2951
+ );
2952
+ }
2953
+
2954
+ if (!/\bIAP_DESIGN\b/.test(viteContent)) {
2955
+ viteContent = viteContent.replace(
2956
+ /(const IOS_ANALYTICS_ENABLED = capacitorConfig\.ios\?\.ANALYTICS_ENABLED \?\? false;\r?\n)/,
2957
+ `$1const IAP_DESIGN = capacitorConfig["IAP-Design"] ?? 'default';\n`
2958
+ );
2959
+ }
2960
+
2961
+ viteContent = viteContent
2962
+ .replace(
2963
+ /const ANDROID_ANALYTICS_ENABLED = capacitorConfig\.android\?\.ANALY[ST]ICS_ENABLED \?\? capacitorConfig\.android\?\.ANALYTICS_ENABLED \?\? false;/g,
2964
+ "const ANDROID_ANALYTICS_ENABLED = capacitorConfig.android?.ANALYTICS_ENABLED ?? false;"
2965
+ )
2966
+ .replace(
2967
+ /const IOS_ANALYTICS_ENABLED = capacitorConfig\.ios\?\.ANALY[ST]ICS_ENABLED \?\? capacitorConfig\.ios\?\.ANALYTICS_ENABLED \?\? false;/g,
2968
+ "const IOS_ANALYTICS_ENABLED = capacitorConfig.ios?.ANALYTICS_ENABLED ?? false;"
2969
+ );
2970
+
2971
+ viteContent = ensureViteDefineEntry(viteContent, "import.meta.env.VITE_APP_UNIQUE_ID", "APP_UNIQUE_ID");
2972
+ viteContent = ensureViteDefineEntry(viteContent, "import.meta.env.VITE_ANDROID_ANALYTICS_ENABLED", "ANDROID_ANALYTICS_ENABLED");
2973
+ viteContent = ensureViteDefineEntry(viteContent, "import.meta.env.VITE_IOS_ANALYTICS_ENABLED", "IOS_ANALYTICS_ENABLED");
2974
+ viteContent = ensureViteDefineEntry(viteContent, "import.meta.env.VITE_IAP_DESIGN", "IAP_DESIGN");
2975
+
2976
+ if (viteContent !== originalContent) {
2977
+ fs.writeFileSync(viteConfigPath, viteContent, "utf8");
2978
+ console.log("✅ vite.config.(m)js privacy/analytics settings synced.");
2979
+ } else {
2980
+ console.log("ℹ️ vite.config.(m)js privacy/analytics settings already synced.");
2981
+ }
2982
+ };
2983
+
2984
+ const syncFirebaseAnalyticsSetup = () => {
2985
+ syncFirebaseAnalyticsPackages();
2986
+ syncFirebaseAnalyticsPodfile();
2987
+ ensurePrivacyAndAnalyticsViteConfig();
2988
+ };
2989
+
2990
+ const getAdmobIapConfigPath = () => {
2991
+ const possiblePaths = [
2992
+ path.join(process.cwd(), 'src', 'js', 'Ads', 'admob-ad-configuration-nextgen.json'),
2993
+ path.join(process.cwd(), 'src', 'js', 'Ads', 'admob-ad-configuration.json')
2994
+ ];
2995
+
2996
+ return possiblePaths.find(fileExists);
2997
+ };
2998
+
2999
+ const getAdmobIapConfig = () => {
3000
+ const admobIapConfigPath = getAdmobIapConfigPath();
3001
+
3002
+ if (!admobIapConfigPath) {
3003
+ console.warn("⚠️ No AdMob configuration JSON found. Skipping RevenueCat IAP package sync.");
3004
+ return null;
3005
+ }
3006
+
3007
+ try {
3008
+ return JSON.parse(fs.readFileSync(admobIapConfigPath, "utf8"));
3009
+ } catch (err) {
3010
+ console.error(`❌ Failed to read ${path.relative(process.cwd(), admobIapConfigPath)}`, err);
3011
+ process.exit(1);
3012
+ }
3013
+ };
3014
+
3015
+ const isAnyIapStoreEnabled = (iapConfig) => {
3016
+ return Boolean(
3017
+ iapConfig?.playstore === true ||
3018
+ iapConfig?.samsung === true ||
3019
+ iapConfig?.amazon === true ||
3020
+ iapConfig?.ios === true
3021
+ );
3022
+ };
3023
+
3024
+ const ensureIapDesignInCapacitorConfig = (iapConfig) => {
3025
+ if (!isAnyIapStoreEnabled(iapConfig)) return;
3026
+
3027
+ if (!fs.existsSync(configPath)) {
3028
+ console.warn("⚠️ capacitor.config.json not found. Skipping IAP-Design sync.");
3029
+ return;
3030
+ }
3031
+
3032
+ const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
3033
+
3034
+ if (Object.prototype.hasOwnProperty.call(config, "IAP-Design")) {
3035
+ console.log("ℹ️ capacitor.config.json IAP-Design already configured.");
3036
+ return;
3037
+ }
3038
+
3039
+ config["IAP-Design"] = "default";
3040
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
3041
+ console.log('✅ Added "IAP-Design": "default" to capacitor.config.json.');
3042
+ };
3043
+
3044
+ const getCurrentStoreId = () => {
3045
+ const envStoreId = Number(process.env.VITE_STORE_ID);
3046
+ if (Number.isInteger(envStoreId) && envStoreId > 0) return envStoreId;
3047
+
3048
+ const viteConfigPath = getViteConfigPath();
3049
+ if (!viteConfigPath) return 1;
3050
+
3051
+ const viteConfigContent = fs.readFileSync(viteConfigPath, "utf8");
3052
+ const commonAliasMatch = viteConfigContent.match(/['"]@common['"]:\s*path\.resolve\(__dirname,\s*['"]([^'"]+)['"]\)/);
3053
+
3054
+ if (!commonAliasMatch) return 1;
3055
+
3056
+ const commonFilePath = path.resolve(path.dirname(viteConfigPath), commonAliasMatch[1]);
3057
+ if (!fs.existsSync(commonFilePath)) return 1;
3058
+
3059
+ const commonFileContent = fs.readFileSync(commonFilePath, "utf8");
3060
+ const storeIdMatch = commonFileContent.match(/export\s+let\s+_storeid\s*=\s*import\.meta\.env\.VITE_STORE_ID\s*\|\|\s*(\d+)\s*;/);
3061
+
3062
+ return storeIdMatch ? Number(storeIdMatch[1]) : 1;
3063
+ };
3064
+
3065
+ const getRevenueCatStoreState = (iapConfig) => {
3066
+ const storeId = getCurrentStoreId();
3067
+ const configuration = iapConfig.configuration || {};
3068
+ const storeMap = {
3069
+ 1: {
3070
+ name: "PlayStore",
3071
+ enabled: iapConfig.playstore === true,
3072
+ apiKeyName: "IAP.configuration.playstore",
3073
+ apiKey: configuration.playstore,
3074
+ usesGoogleBilling: true
3075
+ },
3076
+ 6: {
3077
+ name: "iOSStore",
3078
+ enabled: iapConfig.ios === true,
3079
+ apiKeyName: "IAP.configuration.ios",
3080
+ apiKey: configuration.ios,
3081
+ usesGoogleBilling: false
3082
+ },
3083
+ 7: {
3084
+ name: "AmazonStore",
3085
+ enabled: iapConfig.amazon === true,
3086
+ apiKeyName: "IAP.configuration.amazon",
3087
+ apiKey: configuration.amazon,
3088
+ usesGoogleBilling: false
3089
+ }
3090
+ };
3091
+
3092
+ return {
3093
+ storeId,
3094
+ store: storeMap[storeId] || null
3095
+ };
3096
+ };
3097
+
3098
+ const ensureAndroidPermission = (permission, enabled) => {
3099
+ if (!fs.existsSync(androidManifestPath)) return false;
3100
+
3101
+ let manifestContent = fs.readFileSync(androidManifestPath, "utf8");
3102
+ const permissionLine = ` <uses-permission android:name="${permission}" />`;
3103
+ const permissionRegex = new RegExp(`^\\s*<uses-permission\\s+android:name="${permission.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}"\\s*/?>\\s*\\r?\\n?`, "m");
3104
+
3105
+ if (enabled) {
3106
+ if (permissionRegex.test(manifestContent)) return false;
3107
+
3108
+ manifestContent = manifestContent.replace(
3109
+ /<\/manifest>\s*$/,
3110
+ `${permissionLine}\n</manifest>`
3111
+ );
3112
+ fs.writeFileSync(androidManifestPath, manifestContent, "utf8");
3113
+ console.log(`✅ Added Android permission: ${permission}`);
3114
+ return true;
3115
+ }
3116
+
3117
+ if (!permissionRegex.test(manifestContent)) return false;
3118
+
3119
+ manifestContent = manifestContent.replace(permissionRegex, "");
3120
+ fs.writeFileSync(androidManifestPath, manifestContent, "utf8");
3121
+ console.log(`✅ Removed Android permission: ${permission}`);
3122
+ return true;
3123
+ };
3124
+
3125
+ const syncRevenueCatIapSetup = () => {
3126
+ const admobConfig = getAdmobIapConfig();
3127
+ const iapConfig = admobConfig?.IAP;
3128
+
3129
+ if (!iapConfig) {
3130
+ console.log("ℹ️ IAP configuration not found. Skipping RevenueCat package sync.");
3131
+ return;
3132
+ }
3133
+
3134
+ ensureIapDesignInCapacitorConfig(iapConfig);
3135
+
3136
+ const revenueCatEnabled =
3137
+ iapConfig.playstore === true ||
3138
+ iapConfig.ios === true ||
3139
+ iapConfig.amazon === true;
3140
+ const { storeId, store } = getRevenueCatStoreState(iapConfig);
3141
+ const revenueCatEnabledForCurrentStore = store?.enabled === true;
3142
+
3143
+ if (revenueCatEnabledForCurrentStore && !store.apiKey) {
3144
+ console.error(`❌ RevenueCat IAP is enabled for ${store.name} but ${store.apiKeyName} is missing.`);
3145
+ process.exit(1);
3146
+ }
3147
+
3148
+ let packageChanged = false;
3149
+
3150
+ if (revenueCatEnabledForCurrentStore) {
3151
+ if (!isPackageAvailable("@revenuecat/purchases-capacitor")) {
3152
+ runNpmCommand("npm install @revenuecat/purchases-capacitor");
3153
+ packageChanged = true;
3154
+ } else {
3155
+ console.log(`ℹ️ RevenueCat package already installed for ${store.name}.`);
3156
+ }
3157
+ } else if (isPackageAvailable("@revenuecat/purchases-capacitor")) {
3158
+ runNpmCommand("npm uninstall @revenuecat/purchases-capacitor");
3159
+ packageChanged = true;
3160
+ } else {
3161
+ console.log(`ℹ️ RevenueCat package is not required for store id ${storeId}.`);
3162
+ }
3163
+
3164
+ ensureAndroidPermission("com.android.vending.BILLING", store?.usesGoogleBilling === true && revenueCatEnabledForCurrentStore);
3165
+
3166
+ if (iapConfig.samsung === true && revenueCatEnabled) {
3167
+ console.warn("⚠️ Samsung IAP uses cordova-plugin-samsungiap; PlayStore/Amazon/iOS use RevenueCat.");
3168
+ }
3169
+
3170
+ if (packageChanged) {
3171
+ runNpmCommand("npx cap sync");
3172
+ }
3173
+ };
3174
+
3175
+
3176
+
3177
+
3178
+
3179
+
3180
+
2529
3181
 
2530
3182
 
2531
3183
  const compareVersion = (v1, v2) => {
@@ -2693,14 +3345,16 @@ ensureGitignoreEntry('buildCodeplay/');
2693
3345
 
2694
3346
  // Run the validation
2695
3347
  (async () => {
2696
-
2697
- await loadPluginVersions(); // 🔥 NEW
2698
-
2699
- await checkPlugins();
2700
- checkAndupdateDropInViteConfig();
2701
- syncAdmobAliasInViteConfig();
2702
- checkAdmobConfigurationProperty()
2703
- })();
3348
+
3349
+ await loadPluginVersions(); // 🔥 NEW
3350
+
3351
+ await checkPlugins();
3352
+ syncRevenueCatIapSetup();
3353
+ syncFirebaseAnalyticsSetup();
3354
+ checkAndupdateDropInViteConfig();
3355
+ syncAdmobAliasInViteConfig();
3356
+ checkAdmobConfigurationProperty()
3357
+ })();
2704
3358
 
2705
3359
 
2706
3360
  // ======================================================
@@ -326,6 +326,7 @@ if (!fs.existsSync(configPath)) {
326
326
  const rawData = fs.readFileSync(configPath, 'utf8');
327
327
  const config = JSON.parse(rawData);
328
328
  const targetAppId=config.appId
329
+ const appUniqueId=config.android?.APP_UNIQUE_ID
329
330
 
330
331
 
331
332
 
@@ -394,6 +395,23 @@ function addPostNotificationPermissionIfNeeded() {
394
395
  addPostNotificationPermissionIfNeeded();
395
396
 
396
397
 
398
+ function addExactAlarmPermissionIfNeeded() {
399
+ const permission =
400
+ '<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />';
401
+
402
+ if (Number(appUniqueId) !== 2) {
403
+ console.log('ℹ️ Skipping SCHEDULE_EXACT_ALARM permission for this APP_UNIQUE_ID.');
404
+ return;
405
+ }
406
+
407
+ console.log('✅ Career reminders app detected. Ensuring SCHEDULE_EXACT_ALARM permission...');
408
+ addAndroidPermissions([permission]);
409
+ }
410
+
411
+
412
+ addExactAlarmPermissionIfNeeded();
413
+
414
+
397
415
 
398
416
 
399
417
  //console.log("TARGET APP ID IS : "+ targetAppId)
@@ -418,4 +436,4 @@ else if(targetAppId=="image.to.text.document")
418
436
  ];
419
437
  }
420
438
 
421
- addAndroidPermissions(permissions);
439
+ addAndroidPermissions(permissions);
@@ -17,7 +17,7 @@
17
17
  {"name":"ffmpeg","pattern":"ffmpeg-(\\d+\\.\\d+)$","minVersion":"1.6","baseDir":"js","mandatoryUpdate":true,"isFolder":true},
18
18
  {"name":"theme","pattern":"theme-(\\d+\\.\\d+)$","minVersion":"3.5","baseDir":"theme","destDir":"theme","mandatoryUpdate":true,"isFolder":true},
19
19
  {"name":"certificatejs","pattern":"certificatejs-(\\d+\\.\\d+)$","minVersion":"1.9","baseDir":"certificate","destDir":"certificate","mandatoryUpdate":true,"isFolder":true},
20
- {"name":"IAP","pattern":"IAP-(\\d+\\.\\d+)$","minVersion":"3.0","baseDir":"js/Ads","mandatoryUpdate":true,"isFolder":true},
20
+ {"name":"IAP","pattern":"IAP-(\\d+\\.\\d+)$","minVersion":"3.6","baseDir":"js/Ads","mandatoryUpdate":true,"isFolder":true},
21
21
  {"name":"common-less","pattern":"common-(\\d+\\.\\d+)\\.less$","minVersion":"1.8","baseDir":"assets/css","mandatoryUpdate":true},
22
22
  {"name":"localNotification_AppSettings","pattern":"localNotification_AppSettings-(\\d+\\.\\d+)\\.js$","minVersion":"1.0","baseDir":"js","mandatoryUpdate":true},
23
23
  {"name":"localNotification","pattern":"localNotification-(\\d+\\.\\d+)\\.js$","minVersion":"2.2","baseDir":"js","mandatoryUpdate":true},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeplay-common",
3
- "version": "4.0.4",
3
+ "version": "4.0.7",
4
4
  "description": "Common build scripts and files",
5
5
  "scripts": {
6
6
  "postinstall": "node scripts/sync-files.js",