Enable Firebase by default for Android and iOS builds so normal local, test, and production builds no longer depend on passing -PfirebaseEnabled.
Opt out with -PfirebaseEnabled=false or FIREBASE_ENABLED=false. A missing google-services.json or GoogleService-Info.plist now fails the build instead of silently skipping Firebase.
This commit is contained in:
@@ -2,16 +2,18 @@
|
||||
|
||||
## Android Build — Google Play Services / FOSS Compatibility
|
||||
|
||||
**Firebase is opt-in. Do NOT enable it accidentally.**
|
||||
**Firebase is on by default for Android and iOS builds.** Do not turn it off unless the build is an exceptional FOSS/F-Droid artifact.
|
||||
|
||||
`android/google-services.json` is gitignored and may be present on disk for push notification development, but Firebase is only activated when you explicitly pass `-PfirebaseEnabled` to Gradle:
|
||||
`android/app/google-services.json` is required when Firebase is enabled. It does not decide the switch: a missing file fails the build, and a file on disk is ignored when Firebase is explicitly disabled.
|
||||
|
||||
- **FOSS / APK / Aurora / Zapstore / F-Droid builds**: just `./gradlew assembleRelease` — Firebase stays off even if `google-services.json` is on disk.
|
||||
- **Firebase / FCM / Play Store builds**: `./gradlew bundleRelease -PfirebaseEnabled` — explicitly opt in.
|
||||
- **Default (local, test, production, Play Store)**: `./gradlew assembleRelease` or `./gradlew bundleRelease` — do not pass `-PfirebaseEnabled`.
|
||||
- **Opt out (F-Droid / FOSS)**: `./gradlew assembleRelease -PfirebaseEnabled=false`. npm scripts honor `FIREBASE_ENABLED=false`, which forwards that property.
|
||||
|
||||
This guard is in `android/app/build.gradle`. Do NOT change this conditional to activate Firebase unconditionally based on file presence alone — that was the bug that broke FOSS distribution in June 2026.
|
||||
iOS builds bundle `ios/App/App/GoogleService-Info.plist` by default (`ios/App/scripts/bundle-firebase-config.sh`, run from `scripts/build-ios.sh` and as an Xcode build phase). Opt out with `FIREBASE_ENABLED=false`. A missing plist fails the build; the file's presence does not enable Firebase on its own.
|
||||
|
||||
`google-services.json` is intentionally excluded from git (`android/.gitignore`). Never commit it.
|
||||
The guard is in `android/app/build.gradle`. Do NOT activate Firebase from file presence alone — that was the bug that broke FOSS distribution in June 2026.
|
||||
|
||||
Never commit `google-services.json`.
|
||||
|
||||
Full details, incident history, and F-Droid notes: `doc/android-firebase-gms.md`
|
||||
|
||||
|
||||
+3
-3
@@ -1474,9 +1474,11 @@ So, to create a signed `aab` release:
|
||||
|
||||
```bash
|
||||
# if you use pkgx: pkgx +openjdk.org /bin/zsh
|
||||
./gradlew bundleRelease -Dlint.baselines.continue=true -PfirebaseEnabled
|
||||
./gradlew bundleRelease -Dlint.baselines.continue=true
|
||||
```
|
||||
|
||||
Firebase is on by default (`firebaseEnabled=true` in `android/gradle.properties`). Do not pass `-PfirebaseEnabled` for a normal release. F-Droid and other builds that must omit Firebase use `-PfirebaseEnabled=false`.
|
||||
|
||||
... and find your `aab` file at app/build/outputs/bundle/release
|
||||
|
||||
* Gradle packages whatever web bundle was last synced into
|
||||
@@ -1486,8 +1488,6 @@ So, to create a signed `aab` release:
|
||||
* `build-android.sh` runs `./gradlew clean`, which deletes any earlier `aab`, so
|
||||
run `bundleRelease` after the build script finishes.
|
||||
|
||||
* Note that F-Droid builds should omit `-PfirebaseEnabled`.
|
||||
|
||||
At play.google.com/console:
|
||||
|
||||
- Go to Production or the Closed Testing and either Create Track or Manage Track.
|
||||
|
||||
+27
-15
@@ -130,20 +130,32 @@ dependencies {
|
||||
|
||||
apply from: 'capacitor.build.gradle'
|
||||
|
||||
// Firebase / Google Play Services are opt-in. Pass -PfirebaseEnabled to any Gradle command
|
||||
// to activate Firebase (FCM push notifications). Without this flag the build works on
|
||||
// F-Droid, Aurora, Zapstore, and plain APK sideloading even when google-services.json
|
||||
// is present on disk (it is gitignored; see AGENTS.md for the full story).
|
||||
try {
|
||||
def servicesJSON = file('google-services.json')
|
||||
if (servicesJSON.exists() && servicesJSON.text && project.hasProperty('firebaseEnabled')) {
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
logger.info("Firebase enabled: google-services plugin applied")
|
||||
} else if (servicesJSON.exists() && !project.hasProperty('firebaseEnabled')) {
|
||||
logger.info("google-services.json present but firebaseEnabled not set — skipping Firebase plugin (pass -PfirebaseEnabled to enable)")
|
||||
} else {
|
||||
logger.info("google-services.json not found — Firebase plugin not applied")
|
||||
// Firebase is on for every build. Opt out only with -PfirebaseEnabled=false
|
||||
// (F-Droid / other builds that must not apply the google-services plugin).
|
||||
// The config file does not decide the switch: when Firebase is on, a missing
|
||||
// file fails the build; when it is off, a file on disk is ignored.
|
||||
def firebaseEnabledRaw = project.findProperty('firebaseEnabled')
|
||||
def firebaseEnabled = true
|
||||
if (firebaseEnabledRaw != null) {
|
||||
def normalized = firebaseEnabledRaw.toString().trim().toLowerCase()
|
||||
if (normalized == 'false' || normalized == '0' || normalized == 'no') {
|
||||
firebaseEnabled = false
|
||||
}
|
||||
} catch(Exception e) {
|
||||
logger.info("google-services plugin not applied: ${e.message}")
|
||||
}
|
||||
|
||||
if (!firebaseEnabled) {
|
||||
logger.lifecycle('Firebase disabled (-PfirebaseEnabled=false): google-services plugin not applied')
|
||||
} else {
|
||||
def servicesJSON = file('google-services.json')
|
||||
def servicesBody = servicesJSON.isFile() ? servicesJSON.getText('UTF-8') : ''
|
||||
if (!servicesBody.trim() || !servicesBody.contains('mobilesdk_app_id')) {
|
||||
throw new GradleException(
|
||||
'Firebase is enabled, but android/app/google-services.json is missing, empty, or has no mobilesdk_app_id.\n' +
|
||||
'Download it from the Firebase console (Project settings → Your apps → Android app)\n' +
|
||||
'and place it at android/app/google-services.json.\n' +
|
||||
'To build without Firebase (F-Droid / FOSS), pass -PfirebaseEnabled=false.'
|
||||
)
|
||||
}
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
logger.lifecycle('Firebase enabled: google-services plugin applied')
|
||||
}
|
||||
|
||||
+2
-2
@@ -15,8 +15,8 @@ public class NotificationInspectorPlugin extends Plugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this build carries a Firebase config, i.e. was built with
|
||||
* -PfirebaseEnabled. Without it, PushNotifications.register() throws on the
|
||||
* Whether this build carries a Firebase config. Normal builds do.
|
||||
* -PfirebaseEnabled=false omits it. Without it, PushNotifications.register() throws on the
|
||||
* Capacitor plugin thread and kills the app, so JS must check this first.
|
||||
* The google-services Gradle plugin generates the google_app_id resource,
|
||||
* which is what Firebase's own auto-initialization looks for.
|
||||
|
||||
@@ -22,3 +22,8 @@ org.gradle.jvmargs=-Xmx1536m --add-opens=jdk.compiler/com.sun.tools.javac.api=AL
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
android.suppressUnsupportedCompileSdk=36
|
||||
|
||||
# Firebase/FCM is on for every Gradle build (Android Studio, ./gradlew, npm scripts).
|
||||
# Opt out with -PfirebaseEnabled=false. android/app/google-services.json does not
|
||||
# toggle this; a missing file fails the build while Firebase is enabled.
|
||||
firebaseEnabled=true
|
||||
|
||||
+18
-21
@@ -2,49 +2,45 @@
|
||||
|
||||
## Overview
|
||||
|
||||
The app is designed to work on Android devices with and without Google Play Services (GMS). Firebase/FCM push notifications are an opt-in feature at build time; all other functionality works on GMS-less devices (F-Droid, LineageOS without OpenGApps, etc.).
|
||||
The app is designed to work on Android devices with and without Google Play Services (GMS). Firebase/FCM is **on by default** for local, test, and production builds. GMS-less devices still run the app; FCM degrades when Play Services is absent. F-Droid and other builds that must omit Firebase opt out explicitly.
|
||||
|
||||
## How the opt-in guard works
|
||||
## How the switch works
|
||||
|
||||
`android/app/build.gradle` applies the `com.google.gms.google-services` Gradle plugin only when **both** conditions are true:
|
||||
`android/app/build.gradle` applies `com.google.gms.google-services` unless `firebaseEnabled` is `false`. `android/gradle.properties` sets `firebaseEnabled=true`, so Android Studio, `./gradlew`, and `scripts/build-android.sh` all pick it up. `-PfirebaseEnabled` and `-PfirebaseEnabled=true` stay on. The config file does not decide the switch.
|
||||
|
||||
1. `android/google-services.json` is present on disk
|
||||
2. The Gradle property `firebaseEnabled` is explicitly passed
|
||||
When Firebase is on, `android/app/google-services.json` must exist, be non-empty, and contain `mobilesdk_app_id`. Otherwise configuration fails with an error that names that path. When Firebase is off, the file is ignored even if it is on disk.
|
||||
|
||||
```groovy
|
||||
if (servicesJSON.exists() && servicesJSON.text && project.hasProperty('firebaseEnabled')) {
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
}
|
||||
```
|
||||
`scripts/build-android.sh` forwards an opt-out: `FIREBASE_ENABLED=false` adds `-PfirebaseEnabled=false` to its Gradle commands. npm scripts do not pass `-PfirebaseEnabled`; they rely on the default.
|
||||
|
||||
This means the file can live on disk for development purposes without accidentally activating Firebase.
|
||||
iOS uses the same default. `ios/App/scripts/bundle-firebase-config.sh` copies `ios/App/App/GoogleService-Info.plist` into the app and the share extension. `scripts/build-ios.sh` runs it with `--check` before a device build, and both Xcode targets run it as a build phase. `FIREBASE_ENABLED=false` skips the plist. A missing or empty plist fails the build when Firebase is on.
|
||||
|
||||
## Build commands
|
||||
|
||||
| Target | Command | Firebase |
|
||||
|---|---|---|
|
||||
| APK / sideload / Zapstore | `./gradlew assembleRelease` | off |
|
||||
| Aurora / Play Store without FCM | `./gradlew bundleRelease` | off |
|
||||
| Play Store with FCM push notifications | `./gradlew bundleRelease -PfirebaseEnabled` | on |
|
||||
| F-Droid | `./gradlew assembleRelease` | off (required) |
|
||||
| Local / test / production APK or AAB | `./gradlew assembleRelease` or `bundleRelease` | on |
|
||||
| Play Store with FCM | `./gradlew bundleRelease` | on |
|
||||
| iOS (Xcode or `scripts/build-ios.sh`) | default | on |
|
||||
| F-Droid / FOSS Android | `./gradlew assembleRelease -PfirebaseEnabled=false` | off |
|
||||
| iOS without Firebase | `FIREBASE_ENABLED=false` | off |
|
||||
|
||||
## Behavior on non-GMS devices
|
||||
|
||||
When built with `-PfirebaseEnabled`, Firebase SDKs check for GMS availability at startup and degrade gracefully if it is absent:
|
||||
When Firebase is enabled (the default), Firebase SDKs check for GMS availability at startup and degrade gracefully if it is absent:
|
||||
|
||||
- Firebase initializes but detects no GMS
|
||||
- FCM skips token registration silently (no token, no notifications)
|
||||
- The app continues to work normally
|
||||
|
||||
This means a single Play Store AAB (`bundleRelease -PfirebaseEnabled`) covers both GMS and non-GMS users. GMS users get push notifications; non-GMS users get a fully functional app without them.
|
||||
This means a single Play Store AAB (`bundleRelease`, Firebase on by default) covers both GMS and non-GMS users. GMS users get push notifications; non-GMS users get a fully functional app without them.
|
||||
|
||||
**Aurora Store** pulls the exact APK from Play Store servers, so Aurora users get whichever variant was uploaded. The Play Store AAB built with `-PfirebaseEnabled` is correct for Aurora.
|
||||
**Aurora Store** pulls the exact APK from Play Store servers, so Aurora users get whichever variant was uploaded. The default Play Store AAB (Firebase on) is the one to upload.
|
||||
|
||||
**F-Droid** is stricter: their build policy rejects any APK with GMS dependencies at the binary level, even with graceful degradation. F-Droid submission would require a separate `assembleRelease` build (no flag) and a dedicated F-Droid listing.
|
||||
**F-Droid** is stricter: their build policy rejects any APK with GMS dependencies at the binary level, even with graceful degradation. F-Droid submission would require a separate `assembleRelease -PfirebaseEnabled=false` build and a dedicated F-Droid listing.
|
||||
|
||||
## Push registration in builds without Firebase
|
||||
|
||||
A build without `-PfirebaseEnabled` contains the `@capacitor/push-notifications` plugin, but no Firebase config. In that build, `PushNotifications.register()` throws `Default FirebaseApp is not initialized` on the Capacitor plugin thread, which kills the app; a JS `try/catch` cannot intercept it.
|
||||
A build with `-PfirebaseEnabled=false` contains the `@capacitor/push-notifications` plugin, but no Firebase config. In that build, `PushNotifications.register()` throws `Default FirebaseApp is not initialized` on the Capacitor plugin thread, which kills the app; a JS `try/catch` cannot intercept it.
|
||||
|
||||
The JS therefore asks native code first. `NotificationInspector.isFirebaseConfigured()` (`android/app/src/main/java/app/timesafari/notifications/NotificationInspectorPlugin.java`) reports whether the `google_app_id` string resource exists; the google-services Gradle plugin generates that resource only when Firebase is enabled, and it is the same signal Firebase's auto-initialization uses. `firebaseMessagingClient.ts` skips `register()` on Android when it is absent (or when the check fails), logging `No Firebase config in this Android build; skipping push registration`. Listener setup and the notification permission request run either way, since local daily notifications need that permission.
|
||||
|
||||
@@ -56,7 +52,8 @@ Any code that calls `PushNotifications.register()` or other `FirebaseMessaging`
|
||||
|
||||
- Gitignored (`android/.gitignore` line 80) — never commit it
|
||||
- Contains Firebase project credentials (project number, app ID, API key)
|
||||
- Safe to leave on disk; has no effect unless `-PfirebaseEnabled` is passed
|
||||
- Required for the default (Firebase on) build. Place it at `android/app/google-services.json`
|
||||
- Ignored when you pass `-PfirebaseEnabled=false`; leaving it on disk does not re-enable Firebase
|
||||
- Obtain from the Firebase console: Project Settings → Your apps → Android app → Download `google-services.json`
|
||||
|
||||
## Known GMS dependency: MLKit barcode scanner
|
||||
|
||||
@@ -240,7 +240,7 @@ Complete this section once before your first physical-device push test. If Fireb
|
||||
|
||||
2. The repo gitignores this file — each developer keeps a local copy; never commit it.
|
||||
|
||||
3. Rebuild so Gradle applies the Google Services plugin (`android/app/build.gradle` applies `com.google.gms.google-services` when the file exists). If the file is missing, the build logs: *google-services.json not found, google-services plugin not applied. Push Notifications won't work*.
|
||||
3. Rebuild. Firebase is on by default, so Gradle applies the Google Services plugin. If `android/app/google-services.json` is missing or empty, the build stops and names that path. Opt out only with `-PfirebaseEnabled=false` or `FIREBASE_ENABLED=false`.
|
||||
|
||||
4. Sync Capacitor if you changed native config:
|
||||
|
||||
@@ -928,11 +928,11 @@ Compare with panel **Backend Status** and Event Log error text.
|
||||
|
||||
### Gradle: Push Notifications won't work
|
||||
|
||||
**Symptoms:** Build log: `google-services.json not found, google-services plugin not applied`.
|
||||
**Symptoms:** Build fails: `android/app/google-services.json is missing, empty, or has no mobilesdk_app_id`.
|
||||
|
||||
**Likely causes:** Missing `android/app/google-services.json`.
|
||||
|
||||
**Verification:** File exists on disk; rebuild shows Google Services plugin applied.
|
||||
**Verification:** File exists on disk and the build log says `Firebase enabled: google-services plugin applied`.
|
||||
|
||||
**Fixes:** Download from Firebase Console (package `app.timesafari.app`); place in `android/app/`; `npx cap sync android` and rebuild.
|
||||
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
C86585DF2ED456DE00824752 /* TimeSafariShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = C86585D52ED456DE00824752 /* TimeSafariShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
C8C56E142EE0474B00737D0E /* SharedImageUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C56E132EE0474B00737D0E /* SharedImageUtility.swift */; };
|
||||
C8C56E162EE064CB00737D0E /* SharedImagePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C56E152EE064CA00737D0E /* SharedImagePlugin.swift */; };
|
||||
C8E73DD12FC6E5DC0057F59A /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C8E73DD02FC6E5DC0057F59A /* GoogleService-Info.plist */; };
|
||||
C8E73DD22FC6E5DC0057F59A /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C8E73DD02FC6E5DC0057F59A /* GoogleService-Info.plist */; };
|
||||
E9F1A0022EE05A8B00737D01 /* NotificationInspectorPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F1A0012EE05A8B00737D01 /* NotificationInspectorPlugin.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@@ -185,6 +183,7 @@
|
||||
504EC3001FED79650016851F /* Sources */,
|
||||
504EC3011FED79650016851F /* Frameworks */,
|
||||
504EC3021FED79650016851F /* Resources */,
|
||||
F1REBA5E000000000000A001 /* Bundle Firebase Config */,
|
||||
012076E8FFE4BF260A79B034 /* Fix Privacy Manifest */,
|
||||
96A7EF592DF3366D00084D51 /* Fix Privacy Manifest */,
|
||||
C86585E02ED456DE00824752 /* Embed Foundation Extensions */,
|
||||
@@ -207,6 +206,7 @@
|
||||
C86585D12ED456DE00824752 /* Sources */,
|
||||
C86585D22ED456DE00824752 /* Frameworks */,
|
||||
C86585D32ED456DE00824752 /* Resources */,
|
||||
F1REBA5E000000000000A002 /* Bundle Firebase Config */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -270,7 +270,6 @@
|
||||
50379B232058CBB4000EE86E /* capacitor.config.json in Resources */,
|
||||
504EC30D1FED79650016851F /* Main.storyboard in Resources */,
|
||||
2FAD9763203C412B000D30F8 /* config.xml in Resources */,
|
||||
C8E73DD12FC6E5DC0057F59A /* GoogleService-Info.plist in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -278,7 +277,6 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C8E73DD22FC6E5DC0057F59A /* GoogleService-Info.plist in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -342,6 +340,48 @@
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
F1REBA5E000000000000A001 /* Bundle Firebase Config */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
enableUserScriptSandboxing = 0;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Bundle Firebase Config";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PROJECT_DIR}/scripts/bundle-firebase-config.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
F1REBA5E000000000000A002 /* Bundle Firebase Config */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
enableUserScriptSandboxing = 0;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Bundle Firebase Config";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PROJECT_DIR}/scripts/bundle-firebase-config.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
96A7EF592DF3366D00084D51 /* Fix Privacy Manifest */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
|
||||
Executable
+61
@@ -0,0 +1,61 @@
|
||||
#!/bin/sh
|
||||
# Bundle GoogleService-Info.plist when Firebase is enabled (the default).
|
||||
# Opt out with FIREBASE_ENABLED=false. The plist does not decide the switch:
|
||||
# a missing file fails an enabled build, and an opt-out build does not bundle
|
||||
# the plist even when the file is on disk.
|
||||
#
|
||||
# Xcode run-script phases invoke this with no arguments. build-ios.sh may pass
|
||||
# --check to validate before a long build without copying into a product.
|
||||
set -eu
|
||||
|
||||
check_only=0
|
||||
if [ "${1:-}" = "--check" ]; then
|
||||
check_only=1
|
||||
fi
|
||||
|
||||
if [ -z "${SRCROOT:-}" ]; then
|
||||
script_dir=$(CDPATH= cd -- "$(dirname "$0")" && pwd)
|
||||
SRCROOT=$(CDPATH= cd -- "$script_dir/.." && pwd)
|
||||
fi
|
||||
|
||||
normalized=$(printf '%s' "${FIREBASE_ENABLED:-true}" | tr '[:upper:]' '[:lower:]')
|
||||
plist="${SRCROOT}/App/GoogleService-Info.plist"
|
||||
|
||||
firebase_off=0
|
||||
case "$normalized" in
|
||||
false|0|no) firebase_off=1 ;;
|
||||
esac
|
||||
|
||||
if [ "$firebase_off" -eq 1 ]; then
|
||||
if [ "$check_only" -eq 0 ]; then
|
||||
dest_dir="${TARGET_BUILD_DIR:-}/${UNLOCALIZED_RESOURCES_FOLDER_PATH:-}"
|
||||
if [ -n "${TARGET_BUILD_DIR:-}" ] && [ -n "${UNLOCALIZED_RESOURCES_FOLDER_PATH:-}" ]; then
|
||||
rm -f "${dest_dir}/GoogleService-Info.plist"
|
||||
fi
|
||||
fi
|
||||
echo "note: Firebase disabled (FIREBASE_ENABLED=false); GoogleService-Info.plist will not be bundled."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ ! -s "$plist" ] || ! grep -q "GOOGLE_APP_ID" "$plist"; then
|
||||
echo "error: Firebase is enabled, but ios/App/App/GoogleService-Info.plist is missing, empty, or has no GOOGLE_APP_ID." >&2
|
||||
echo "error: Download it from the Firebase console (Project settings → Your apps → Apple apps)" >&2
|
||||
echo "error: and place it at ios/App/App/GoogleService-Info.plist." >&2
|
||||
echo "error: To build without Firebase, set FIREBASE_ENABLED=false." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$check_only" -eq 1 ]; then
|
||||
echo "note: Firebase enabled; ios/App/App/GoogleService-Info.plist is present."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -z "${TARGET_BUILD_DIR:-}" ] || [ -z "${UNLOCALIZED_RESOURCES_FOLDER_PATH:-}" ]; then
|
||||
echo "error: Bundle Firebase Config must run as an Xcode build phase (TARGET_BUILD_DIR is unset)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dest_dir="${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||
mkdir -p "$dest_dir"
|
||||
cp "$plist" "${dest_dir}/GoogleService-Info.plist"
|
||||
echo "note: Firebase enabled; bundled GoogleService-Info.plist."
|
||||
@@ -23,6 +23,7 @@
|
||||
# --assets Generate assets only
|
||||
# --deploy Deploy APK to connected device
|
||||
# --uninstall Uninstall app from connected device
|
||||
# FIREBASE_ENABLED=false Opt out of Firebase (-PfirebaseEnabled=false)
|
||||
# -h, --help Show this help message
|
||||
# -v, --verbose Enable verbose logging
|
||||
#
|
||||
@@ -439,6 +440,9 @@ print_android_usage() {
|
||||
echo " --uninstall Uninstall app from connected device"
|
||||
echo " --api-ip <ip> Custom IP address for claim API (defaults to 10.0.2.2)"
|
||||
echo ""
|
||||
echo "Firebase is on by default. Opt out with FIREBASE_ENABLED=false"
|
||||
echo "(passes -PfirebaseEnabled=false to Gradle)."
|
||||
echo ""
|
||||
echo "Common Options:"
|
||||
echo " -h, --help Show this help message"
|
||||
echo " -v, --verbose Enable verbose logging"
|
||||
@@ -460,6 +464,17 @@ print_android_usage() {
|
||||
# Parse command line arguments
|
||||
parse_android_args "$@"
|
||||
|
||||
# Firebase is on unless FIREBASE_ENABLED is false, 0, or no. Gradle also
|
||||
# defaults firebaseEnabled=true; this only forwards an explicit opt-out.
|
||||
gradle_firebase_arg=""
|
||||
firebase_enabled_normalized=$(printf '%s' "${FIREBASE_ENABLED:-true}" | tr '[:upper:]' '[:lower:]')
|
||||
case "$firebase_enabled_normalized" in
|
||||
false|0|no)
|
||||
gradle_firebase_arg="-PfirebaseEnabled=false"
|
||||
log_info "Firebase disabled for this Android build (FIREBASE_ENABLED=${FIREBASE_ENABLED})"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Print build header
|
||||
print_header "TimeSafari Android Build Process"
|
||||
|
||||
@@ -528,7 +543,7 @@ if [ "$CLEAN_ONLY" = true ]; then
|
||||
log_info "Clean-only mode: cleaning build artifacts"
|
||||
safe_execute "Cleaning Android app" "npm run clean:android" || exit 1
|
||||
safe_execute "Cleaning dist directory" "clean_build_artifacts dist" || exit 1
|
||||
safe_execute "Cleaning Gradle build" "cd android && ./gradlew clean && cd .." || exit 4
|
||||
safe_execute "Cleaning Gradle build" "cd android && ./gradlew clean ${gradle_firebase_arg} && cd .." || exit 4
|
||||
log_success "Clean completed successfully!"
|
||||
exit 0
|
||||
fi
|
||||
@@ -561,7 +576,7 @@ if [ "$DEPLOY_APP" = true ]; then
|
||||
fi
|
||||
|
||||
# Build APK
|
||||
safe_execute "Building APK" "cd android && ./gradlew assembleDebug && cd .." || exit 5
|
||||
safe_execute "Building APK" "cd android && ./gradlew assembleDebug ${gradle_firebase_arg} && cd .." || exit 5
|
||||
|
||||
# Install APK on device
|
||||
safe_execute "Installing APK on device" "adb install -r android/app/build/outputs/apk/debug/app-debug.apk" || exit 6
|
||||
@@ -628,26 +643,26 @@ fi
|
||||
safe_execute "Generating assets" "npx capacitor-assets generate --android --assetPath resources" || exit 7
|
||||
|
||||
# Step 8: Clean Gradle build
|
||||
safe_execute "Cleaning Gradle build" "cd android && ./gradlew clean && cd .." || exit 4
|
||||
safe_execute "Cleaning Gradle build" "cd android && ./gradlew clean ${gradle_firebase_arg} && cd .." || exit 4
|
||||
|
||||
# Step 9: Build based on type
|
||||
if [ "$BUILD_TYPE" = "debug" ]; then
|
||||
safe_execute "Assembling debug build" "cd android && ./gradlew assembleDebug && cd .." || exit 5
|
||||
safe_execute "Assembling debug build" "cd android && ./gradlew assembleDebug ${gradle_firebase_arg} && cd .." || exit 5
|
||||
elif [ "$BUILD_TYPE" = "release" ]; then
|
||||
safe_execute "Assembling release build" "cd android && ./gradlew assembleRelease && cd .." || exit 5
|
||||
safe_execute "Assembling release build" "cd android && ./gradlew assembleRelease ${gradle_firebase_arg} && cd .." || exit 5
|
||||
fi
|
||||
|
||||
# Step 10: Build APK/AAB if requested
|
||||
if [ "$BUILD_APK" = true ]; then
|
||||
if [ "$BUILD_TYPE" = "debug" ]; then
|
||||
safe_execute "Building debug APK" "cd android && ./gradlew assembleDebug && cd .." || exit 5
|
||||
safe_execute "Building debug APK" "cd android && ./gradlew assembleDebug ${gradle_firebase_arg} && cd .." || exit 5
|
||||
else
|
||||
safe_execute "Building release APK" "cd android && ./gradlew assembleRelease && cd .." || exit 5
|
||||
safe_execute "Building release APK" "cd android && ./gradlew assembleRelease ${gradle_firebase_arg} && cd .." || exit 5
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$BUILD_AAB" = true ]; then
|
||||
safe_execute "Building AAB" "cd android && ./gradlew bundleRelease && cd .." || exit 5
|
||||
safe_execute "Building AAB" "cd android && ./gradlew bundleRelease ${gradle_firebase_arg} && cd .." || exit 5
|
||||
if [ "$BUILD_MODE" = "production" ]; then
|
||||
safe_execute "Checking production AAB" "./scripts/check-prod-bundle.sh android/app/build/outputs/bundle/release/app-release.aab" || exit 10
|
||||
fi
|
||||
|
||||
@@ -44,6 +44,8 @@ print_ios_usage() {
|
||||
echo " --auto-run Auto-run app after build"
|
||||
echo " --api-ip <ip> Custom IP address for claim API (uses Capacitor default)"
|
||||
echo ""
|
||||
echo "Firebase is on by default. Opt out with FIREBASE_ENABLED=false."
|
||||
echo ""
|
||||
echo "Common Options:"
|
||||
echo " -h, --help Show this help message"
|
||||
echo " -v, --verbose Enable verbose logging"
|
||||
@@ -537,6 +539,13 @@ if [ "$ASSETS_ONLY" = true ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Firebase is on unless FIREBASE_ENABLED=false. Fail before the web build and
|
||||
# xcodebuild when the plist is missing. Xcode runs the same script as a build
|
||||
# phase so GUI builds get the same check and bundle the plist.
|
||||
if ! ios/App/scripts/bundle-firebase-config.sh --check; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Handle deploy-app mode
|
||||
if [ "$DEPLOY_APP" = true ]; then
|
||||
deploy_ios_app
|
||||
|
||||
@@ -11,7 +11,7 @@ export type PendingNotificationInfo = {
|
||||
|
||||
export interface NotificationInspectorPlugin {
|
||||
getPendingNotifications(): Promise<{ pending: PendingNotificationInfo[] }>;
|
||||
/** Android only: whether the build has a Firebase config (-PfirebaseEnabled). */
|
||||
/** Android only: whether this build included Firebase (off only with -PfirebaseEnabled=false). */
|
||||
isFirebaseConfigured(): Promise<{ configured: boolean }>;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,9 +69,10 @@ async function registerRetrievedToken(
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether PushNotifications.register() is safe to call. Android builds without
|
||||
* -PfirebaseEnabled (FOSS/F-Droid/APK) have no Firebase config, and register()
|
||||
* there throws natively, crashing the app before any JS catch can run.
|
||||
* Whether PushNotifications.register() is safe to call. Android builds with
|
||||
* Firebase disabled (-PfirebaseEnabled=false, FOSS/F-Droid) have no Firebase
|
||||
* config, and register() there throws natively, crashing the app before any
|
||||
* JS catch can run.
|
||||
*/
|
||||
async function canRegisterNativePush(): Promise<boolean> {
|
||||
if (Capacitor.getPlatform() !== "android") {
|
||||
@@ -101,7 +102,7 @@ export async function reregisterFcmTokenNow(): Promise<string> {
|
||||
|
||||
if (!(await canRegisterNativePush())) {
|
||||
throw new Error(
|
||||
"Push is unavailable: this Android build has no Firebase config (built without -PfirebaseEnabled)",
|
||||
"Push is unavailable: this Android build has no Firebase config (built with -PfirebaseEnabled=false)",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user