← Back to all posts

04 Xpense

AndroidManifest.xml Explained

Xpense Dev Retrospective


The Identity Document

Before Android runs a single line of Xpense's code, it reads AndroidManifest.xml first. This file declares what the app is, every screen it has, and what it's allowed to do. If a screen isn't declared here, Android won't launch it, no matter how correct the Kotlin code behind it is.


Xpense's Actual Manifest

XML
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <application
        android:allowBackup="false"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Xpense">

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <activity android:name=".OnboardingActivity" android:exported="false"/>
        <activity android:name=".AddExpenseActivity"/>
        <activity android:name=".HistoryActivity"/>
        <activity android:name=".SettingsActivity"/>
        <activity android:name=".AboutActivity"/>
        <activity android:name=".DataActivity"/>
        <activity android:name=".SettlementsActivity"/>
        <activity android:name=".AddSettlementActivity"/>

    </application>
</manifest>

That's genuinely the whole thing. Nine activities, one application block, no permissions block at all.


The application Block, Line by Line

AttributeWhat it does
android:allowBackup="false"Turns off Android's own automatic cloud backup of app data
android:icon / roundIconThe launcher icon, square and round variants for different launchers
android:labelThe app name shown under the icon, pulled from strings.xml
android:supportsRtlLets layouts mirror correctly for right-to-left languages
android:themePoints to Theme.Xpense, defined separately for light and dark in values/ and values-night/

Why allowBackup is false: this wasn't an oversight, it's deliberate. If it were true, Android would try to auto-backup Xpense's data to the user's Google account in its own generic format, outside my control. Since Xpense already has its own backup and restore feature in DataActivity, with a format I define, letting Android's system also silently back things up on top of that is just an extra thing that could go wrong for no benefit.


Why MainActivity Looks Different

MainActivity is the only activity with an intent-filter. The combination of action.MAIN and category.LAUNCHER is what tells Android "this is the screen that opens when someone taps the app icon." Every other activity in Xpense gets opened from inside the app itself, through an Intent fired from another screen, so none of them need a launcher filter.

About android:exported: since Android 12, any activity with an intent filter must explicitly declare exported, true or false, the OS won't guess anymore. MainActivity needs true since it's the launcher entry point other apps and the OS need to reach. OnboardingActivity explicitly sets false since it has no intent filter and nothing outside the app should ever be able to launch it directly. The rest don't declare it at all, since without an intent filter, not exporting is already the default.


No Permissions Block, on Purpose

There's no <uses-permission> anywhere in the manifest. Xpense doesn't touch the camera, contacts, location, or the internet. Every expense, every settlement, every setting stays entirely on the device. There was never a reason to ask for anything more.


Closing

The manifest says what the app is. Next up is what actually turns the Kotlin files and XML into the APK that manifest describes: Gradle and the build system.

← Previous 03 - Xpense's Project Structure Next → 05 - Gradle and the Build System