DEV Community

Cover image for Creating an Android Config Plugin for Expo | React Native Guide
PEAKIQ
PEAKIQ

Posted on • Originally published at peakiq.in

Creating an Android Config Plugin for Expo | React Native Guide

Originally published on PEAKIQ


Introduction

Expo config plugins allow you to extend and customize the native behavior of your Expo app. Here, we will create a plugin that modifies various aspects of the Android project configuration, including the AndroidManifest.xml, Gradle properties, and MainApplication.java file.

Prerequisites

  • Basic knowledge of JavaScript and Node.js
  • Understanding of Android development concepts
  • Expo CLI installed

Steps to Create the Plugin

1. Setting Up the Plugin

First, ensure you have the necessary imports from @expo/config-plugins:

const { withAndroidManifest, withGradleProperties, withProjectBuildGradle, withAppBuildGradle, withMainApplication } = require("@expo/config-plugins");

Enter fullscreen mode Exit fullscreen mode

2. Defining Configuration Changes

We will define various configuration changes that our plugin will apply:

  • Gradle Properties: Define new Gradle properties to be added to the project.
  • Activities: Add new activities to the AndroidManifest.xml.
  • Intent Data: Add new intent filters to the AndroidManifest.xml.
const new_Gradle_Properties = [
    { type: 'property', key: 'AsyncStorage_db_size_in_MB', value: '2048' },
    { type: 'property', key: 'android.disableAutomaticComponentCreation', value: 'true' },
    { type: 'property', key: 'hermesEnabled', value: 'false' }
];

const new_Activities = [
    { $: { "android:name": 'com.ahmedadeltito.photoeditor.PhotoEditorActivity' } },
    { $: { "android:name": 'com.yalantis.ucrop.UCropActivity' } }
];

const new_intentData = [
    { $: { "android:mimeType": "*/*" } }
];

Enter fullscreen mode Exit fullscreen mode

3. Modifying MainApplication.java

We will modify the MainApplication.java file to include necessary imports and update the onCreate method to change the CursorWindow size:

const ModifyMainApplication = withMainApplication(config, async config => {
    let { contents } = config.modResults;

    if (!contents.includes('import static com.facebook.react.views.textinput.ReactEditText.DEBUG_MODE;')) {
        const packageIndex = contents.indexOf('package ');
        const packageEndIndex = contents.indexOf(';', packageIndex) + 1;

        contents = [
            contents.slice(0, packageEndIndex),
            `\nimport static com.facebook.react.views.textinput.ReactEditText.DEBUG_MODE;
import android.database.CursorWindow;
import java.lang.reflect.Field;\n`,
            contents.slice(packageEndIndex)
        ].join('');
    }

    const onCreateMethodMatch = 'super.onCreate();';
    const tryCatchBlock = `
        try {
            Field field = CursorWindow.class.getDeclaredField("sCursorWindowSize");
            field.setAccessible(true);
            field.set(null, 100 * 1024 * 1024); // 100MB
        } catch (Exception e) {
            if (DEBUG_MODE) {
                e.printStackTrace();
            }
        }
    `;

    if (contents.includes(onCreateMethodMatch) && !contents.includes(tryCatchBlock.trim())) {
        contents = contents.replace(
            onCreateMethodMatch,
            `${onCreateMethodMatch}${tryCatchBlock}`
        );
    }

    config.modResults.contents = contents;
    return config;
});

Enter fullscreen mode Exit fullscreen mode

4. Modifying AndroidManifest.xml

We will add new activities and intent filters to the AndroidManifest.xml:

const AddActivityMain_fest = withAndroidManifest(config, async config => {
    const androidManifest = config.modResults.manifest;
    const mainApplication = androidManifest.application[0];

    mainApplication.$['android:hardwareAccelerated'] = 'true';
    mainApplication.$['android:largeHeap'] = 'true';

    const currentActivities = mainApplication.activity;

    new_Activities.map(activity => {
        const foundActivity = currentActivities.find(anActivity => anActivity.$['android:name'] === activity.$['android:name']);
        if (!foundActivity)
            currentActivities.push(activity);
    });

    const currIntent = androidManifest.queries[0]?.intent[0].data;
    new_intentData.map(intentData => {
        const foundIntentData = currIntent.find(anIntendData => anIntendData.$['android:mimeType'] === intentData.$['android:mimeType']);
        if (!foundIntentData)
            currIntent.push(intentData);
    });
    return config;
});

Enter fullscreen mode Exit fullscreen mode

5. Modifying Gradle Properties

We will set the new Gradle properties:

const SetGradleProperties = withGradleProperties(config, config => {
    new_Gradle_Properties.map(gradleProperty => {
        const foundProperty = config.modResults.find(prop => prop.key === gradleProperty.key);
        if (foundProperty) {
            foundProperty.value = gradleProperty.value;
        } else {
            config.modResults.push(gradleProperty);
        }
    });
    return config;
});

Enter fullscreen mode Exit fullscreen mode

6. Modifying Build Gradle

We will modify the project and app build gradle files:

const SetAppBuildGradleProperties = withProjectBuildGradle(config, config => {
    config.modResults.contents += `
subprojects { subproject ->
    if(project['name'] == 'react-native-reanimated'){
        project.configurations { compile { } }
    }
}`;
    return config;
});

const SetAppBuildGradle = withAppBuildGradle(config, config => {
    config.modResults.contents += `
android { defaultConfig {
    manifestPlaceholders = [appAuthRedirectScheme: 'gopak360']
}}`;
    return config;
});

Enter fullscreen mode Exit fullscreen mode

7. Exporting the Plugin

Finally, we export the plugin by combining all the modifications:

module.exports = function AndroidPlugin(config) {
    return Object.assign(
        SetGradleProperties,
        AddActivityMain_fest,
        SetAppBuildGradleProperties,
        SetAppBuildGradle,
        ModifyMainApplication
    );
};

Enter fullscreen mode Exit fullscreen mode

Conclusion

By following the steps outlined above, you can create a custom Android config plugin for Expo to manage and modify your Android project's configuration efficiently. This approach helps keep your project maintainable and flexible, allowing for easy updates and modifications. For more details and advanced usage, refer to the Expo documentation.

Top comments (0)