fix(cli, breaking): Align Android applicationId with user's bundle.identifier (#4103)

* fix(cli, breaking): Align Android applicationId with user's `bundle.identifier`

This commit fixes the Android `applicationId` generation to accurately reflect the user-configured `bundle.identifier` when provided. It also refactors the logic by removing the `full_mobile_app_name` function, with `bundle_identifier` now being the direct source for the Android `applicationId`.

The `bundle_identifier` function now operates as follows:
1. If `bundle.identifier` is explicitly set by the user:
    - It validates that the identifier has at least three segments (e.g., `com.example.app`).
    - The user's `identifier` is then returned and used directly as the Android `applicationId`.
2. If `config.bundle.identifier` is not set by the user:
    - It defaults to `com.example.{PascalCaseExecutableName}`. The `executable_name` (e.g., "my-app" derived from a project name like "MyApp") is converted to `PascalCase` (e.g., "MyApp") for this default.

**BREAKING CHANGE**:

This change primarily affects Android builds and may alter the final `applicationId` for users who had a custom `bundle.identifier` set, particularly if its casing or final segment differed from the PascalCased `executable_name`.

- **Previous Behavior**:

    The Android `applicationId` was derived from `full_mobile_app_name`. This function effectively used the first two segments of the `bundle_identifier` (user-configured or default) but then **always** appended a PascalCased version of the `executable_name` as the final segment.

    Example Scenario:
    - User project name: "MyApp" (CLI set `executable_name` to "my-app")
    - User configured `bundle.identifier` in Dioxus config: `"com.custom.myapp"`
    - `bundled_app_name()` (from "my-app"): "MyApp"
    - Old `applicationId` (via `full_mobile_app_name`): `"com.custom.MyApp"`

- **New Behavior**:

    With the same user configuration (`"com.custom.myapp"`), the `applicationId` will now be: `"com.custom.myapp"`

This change ensures the `applicationId` respects the user's explicit `bundle.identifier` string. However, if users were unknowingly relying on the previous behavior where the last segment was always a PascalCased version of the `executable_name` (potentially mismatching their intended lowercase final segment in `bundle.identifier`), their `applicationId` will change upon updating the CLI.

According to Google's Android documentation, the `applicationId` must not be changed after an app is published, as this causes Google Play to treat it as a new application. Users who have published Android apps and utilized custom `bundle.identifier` configurations should meticulously verify their `applicationId` after this update to ensure it remains consistent with their published versions or to take appropriate action if a change is detected.

* fix: relax validation for bundle.identifier

1. Contains at least one dot ('.').
2. Does not start with a dot.
3. Does not end with a dot.
4. Does not contain consecutive dots ('..').

e.g., `com.example` or `com.example.app`

---------

Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
This commit is contained in:
FontlosandJonathan Kelley authored and GitHub committed 2025-06-24 12:52:10 -07:00
1 parent 2be2333ef4
commit bfb0642a1d
2 files changed
+17 -21

No files matched your search

+2 -2
View File
@@ -1180,7 +1180,7 @@ We checked the folder: {}
) -> Result<()> {
let apk_path = self.build.debug_apk_path();
let session_cache = self.build.session_cache_dir();
let full_mobile_app_name = self.build.full_mobile_app_name();
let application_id = self.build.bundle_identifier();
let adb = self.build.workspace.android_tools()?.adb.clone();
// Start backgrounded since .open() is called while in the arm of the top-level match
@@ -1261,7 +1261,7 @@ We checked the folder: {}
// eventually, use the user's MainActivity, not our MainActivity
// adb shell am start -n dev.dioxus.main/dev.dioxus.main.MainActivity
let activity_name = format!("{}/dev.dioxus.main.MainActivity", full_mobile_app_name,);
let activity_name = format!("{application_id}/dev.dioxus.main.MainActivity");
let res = Command::new(&adb)
.arg("shell")
.arg("am")
+15 -19
View File
@@ -2567,7 +2567,7 @@ impl BuildRequest {
android_bundle: Option<crate::AndroidSettings>,
}
let hbs_data = AndroidHandlebarsObjects {
application_id: self.full_mobile_app_name(),
application_id: self.bundle_identifier(),
app_name: self.bundled_app_name(),
android_bundle: self.config.bundle.android.clone(),
};
@@ -2979,30 +2979,26 @@ impl BuildRequest {
kept_features
}
pub(crate) fn mobile_org(&self) -> String {
let identifier = self.bundle_identifier();
let mut split = identifier.splitn(3, '.');
let sub = split
.next()
.expect("Identifier to have at least 3 periods like `com.example.app`");
let tld = split
.next()
.expect("Identifier to have at least 3 periods like `com.example.app`");
format!("{}.{}", sub, tld)
}
pub(crate) fn bundled_app_name(&self) -> String {
use convert_case::{Case, Casing};
self.executable_name().to_case(Case::Pascal)
}
pub(crate) fn full_mobile_app_name(&self) -> String {
format!("{}.{}", self.mobile_org(), self.bundled_app_name())
}
pub(crate) fn bundle_identifier(&self) -> String {
if let Some(identifier) = self.config.bundle.identifier.clone() {
return identifier.clone();
if let Some(identifier) = &self.config.bundle.identifier {
if identifier.contains('.')
&& !identifier.starts_with('.')
&& !identifier.ends_with('.')
&& !identifier.contains("..")
{
return identifier.clone();
} else {
// The original `mobile_org` function used `expect` directly.
// Maybe it's acceptable for the CLI to panic directly when this error occurs.
// And if we change it to a Result type, the `client_connected` function in serve/runner.rs does not return a Result and cannot call `?`,
// We also need to handle the error in place, otherwise it will expand the scope of modifications further.
panic!("Invalid bundle identifier: {identifier:?}. E.g. `com.example`, `com.example.app`");
}
}
format!("com.example.{}", self.bundled_app_name())