This commit is contained in:
Yamozha
2021-04-02 02:24:13 +03:00
parent c23950b545
commit 7256d79e2c
31493 changed files with 3036630 additions and 0 deletions

22
node_modules/@expo/config-plugins/LICENSE generated vendored Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015-present 650 Industries, Inc. (aka Expo)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

469
node_modules/@expo/config-plugins/README.md generated vendored Normal file
View File

@ -0,0 +1,469 @@
# Expo Config Plugins
The Expo config is a powerful tool for generating native app code from a unified JavaScript interface. Most basic functionality can be controlled by using the the [static Expo config](https://docs.expo.io/versions/latest/config/app/), but some features require manipulation of the native project files. To support complex behavior we've created config plugins, and mods (short for modifiers).
> 💡 **Hands-on Learners**: Use [this sandbox][sandbox] to play with the core functionality of Expo config plugins. For more complex tests, use a local Expo project, with `expo eject --no-install` to apply changes.
- [Usage](#usage)
- [What are plugins](#what-are-plugins)
- [Creating a plugin](#creating-a-plugin)
- [Importing plugins](#importing-plugins)
- [Chaining plugins](#chaining-plugins)
- [What are mods](#what-are-mods)
- [How mods works](#how-mods-works)
- [Default mods](#default-mods)
- [Mod plugins](#mod-plugins)
- [Creating a mod](#creating-a-mod)
- [Experimental functionality](#experimental-functionality)
- [Plugin module resolution](#plugin-module-resolution)
- [Project file](#project-file)
- [app.plugin.js](#apppluginjs)
- [Node module default file](#node-module-default-file)
- [Project folder](#project-folder)
- [Module internals](#module-internals)
- [Raw functions](#raw-functions)
- [Why app.plugin.js for plugins](#why-apppluginjs-for-plugins)
**Quick facts**
- Plugins are functions that can change values on your Expo config.
- Plugins are mostly meant to be used with [`expo eject`][cli-eject] or `eas build` commands.
- We recommend you use plugins with `app.config.json` or `app.config.js` instead of `app.json` (no top-level `expo` object is required).
- `mods` are async functions that modify native project files, such as source code or configuration (plist, xml) files.
- Changes performed with `mods` will require rebuilding the affected native projects.
- `mods` are removed from the public app manifest.
- 💡 Everything in the Expo config must be able to be converted to JSON (with the exception of the `mods` field). So no async functions outside of `mods` in your config plugins!
## Usage
Here is a basic config that uses the `expo-splash-screen` plugin:
```json
{
"name": "my app",
"plugins": ["expo-splash-screen"]
}
```
Some plugins can be customized by passing an array, where the second argument is the options:
```json
{
"name": "my app",
"plugins": [
[
"expo-splash-screen",
{
/* Values passed to the plugin */
}
]
]
}
```
If you run `expo eject`, the `mods` will be compiled, and the native files be changed! The changes won't be fully shown until you rebuild the native project with `eas build -p ios` or locally with `npx react-native run-ios` or `npx react-native run-android`.
For instance, if you add a plugin that adds permission messages to your app, the app will need to be rebuilt.
And that's it! Now you're using Config plugins. No more having to interact with the native projects!
> 💡 Check out all the different ways you can import `plugins`: [plugin module resolution](#Plugin-module-resolution)
## What are plugins
Plugins are **synchronous** functions that accept an [`ExpoConfig`][config-docs] and return a modified [`ExpoConfig`][config-docs].
- Plugins should be named using the following convention: `with<Plugin Functionality>` i.e. `withFacebook`.
- Plugins should be synchronous and their return value should be serializable, except for any `mods` that are added.
- Optionally, a second argument can be passed to the plugin to configure it.
- `plugins` are always invoked when the config is read by `@expo/config`s `getConfig` method. However, the `mods` are only invoked during the "syncing" phase of `expo eject`.
## Creating a plugin
> 💡 Hands-on learners: Try this [sandbox](https://codesandbox.io/s/expo-config-plugins-basic-example-xopto?file=/src/project/app.config.js) (check the terminal logs).
Here is an example of the most basic config plugin:
```ts
const withNothing = config => config;
```
Say you wanted to create a plugin which added custom values to the native iOS Info.plist:
```ts
const withMySDK = (config, { apiKey }) => {
// Ensure the objects exist
if (!config.ios) {
config.ios = {};
}
if (!config.ios.infoPlist) {
config.ios.infoPlist = {};
}
// Append the apiKey
config.ios.infoPlist['MY_CUSTOM_NATIVE_IOS_API_KEY'] = apiKey;
return config;
};
// 💡 Usage:
/// Create a config
const config = {
name: 'my app',
};
/// Use the plugin
export default withMySDK(config, { apiKey: 'X-XXX-XXX' });
```
### Importing plugins
You may want to create a plugin in a different file, here's how:
- The root file can be any JS file or a file named `app.plugin.js` in the [root of a Node module](#root-app.plugin.js).
- The file should export a function that satisfies the [`ConfigPlugin`][configplugin] type.
- Plugins should be transpiled for Node environments ahead of time!
- They should support the versions of Node that [Expo supports](https://docs.expo.io/get-started/installation/#requirements) (LTS).
- No `import/export` keywords, use `module.exports` in the shipped plugin file.
- Expo only transpiles the user's initial `app.config` file, anything more would require a bundler which would add too many "opinions" for a config file.
Consider the following example that changes the config name:
```
╭── app.config.js ➡️ Expo Config
╰── my-plugin.js ➡️ Our custom plugin file
```
`my-plugin.js`
```js
module.exports = function withCustomName(config, name) {
// Modify the config
config.name = 'custom-' + name;
// Return the results
return config;
};
```
`app.config.json`
```json
{
"name": "my-app",
"plugins": ["./my-plugin", "app"]
}
```
↓ ↓ ↓
**Evaluated config JSON**
```json
{
"name": "custom-app",
"plugins": ["./my-plugin", "app"]
}
```
### Chaining plugins
Once you add a few plugins, your `app.config.js` code can become difficult to read and manipulate. To combat this, `@expo/config-plugins` provides a `withPlugins` function which can be used to chain plugins together and execute them in order.
```js
/// Create a config
const config = {
name: 'my app',
};
// ❌ Hard to read
withDelta(withFoo(withBar(config, 'input 1'), 'input 2'), 'input 3');
// ✅ Easy to read
import { withPlugins } from '@expo/config-plugins';
withPlugins(config, [
[withBar, 'input 1'],
[withFoo, 'input 2'],
// When no input is required, you can just pass the method...
withDelta,
]);
```
To support JSON configs, we also added the `plugins` array which just uses `withPlugins` under the hood.
Here is the same config as above, but even simpler:
```js
export default {
name: 'my app',
plugins: [
[withBar, 'input 1'],
[withFoo, 'input 2'],
[withDelta, 'input 3'],
],
};
```
## What are mods
An async function which accepts a config and a data object, then manipulates and returns both as an object.
Modifiers (mods for short) are added to the `mods` object of the Expo config. The `mods` object is different to the rest of the Expo config because it doesn't get serialized after the initial reading, this means you can use it to perform actions _during_ code generation. If possible, you should attempt to use basic plugins instead of mods as they're simpler to work with.
- `mods` are omitted from the manifest and **cannot** be accessed via `Updates.manifest`. mods exist for the sole purpose of modifying native files during code generation!
- `mods` can be used to read and write files safely during the `expo eject` command. This is how Expo CLI modifies the Info.plist, entitlements, xcproj, etc...
- `mods` are platform specific and should always be added to a platform specific object:
`app.config.js`
```js
module.exports = {
name: 'my-app',
mods: {
ios: {
/* iOS mods... */
},
android: {
/* Android mods... */
},
},
};
```
## How mods work
- The config is read using `getConfig` from `@expo/config`
- All of the core functionality supported by Expo is added via plugins in `withExpoIOSPlugins`. This is stuff like name, version, icons, locales, etc.
- The config is passed to the compiler `compileModifiersAsync`
- The compiler adds base mods which are responsible for reading data (like `Info.plist`), executing a named mod (like `mods.ios.infoPlist`), then writing the results to the file system.
- The compiler iterates over all of the mods and asynchronously evaluates them, providing some base props like the `projectRoot`.
- After each mod, error handling asserts if the mod chain was corrupted by an invalid mod.
<!-- TODO: Move to a section about mod compiler -->
> 💡 Here is a [colorful chart](https://whimsical.com/UjytoYXT2RN43LywvWExfK) of the mod compiler for visual learners.
### Default mods
The following default mods are provided by the mod compiler for common file manipulation:
- `mods.ios.appDelegate` -- Modify the `ios/<name>/AppDelegate.m` as a string.
- `mods.ios.infoPlist` -- Modify the `ios/<name>/Info.plist` as JSON (parsed with [`@expo/plist`](https://www.npmjs.com/package/@expo/plist)).
- `mods.ios.entitlements` -- Modify the `ios/<name>/<product-name>.entitlements` as JSON (parsed with [`@expo/plist`](https://www.npmjs.com/package/@expo/plist)).
- `mods.ios.expoPlist` -- Modify the `ios/<name>/Expo.plist` as JSON (Expo updates config for iOS) (parsed with [`@expo/plist`](https://www.npmjs.com/package/@expo/plist)).
- `mods.ios.xcodeproj` -- Modify the `ios/<name>.xcodeproj` as an `XcodeProject` object (parsed with [`xcode`](https://www.npmjs.com/package/xcode)).
- `mods.android.manifest` -- Modify the `android/app/src/main/AndroidManifest.xml` as JSON (parsed with [`xml2js`](https://www.npmjs.com/package/xml2js)).
- `mods.android.strings` -- Modify the `android/app/src/main/res/values/strings.xml` as JSON (parsed with [`xml2js`](https://www.npmjs.com/package/xml2js)).
- `mods.android.mainActivity` -- Modify the `android/app/src/main/<package>/MainActivity.java` as a string.
- `mods.android.appBuildGradle` -- Modify the `android/app/build.gradle` as a string.
- `mods.android.projectBuildGradle` -- Modify the `android/build.gradle` as a string.
- `mods.android.settingsGradle` -- Modify the `android/settings.gradle` as a string.
- `mods.android.gradleProperties` -- Modify the `android/gradle.properties` as a `Properties.PropertiesItem[]`.
After the mods are resolved, the contents of each mod will be written to disk. Custom default mods can be added to support new native files.
For example, you can create a mod to support the `GoogleServices-Info.plist`, and pass it to other mods.
### Mod plugins
Mods are responsible for a lot of things, so they can be pretty difficult to understand at first.
If you're developing a feature that requires mods, it's best not to interact with them directly.
Instead you should use the helper mods provided by `@expo/config-plugins`:
- iOS
- `withAppDelegate`
- `withInfoPlist`
- `withEntitlementsPlist`
- `withExpoPlist`
- `withXcodeProject`
- Android
- `withAndroidManifest`
- `withStringsXml`
- `withMainActivity`
- `withProjectBuildGradle`
- `withAppBuildGradle`
- `withSettingsGradle`
- `withGradleProperties`
A mod plugin gets passed a `config` object with additional properties `modResults` and `modRequest` added to it.
- `modResults`: The object to modify and return. The type depends on the mod that's being used.
- `modRequest`: Additional properties supplied by the mod compiler.
- `projectRoot: string`: Project root directory for the universal app.
- `platformProjectRoot: string`: Project root for the specific platform.
- `modName: string`: Name of the mod.
- `platform: ModPlatform`: Name of the platform used in the mods config.
- `projectName?: string`: iOS only: The path component used for querying project files. ex. `projectRoot/ios/[projectName]/`
## Creating a mod
Say you wanted to write a mod to update the Xcode Project's "product name":
```ts
import { ConfigPlugin, withXcodeProject } from '@expo/config-plugins';
const withCustomProductName: ConfigPlugin = (config, customName) => {
return withXcodeProject(config, async config => {
// config = { modResults, modRequest, ...expoConfig }
const xcodeProject = config.modResults;
xcodeProject.productName = customName;
return config;
});
};
// 💡 Usage:
/// Create a config
const config = {
name: 'my app',
};
/// Use the plugin
export default withCustomProductName(config, 'new_name');
```
### Experimental functionality
Some parts of the mod system aren't fully flushed out, these parts use `withDangerousModifier` to read/write data without a base mod. These methods essentially act as their own base mod and cannot be extended. Icons for example, currently use the dangerous mod to perform a single generation step with no ability to customize the results.
```ts
export const withIcons: ConfigPlugin = config => {
return withDangerousModifier(config, async config => {
// No modifications are made to the config
await setIconsAsync(config, config.modRequest.projectRoot);
return config;
});
};
```
Be careful using `withDangerousModifier` as it is subject to change in the future.
The order with which it gets executed is not reliable either.
Currently dangerous mods run first before all other modifiers, this is because we use dangerous mods internally for large file system refactoring like when the package name changes.
## Plugin module resolution
The strings passed to the `plugins` array can be resolved in a few different ways.
> Any resolution pattern that isn't specified below is unexpected behavior, and subject to breaking changes.
### Project file
You can quickly create a plugin in your project and use it in your config.
-`'./my-config-plugin'`
-`'./my-config-plugin.js'`
```
╭── app.config.js ➡️ Expo Config
╰── my-config-plugin.js ➡️ ✅ `module.exports = (config) => config`
```
### app.plugin.js
Sometimes you want your package to export React components and also support a plugin, to do this, multiple entry points need to be used (because the transpilation (Babel preset) may be different).
If a `app.plugin.js` file is present in the root of a Node module's folder, it'll be used instead of the package's `main` file.
-`'expo-splash-screen'`
-`'expo-splash-screen/app.plugin.js'`
```
╭── app.config.js ➡️ Expo Config
╰── node_modules/expo-splash-screen/ ➡️ Module installed from NPM (works with Yarn workspaces as well).
├── package.json ➡️ The `main` file will be used if `app.plugin.js` doesn't exist.
├── app.plugin.js ➡️ ✅ `module.exports = (config) => config` -- must export a function.
╰── build/index.js ➡️ ❌ Ignored because `app.plugin.js` exists. This could be used with `expo-splash-screen/build/index.js`
```
### Node module default file
A config plugin in a node module (without an `app.plugin.js`) will use the `main` file defined in the `package.json`.
-`'expo-splash-screen'`
-`'expo-splash-screen/build/index'`
```
╭── app.config.js ➡️ Expo Config
╰── node_modules/expo-splash-screen/ ➡️ Module installed from NPM (works with Yarn workspaces as well).
├── package.json ➡️ The `main` file points to `build/index.js`
╰── build/index.js ➡️ ✅ Node resolves to this module.
```
### Project folder
-`'./my-config-plugin'`
-`'./my-config-plugin.js'`
This is different to how Node modules work because `app.plugin.js` won't be resolved by default in a directory. You'll have to manually specify `./my-config-plugin/app.plugin.js` to use it, otherwise `index.js` in the directory will be used.
```
╭── app.config.js ➡️ Expo Config
╰── my-config-plugin/ ➡️ Folder containing plugin code
╰── index.js ➡️ ✅ By default, Node resolves a folder's index.js file as the main file.
```
### Module internals
If a file inside a Node module is specified, then the module's root `app.plugin.js` resolution will be skipped. This is referred to as "reaching inside a package" and is considered **bad form**.
We support this to make testing, and plugin authoring easier, but we don't expect library authors to expose their plugins like this as a public API.
-`'expo-splash-screen/build/index.js'`
-`'expo-splash-screen/build'`
```
╭── app.config.js ➡️ Expo Config
╰── node_modules/expo-splash-screen/ ➡️ Module installed from npm (works with Yarn workspaces as well).
├── package.json ➡️ The `main` file will be used if `app.plugin.js` doesn't exist.
├── app.plugin.js ➡️ ❌ Ignored because the reference reaches into the package internals.
╰── build/index.js ➡️ ✅ `module.exports = (config) => config`
```
### Raw functions
You can also just pass in a config plugin.
```js
const withCustom = (config, props) => config;
const config = {
plugins: [
[
withCustom,
{
/* props */
},
],
// Without props
withCustom,
],
};
```
One caveat to using functions instead of strings is that serialization will replace the function with the function's name. This keeps **manifests** (kinda like the `index.html` for your app) working as expected.
Here is what the serialized config would look like:
```json
{
"plugins": [["withCustom", {}], "withCustom"]
}
```
## Why app.plugin.js for plugins
Config resolution searches for a `app.plugin.js` first when a Node module name is provided.
This is because Node environments are often different to iOS, Android, or web JS environments and therefore require different transpilation presets (ex: `module.exports` instead of `import/export`).
Because of this reasoning, the root of a Node module is searched instead of right next to the `index.js`. Imagine you had a TypeScript Node module where the transpiled main file was located at `build/index.js`, if Expo config plugin resolution searched for `build/app.plugin.js` you'd lose the ability to transpile the file differently.
[config-docs]: https://docs.expo.io/versions/latest/config/app/
[cli-eject]: https://docs.expo.io/workflow/expo-cli/#eject
[sandbox]: https://codesandbox.io/s/expo-config-plugins-8qhof?file=/src/project/app.config.js
[configplugin]: ./src/Plugin.types.ts
## Debugging
You can debug config plugins by running `expo prebuild`. If `EXPO_DEBUG` is enabled, the plugin stack logs will be printed, these are useful for viewing which mods ran, and in what order they ran in. To view all static plugin resolution errors, enable `EXPO_CONFIG_PLUGIN_VERBOSE_ERRORS`, this should only be needed for plugin authors.
By default some automatic plugin errors are hidden because they're usually related to versioning issues and aren't very helpful (i.e. legacy package doesn't have a config plugin yet).

View File

@ -0,0 +1,105 @@
import { ExpoConfig } from '@expo/config-types';
import { JSONObject } from '@expo/json-file';
import { XcodeProject } from 'xcode';
import { Properties } from './android';
import { AndroidManifest } from './android/Manifest';
import * as AndroidPaths from './android/Paths';
import { ResourceXML } from './android/Resources';
import { ExpoPlist, InfoPlist } from './ios/IosConfig.types';
import { AppDelegateProjectFile } from './ios/Paths';
declare type OptionalPromise<T> = Promise<T> | T;
declare type Plist = JSONObject;
export interface ModProps<T = any> {
/**
* Project root directory for the universal app.
*/
readonly projectRoot: string;
/**
* Project root for the specific platform.
*/
readonly platformProjectRoot: string;
/**
* Name of the mod.
*/
readonly modName: string;
/**
* Name of the platform used in the mods config.
*/
readonly platform: ModPlatform;
/**
* [iOS]: The path component used for querying project files.
*
* @example projectRoot/ios/[projectName]/
*/
readonly projectName?: string;
nextMod?: Mod<T>;
}
export interface ExportedConfig extends ExpoConfig {
mods?: ModConfig | null;
}
export interface ExportedConfigWithProps<Data = any> extends ExpoConfig {
/**
* The Object representation of a complex file type.
*/
modResults: Data;
modRequest: ModProps<Data>;
}
export declare type ConfigPlugin<Props = void> = (config: ExpoConfig, props: Props) => ExpoConfig;
export declare type StaticPlugin<T = any> = [string | ConfigPlugin<T>, T];
export declare type Mod<Props = any> = (config: ExportedConfigWithProps<Props>) => OptionalPromise<ExportedConfigWithProps<Props>>;
export interface ModConfig {
android?: {
/**
* Modify the `android/app/src/main/AndroidManifest.xml` as JSON (parsed with [`xml2js`](https://www.npmjs.com/package/xml2js)).
*/
manifest?: Mod<AndroidManifest>;
/**
* Modify the `android/app/src/main/res/values/strings.xml` as JSON (parsed with [`xml2js`](https://www.npmjs.com/package/xml2js)).
*/
strings?: Mod<ResourceXML>;
/**
* Modify the `android/app/src/main/<package>/MainActivity.java` as a string.
*/
mainActivity?: Mod<AndroidPaths.ApplicationProjectFile>;
/**
* Modify the `android/app/build.gradle` as a string.
*/
appBuildGradle?: Mod<AndroidPaths.GradleProjectFile>;
/**
* Modify the `android/build.gradle` as a string.
*/
projectBuildGradle?: Mod<AndroidPaths.GradleProjectFile>;
/**
* Modify the `android/settings.gradle` as a string.
*/
settingsGradle?: Mod<AndroidPaths.GradleProjectFile>;
/**
* Modify the `android/gradle.properties` as a `Properties.PropertiesItem[]`.
*/
gradleProperties?: Mod<Properties.PropertiesItem[]>;
};
ios?: {
/**
* Modify the `ios/<name>/Info.plist` as JSON (parsed with [`@expo/plist`](https://www.npmjs.com/package/@expo/plist)).
*/
infoPlist?: Mod<InfoPlist>;
/**
* Modify the `ios/<name>/<product-name>.entitlements` as JSON (parsed with [`@expo/plist`](https://www.npmjs.com/package/@expo/plist)).
*/
entitlements?: Mod<Plist>;
/**
* Modify the `ios/<name>/Expo.plist` as JSON (Expo updates config for iOS) (parsed with [`@expo/plist`](https://www.npmjs.com/package/@expo/plist)).
*/
expoPlist?: Mod<Plist>;
/**
* Modify the `ios/<name>.xcodeproj` as an `XcodeProject` (parsed with [`xcode`](https://www.npmjs.com/package/xcode))
*/
xcodeproj?: Mod<XcodeProject>;
/**
* Modify the `ios/<name>/AppDelegate.m` as a string (dangerous)
*/
appDelegate?: Mod<AppDelegateProjectFile>;
};
}
export declare type ModPlatform = keyof ModConfig;
export { XcodeProject, InfoPlist, ExpoPlist, AndroidManifest };

View File

@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const xcode_1 = require("xcode");
exports.XcodeProject = xcode_1.XcodeProject;
//# sourceMappingURL=Plugin.types.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Plugin.types.js","sourceRoot":"","sources":["../src/Plugin.types.ts"],"names":[],"mappings":";;AAEA,iCAAqC;AA0H5B,uBA1HA,oBAAY,CA0HA","sourcesContent":["import { ExpoConfig } from '@expo/config-types';\nimport { JSONObject } from '@expo/json-file';\nimport { XcodeProject } from 'xcode';\n\nimport { Properties } from './android';\nimport { AndroidManifest } from './android/Manifest';\nimport * as AndroidPaths from './android/Paths';\nimport { ResourceXML } from './android/Resources';\nimport { ExpoPlist, InfoPlist } from './ios/IosConfig.types';\nimport { AppDelegateProjectFile } from './ios/Paths';\n\ntype OptionalPromise<T> = Promise<T> | T;\n\ntype Plist = JSONObject;\n\nexport interface ModProps<T = any> {\n /**\n * Project root directory for the universal app.\n */\n readonly projectRoot: string;\n\n /**\n * Project root for the specific platform.\n */\n readonly platformProjectRoot: string;\n\n /**\n * Name of the mod.\n */\n readonly modName: string;\n\n /**\n * Name of the platform used in the mods config.\n */\n readonly platform: ModPlatform;\n\n /**\n * [iOS]: The path component used for querying project files.\n *\n * @example projectRoot/ios/[projectName]/\n */\n readonly projectName?: string;\n\n nextMod?: Mod<T>;\n}\n\n// TODO: Migrate ProjectConfig to using expo instead if exp\nexport interface ExportedConfig extends ExpoConfig {\n mods?: ModConfig | null;\n}\n\nexport interface ExportedConfigWithProps<Data = any> extends ExpoConfig {\n /**\n * The Object representation of a complex file type.\n */\n modResults: Data;\n modRequest: ModProps<Data>;\n}\n\nexport type ConfigPlugin<Props = void> = (config: ExpoConfig, props: Props) => ExpoConfig;\n\nexport type StaticPlugin<T = any> = [string | ConfigPlugin<T>, T];\n\nexport type Mod<Props = any> = (\n config: ExportedConfigWithProps<Props>\n) => OptionalPromise<ExportedConfigWithProps<Props>>;\n\nexport interface ModConfig {\n android?: {\n /**\n * Modify the `android/app/src/main/AndroidManifest.xml` as JSON (parsed with [`xml2js`](https://www.npmjs.com/package/xml2js)).\n */\n manifest?: Mod<AndroidManifest>;\n /**\n * Modify the `android/app/src/main/res/values/strings.xml` as JSON (parsed with [`xml2js`](https://www.npmjs.com/package/xml2js)).\n */\n strings?: Mod<ResourceXML>;\n /**\n * Modify the `android/app/src/main/<package>/MainActivity.java` as a string.\n */\n mainActivity?: Mod<AndroidPaths.ApplicationProjectFile>;\n /**\n * Modify the `android/app/build.gradle` as a string.\n */\n appBuildGradle?: Mod<AndroidPaths.GradleProjectFile>;\n /**\n * Modify the `android/build.gradle` as a string.\n */\n projectBuildGradle?: Mod<AndroidPaths.GradleProjectFile>;\n /**\n * Modify the `android/settings.gradle` as a string.\n */\n settingsGradle?: Mod<AndroidPaths.GradleProjectFile>;\n /**\n * Modify the `android/gradle.properties` as a `Properties.PropertiesItem[]`.\n */\n gradleProperties?: Mod<Properties.PropertiesItem[]>;\n };\n ios?: {\n /**\n * Modify the `ios/<name>/Info.plist` as JSON (parsed with [`@expo/plist`](https://www.npmjs.com/package/@expo/plist)).\n */\n infoPlist?: Mod<InfoPlist>;\n /**\n * Modify the `ios/<name>/<product-name>.entitlements` as JSON (parsed with [`@expo/plist`](https://www.npmjs.com/package/@expo/plist)).\n */\n entitlements?: Mod<Plist>;\n /**\n * Modify the `ios/<name>/Expo.plist` as JSON (Expo updates config for iOS) (parsed with [`@expo/plist`](https://www.npmjs.com/package/@expo/plist)).\n */\n expoPlist?: Mod<Plist>;\n /**\n * Modify the `ios/<name>.xcodeproj` as an `XcodeProject` (parsed with [`xcode`](https://www.npmjs.com/package/xcode))\n */\n xcodeproj?: Mod<XcodeProject>;\n /**\n * Modify the `ios/<name>/AppDelegate.m` as a string (dangerous)\n */\n appDelegate?: Mod<AppDelegateProjectFile>;\n };\n}\n\nexport type ModPlatform = keyof ModConfig;\n\nexport { XcodeProject, InfoPlist, ExpoPlist, AndroidManifest };\n"]}

View File

@ -0,0 +1,6 @@
import { ExpoConfig } from '@expo/config-types';
import { AndroidManifest } from './Manifest';
export declare const withAdMob: import("..").ConfigPlugin<void>;
export declare function getGoogleMobileAdsAppId(config: Pick<ExpoConfig, 'android'>): string | null;
export declare function getGoogleMobileAdsAutoInit(config: Pick<ExpoConfig, 'android'>): boolean;
export declare function setAdMobConfig(config: Pick<ExpoConfig, 'android'>, androidManifest: AndroidManifest): AndroidManifest;

View File

@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const android_plugins_1 = require("../plugins/android-plugins");
const Manifest_1 = require("./Manifest");
const META_APPLICATION_ID = 'com.google.android.gms.ads.APPLICATION_ID';
const META_DELAY_APP_MEASUREMENT_INIT = 'com.google.android.gms.ads.DELAY_APP_MEASUREMENT_INIT';
exports.withAdMob = android_plugins_1.createAndroidManifestPlugin(setAdMobConfig, 'withAdMob');
function getGoogleMobileAdsAppId(config) {
var _a, _b, _c;
return (_c = (_b = (_a = config.android) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.googleMobileAdsAppId) !== null && _c !== void 0 ? _c : null;
}
exports.getGoogleMobileAdsAppId = getGoogleMobileAdsAppId;
function getGoogleMobileAdsAutoInit(config) {
var _a, _b, _c;
return (_c = (_b = (_a = config.android) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.googleMobileAdsAutoInit) !== null && _c !== void 0 ? _c : false;
}
exports.getGoogleMobileAdsAutoInit = getGoogleMobileAdsAutoInit;
function setAdMobConfig(config, androidManifest) {
const appId = getGoogleMobileAdsAppId(config);
const autoInit = getGoogleMobileAdsAutoInit(config);
const mainApplication = Manifest_1.getMainApplicationOrThrow(androidManifest);
if (appId) {
Manifest_1.addMetaDataItemToMainApplication(mainApplication, META_APPLICATION_ID, appId);
Manifest_1.addMetaDataItemToMainApplication(mainApplication, META_DELAY_APP_MEASUREMENT_INIT, String(!autoInit));
}
else {
Manifest_1.removeMetaDataItemFromMainApplication(mainApplication, META_APPLICATION_ID);
Manifest_1.removeMetaDataItemFromMainApplication(mainApplication, META_DELAY_APP_MEASUREMENT_INIT);
}
return androidManifest;
}
exports.setAdMobConfig = setAdMobConfig;
//# sourceMappingURL=AdMob.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"AdMob.js","sourceRoot":"","sources":["../../src/android/AdMob.ts"],"names":[],"mappings":";;AAEA,gEAAyE;AACzE,yCAKoB;AAEpB,MAAM,mBAAmB,GAAG,2CAA2C,CAAC;AACxE,MAAM,+BAA+B,GAAG,uDAAuD,CAAC;AAEnF,QAAA,SAAS,GAAG,6CAA2B,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;AAElF,SAAgB,uBAAuB,CAAC,MAAmC;;IACzE,yBAAO,MAAM,CAAC,OAAO,0CAAE,MAAM,0CAAE,oBAAoB,mCAAI,IAAI,CAAC;AAC9D,CAAC;AAFD,0DAEC;AAED,SAAgB,0BAA0B,CAAC,MAAmC;;IAC5E,yBAAO,MAAM,CAAC,OAAO,0CAAE,MAAM,0CAAE,uBAAuB,mCAAI,KAAK,CAAC;AAClE,CAAC;AAFD,gEAEC;AAED,SAAgB,cAAc,CAC5B,MAAmC,EACnC,eAAgC;IAEhC,MAAM,KAAK,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,0BAA0B,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,eAAe,GAAG,oCAAyB,CAAC,eAAe,CAAC,CAAC;IAEnE,IAAI,KAAK,EAAE;QACT,2CAAgC,CAAC,eAAe,EAAE,mBAAmB,EAAE,KAAK,CAAC,CAAC;QAC9E,2CAAgC,CAC9B,eAAe,EACf,+BAA+B,EAC/B,MAAM,CAAC,CAAC,QAAQ,CAAC,CAClB,CAAC;KACH;SAAM;QACL,gDAAqC,CAAC,eAAe,EAAE,mBAAmB,CAAC,CAAC;QAC5E,gDAAqC,CAAC,eAAe,EAAE,+BAA+B,CAAC,CAAC;KACzF;IAED,OAAO,eAAe,CAAC;AACzB,CAAC;AArBD,wCAqBC","sourcesContent":["import { ExpoConfig } from '@expo/config-types';\n\nimport { createAndroidManifestPlugin } from '../plugins/android-plugins';\nimport {\n addMetaDataItemToMainApplication,\n AndroidManifest,\n getMainApplicationOrThrow,\n removeMetaDataItemFromMainApplication,\n} from './Manifest';\n\nconst META_APPLICATION_ID = 'com.google.android.gms.ads.APPLICATION_ID';\nconst META_DELAY_APP_MEASUREMENT_INIT = 'com.google.android.gms.ads.DELAY_APP_MEASUREMENT_INIT';\n\nexport const withAdMob = createAndroidManifestPlugin(setAdMobConfig, 'withAdMob');\n\nexport function getGoogleMobileAdsAppId(config: Pick<ExpoConfig, 'android'>) {\n return config.android?.config?.googleMobileAdsAppId ?? null;\n}\n\nexport function getGoogleMobileAdsAutoInit(config: Pick<ExpoConfig, 'android'>) {\n return config.android?.config?.googleMobileAdsAutoInit ?? false;\n}\n\nexport function setAdMobConfig(\n config: Pick<ExpoConfig, 'android'>,\n androidManifest: AndroidManifest\n) {\n const appId = getGoogleMobileAdsAppId(config);\n const autoInit = getGoogleMobileAdsAutoInit(config);\n const mainApplication = getMainApplicationOrThrow(androidManifest);\n\n if (appId) {\n addMetaDataItemToMainApplication(mainApplication, META_APPLICATION_ID, appId);\n addMetaDataItemToMainApplication(\n mainApplication,\n META_DELAY_APP_MEASUREMENT_INIT,\n String(!autoInit)\n );\n } else {\n removeMetaDataItemFromMainApplication(mainApplication, META_APPLICATION_ID);\n removeMetaDataItemFromMainApplication(mainApplication, META_DELAY_APP_MEASUREMENT_INIT);\n }\n\n return androidManifest;\n}\n"]}

View File

@ -0,0 +1,6 @@
import { ExpoConfig } from '@expo/config-types';
import { AndroidManifest } from './Manifest';
export declare const withAllowBackup: import("..").ConfigPlugin<void>;
export declare function getAllowBackup(config: Pick<ExpoConfig, 'android'>): boolean;
export declare function setAllowBackup(config: Pick<ExpoConfig, 'android'>, androidManifest: AndroidManifest): AndroidManifest;
export declare function getAllowBackupFromManifest(androidManifest: AndroidManifest): boolean | null;

View File

@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const android_plugins_1 = require("../plugins/android-plugins");
const Manifest_1 = require("./Manifest");
exports.withAllowBackup = android_plugins_1.createAndroidManifestPlugin(setAllowBackup, 'withAllowBackup');
function getAllowBackup(config) {
var _a, _b;
// Defaults to true.
// https://docs.expo.io/versions/latest/config/app/#allowbackup
return (_b = (_a = config.android) === null || _a === void 0 ? void 0 : _a.allowBackup) !== null && _b !== void 0 ? _b : true;
}
exports.getAllowBackup = getAllowBackup;
function setAllowBackup(config, androidManifest) {
const allowBackup = getAllowBackup(config);
const mainApplication = Manifest_1.getMainApplication(androidManifest);
if (mainApplication === null || mainApplication === void 0 ? void 0 : mainApplication.$) {
mainApplication.$['android:allowBackup'] = String(allowBackup);
}
return androidManifest;
}
exports.setAllowBackup = setAllowBackup;
function getAllowBackupFromManifest(androidManifest) {
const mainApplication = Manifest_1.getMainApplication(androidManifest);
if (mainApplication === null || mainApplication === void 0 ? void 0 : mainApplication.$) {
return String(mainApplication.$['android:allowBackup']) === 'true';
}
return null;
}
exports.getAllowBackupFromManifest = getAllowBackupFromManifest;
//# sourceMappingURL=AllowBackup.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"AllowBackup.js","sourceRoot":"","sources":["../../src/android/AllowBackup.ts"],"names":[],"mappings":";;AAEA,gEAAyE;AACzE,yCAAgF;AAEnE,QAAA,eAAe,GAAG,6CAA2B,CAAC,cAAc,EAAE,iBAAiB,CAAC,CAAC;AAE9F,SAAgB,cAAc,CAAC,MAAmC;;IAChE,oBAAoB;IACpB,+DAA+D;IAC/D,mBAAO,MAAM,CAAC,OAAO,0CAAE,WAAW,mCAAI,IAAI,CAAC;AAC7C,CAAC;AAJD,wCAIC;AAED,SAAgB,cAAc,CAC5B,MAAmC,EACnC,eAAgC;IAEhC,MAAM,WAAW,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IAE3C,MAAM,eAAe,GAAG,6BAAkB,CAAC,eAAe,CAAC,CAAC;IAC5D,IAAI,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,CAAC,EAAE;QACtB,eAAe,CAAC,CAAC,CAAC,qBAAqB,CAAC,GAAG,MAAM,CAAC,WAAW,CAAkB,CAAC;KACjF;IAED,OAAO,eAAe,CAAC;AACzB,CAAC;AAZD,wCAYC;AAED,SAAgB,0BAA0B,CAAC,eAAgC;IACzE,MAAM,eAAe,GAAG,6BAAkB,CAAC,eAAe,CAAC,CAAC;IAE5D,IAAI,eAAe,aAAf,eAAe,uBAAf,eAAe,CAAE,CAAC,EAAE;QACtB,OAAO,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,KAAK,MAAM,CAAC;KACpE;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AARD,gEAQC","sourcesContent":["import { ExpoConfig } from '@expo/config-types';\n\nimport { createAndroidManifestPlugin } from '../plugins/android-plugins';\nimport { AndroidManifest, getMainApplication, StringBoolean } from './Manifest';\n\nexport const withAllowBackup = createAndroidManifestPlugin(setAllowBackup, 'withAllowBackup');\n\nexport function getAllowBackup(config: Pick<ExpoConfig, 'android'>) {\n // Defaults to true.\n // https://docs.expo.io/versions/latest/config/app/#allowbackup\n return config.android?.allowBackup ?? true;\n}\n\nexport function setAllowBackup(\n config: Pick<ExpoConfig, 'android'>,\n androidManifest: AndroidManifest\n) {\n const allowBackup = getAllowBackup(config);\n\n const mainApplication = getMainApplication(androidManifest);\n if (mainApplication?.$) {\n mainApplication.$['android:allowBackup'] = String(allowBackup) as StringBoolean;\n }\n\n return androidManifest;\n}\n\nexport function getAllowBackupFromManifest(androidManifest: AndroidManifest): boolean | null {\n const mainApplication = getMainApplication(androidManifest);\n\n if (mainApplication?.$) {\n return String(mainApplication.$['android:allowBackup']) === 'true';\n }\n\n return null;\n}\n"]}

View File

@ -0,0 +1,5 @@
import { ExpoConfig } from '@expo/config-types';
import { AndroidManifest } from './Manifest';
export declare const withBranch: import("..").ConfigPlugin<void>;
export declare function getBranchApiKey(config: ExpoConfig): string | null;
export declare function setBranchApiKey(config: ExpoConfig, androidManifest: AndroidManifest): AndroidManifest;

View File

@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const android_plugins_1 = require("../plugins/android-plugins");
const Manifest_1 = require("./Manifest");
const META_BRANCH_KEY = 'io.branch.sdk.BranchKey';
exports.withBranch = android_plugins_1.createAndroidManifestPlugin(setBranchApiKey, 'withBranch');
function getBranchApiKey(config) {
var _a, _b, _c, _d;
return (_d = (_c = (_b = (_a = config.android) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.branch) === null || _c === void 0 ? void 0 : _c.apiKey) !== null && _d !== void 0 ? _d : null;
}
exports.getBranchApiKey = getBranchApiKey;
function setBranchApiKey(config, androidManifest) {
const apiKey = getBranchApiKey(config);
const mainApplication = Manifest_1.getMainApplicationOrThrow(androidManifest);
if (apiKey) {
// If the item exists, add it back
Manifest_1.addMetaDataItemToMainApplication(mainApplication, META_BRANCH_KEY, apiKey);
}
else {
// Remove any existing item
Manifest_1.removeMetaDataItemFromMainApplication(mainApplication, META_BRANCH_KEY);
}
return androidManifest;
}
exports.setBranchApiKey = setBranchApiKey;
//# sourceMappingURL=Branch.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Branch.js","sourceRoot":"","sources":["../../src/android/Branch.ts"],"names":[],"mappings":";;AAEA,gEAAyE;AACzE,yCAKoB;AAEpB,MAAM,eAAe,GAAG,yBAAyB,CAAC;AAErC,QAAA,UAAU,GAAG,6CAA2B,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC;AAErF,SAAgB,eAAe,CAAC,MAAkB;;IAChD,+BAAO,MAAM,CAAC,OAAO,0CAAE,MAAM,0CAAE,MAAM,0CAAE,MAAM,mCAAI,IAAI,CAAC;AACxD,CAAC;AAFD,0CAEC;AAED,SAAgB,eAAe,CAAC,MAAkB,EAAE,eAAgC;IAClF,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAEvC,MAAM,eAAe,GAAG,oCAAyB,CAAC,eAAe,CAAC,CAAC;IAEnE,IAAI,MAAM,EAAE;QACV,kCAAkC;QAClC,2CAAgC,CAAC,eAAe,EAAE,eAAe,EAAE,MAAM,CAAC,CAAC;KAC5E;SAAM;QACL,2BAA2B;QAC3B,gDAAqC,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC;KACzE;IACD,OAAO,eAAe,CAAC;AACzB,CAAC;AAbD,0CAaC","sourcesContent":["import { ExpoConfig } from '@expo/config-types';\n\nimport { createAndroidManifestPlugin } from '../plugins/android-plugins';\nimport {\n addMetaDataItemToMainApplication,\n AndroidManifest,\n getMainApplicationOrThrow,\n removeMetaDataItemFromMainApplication,\n} from './Manifest';\n\nconst META_BRANCH_KEY = 'io.branch.sdk.BranchKey';\n\nexport const withBranch = createAndroidManifestPlugin(setBranchApiKey, 'withBranch');\n\nexport function getBranchApiKey(config: ExpoConfig) {\n return config.android?.config?.branch?.apiKey ?? null;\n}\n\nexport function setBranchApiKey(config: ExpoConfig, androidManifest: AndroidManifest) {\n const apiKey = getBranchApiKey(config);\n\n const mainApplication = getMainApplicationOrThrow(androidManifest);\n\n if (apiKey) {\n // If the item exists, add it back\n addMetaDataItemToMainApplication(mainApplication, META_BRANCH_KEY, apiKey);\n } else {\n // Remove any existing item\n removeMetaDataItemFromMainApplication(mainApplication, META_BRANCH_KEY);\n }\n return androidManifest;\n}\n"]}

View File

@ -0,0 +1,6 @@
import { ResourceItemXML, ResourceKind, ResourceXML } from './Resources';
export declare function getProjectColorsXMLPathAsync(projectRoot: string, { kind }?: {
kind?: ResourceKind;
}): Promise<string>;
export declare function setColorItem(itemToAdd: ResourceItemXML, colorFileContentsJSON: ResourceXML): ResourceXML;
export declare function removeColorItem(named: string, contents: ResourceXML): ResourceXML;

View File

@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Paths_1 = require("./Paths");
function getProjectColorsXMLPathAsync(projectRoot, { kind } = {}) {
return Paths_1.getResourceXMLPathAsync(projectRoot, { kind, name: 'colors' });
}
exports.getProjectColorsXMLPathAsync = getProjectColorsXMLPathAsync;
function setColorItem(itemToAdd, colorFileContentsJSON) {
var _a;
if ((_a = colorFileContentsJSON.resources) === null || _a === void 0 ? void 0 : _a.color) {
const colorNameExists = colorFileContentsJSON.resources.color.filter((e) => e.$.name === itemToAdd.$.name)[0];
if (colorNameExists) {
colorNameExists._ = itemToAdd._;
}
else {
colorFileContentsJSON.resources.color.push(itemToAdd);
}
}
else {
if (!colorFileContentsJSON.resources || typeof colorFileContentsJSON.resources === 'string') {
//file was empty and JSON is `{resources : ''}`
colorFileContentsJSON.resources = {};
}
colorFileContentsJSON.resources.color = [itemToAdd];
}
return colorFileContentsJSON;
}
exports.setColorItem = setColorItem;
function removeColorItem(named, contents) {
var _a;
if ((_a = contents.resources) === null || _a === void 0 ? void 0 : _a.color) {
const index = contents.resources.color.findIndex((e) => e.$.name === named);
if (index > -1) {
// replace the previous value
contents.resources.color.splice(index, 1);
}
}
return contents;
}
exports.removeColorItem = removeColorItem;
//# sourceMappingURL=Colors.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Colors.js","sourceRoot":"","sources":["../../src/android/Colors.ts"],"names":[],"mappings":";;AAAA,mCAAkD;AAGlD,SAAgB,4BAA4B,CAC1C,WAAmB,EACnB,EAAE,IAAI,KAA8B,EAAE;IAEtC,OAAO,+BAAuB,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;AACxE,CAAC;AALD,oEAKC;AAED,SAAgB,YAAY,CAAC,SAA0B,EAAE,qBAAkC;;IACzF,UAAI,qBAAqB,CAAC,SAAS,0CAAE,KAAK,EAAE;QAC1C,MAAM,eAAe,GAAG,qBAAqB,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAClE,CAAC,CAAkB,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CACtD,CAAC,CAAC,CAAC,CAAC;QACL,IAAI,eAAe,EAAE;YACnB,eAAe,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC;SACjC;aAAM;YACL,qBAAqB,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;SACvD;KACF;SAAM;QACL,IAAI,CAAC,qBAAqB,CAAC,SAAS,IAAI,OAAO,qBAAqB,CAAC,SAAS,KAAK,QAAQ,EAAE;YAC3F,+CAA+C;YAC/C,qBAAqB,CAAC,SAAS,GAAG,EAAE,CAAC;SACtC;QACD,qBAAqB,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,SAAS,CAAC,CAAC;KACrD;IACD,OAAO,qBAAqB,CAAC;AAC/B,CAAC;AAlBD,oCAkBC;AAED,SAAgB,eAAe,CAAC,KAAa,EAAE,QAAqB;;IAClE,UAAI,QAAQ,CAAC,SAAS,0CAAE,KAAK,EAAE;QAC7B,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAkB,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC;QAC7F,IAAI,KAAK,GAAG,CAAC,CAAC,EAAE;YACd,6BAA6B;YAC7B,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;SAC3C;KACF;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AATD,0CASC","sourcesContent":["import { getResourceXMLPathAsync } from './Paths';\nimport { ResourceItemXML, ResourceKind, ResourceXML } from './Resources';\n\nexport function getProjectColorsXMLPathAsync(\n projectRoot: string,\n { kind }: { kind?: ResourceKind } = {}\n) {\n return getResourceXMLPathAsync(projectRoot, { kind, name: 'colors' });\n}\n\nexport function setColorItem(itemToAdd: ResourceItemXML, colorFileContentsJSON: ResourceXML) {\n if (colorFileContentsJSON.resources?.color) {\n const colorNameExists = colorFileContentsJSON.resources.color.filter(\n (e: ResourceItemXML) => e.$.name === itemToAdd.$.name\n )[0];\n if (colorNameExists) {\n colorNameExists._ = itemToAdd._;\n } else {\n colorFileContentsJSON.resources.color.push(itemToAdd);\n }\n } else {\n if (!colorFileContentsJSON.resources || typeof colorFileContentsJSON.resources === 'string') {\n //file was empty and JSON is `{resources : ''}`\n colorFileContentsJSON.resources = {};\n }\n colorFileContentsJSON.resources.color = [itemToAdd];\n }\n return colorFileContentsJSON;\n}\n\nexport function removeColorItem(named: string, contents: ResourceXML) {\n if (contents.resources?.color) {\n const index = contents.resources.color.findIndex((e: ResourceItemXML) => e.$.name === named);\n if (index > -1) {\n // replace the previous value\n contents.resources.color.splice(index, 1);\n }\n }\n return contents;\n}\n"]}

View File

@ -0,0 +1,3 @@
export declare function getEasBuildGradlePath(projectRoot: string): string;
export declare function configureEasBuildAsync(projectRoot: string): Promise<void>;
export declare function isEasBuildGradleConfiguredAsync(projectRoot: string): Promise<boolean>;

View File

@ -0,0 +1,49 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const EasBuildGradleScript_1 = __importDefault(require("./EasBuildGradleScript"));
const Paths = __importStar(require("./Paths"));
const APPLY_EAS_GRADLE = 'apply from: "./eas-build.gradle"';
function hasApplyLine(content, applyLine) {
return (content
.replace(/\r\n/g, '\n')
.split('\n')
// Check for both single and double quotes
.some(line => line === applyLine || line === applyLine.replace(/"/g, "'")));
}
function getEasBuildGradlePath(projectRoot) {
return path_1.default.join(projectRoot, 'android', 'app', 'eas-build.gradle');
}
exports.getEasBuildGradlePath = getEasBuildGradlePath;
async function configureEasBuildAsync(projectRoot) {
const buildGradlePath = Paths.getAppBuildGradle(projectRoot);
const easGradlePath = getEasBuildGradlePath(projectRoot);
await fs_extra_1.default.writeFile(easGradlePath, EasBuildGradleScript_1.default);
const buildGradleContent = await fs_extra_1.default.readFile(path_1.default.join(buildGradlePath), 'utf8');
const hasEasGradleApply = hasApplyLine(buildGradleContent, APPLY_EAS_GRADLE);
if (!hasEasGradleApply) {
await fs_extra_1.default.writeFile(buildGradlePath, `${buildGradleContent.trim()}\n${APPLY_EAS_GRADLE}\n`);
}
}
exports.configureEasBuildAsync = configureEasBuildAsync;
async function isEasBuildGradleConfiguredAsync(projectRoot) {
const buildGradlePath = Paths.getAppBuildGradle(projectRoot);
const easGradlePath = getEasBuildGradlePath(projectRoot);
const hasEasGradleFile = await fs_extra_1.default.pathExists(easGradlePath);
const buildGradleContent = await fs_extra_1.default.readFile(path_1.default.join(buildGradlePath), 'utf8');
const hasEasGradleApply = hasApplyLine(buildGradleContent, APPLY_EAS_GRADLE);
return hasEasGradleApply && hasEasGradleFile;
}
exports.isEasBuildGradleConfiguredAsync = isEasBuildGradleConfiguredAsync;
//# sourceMappingURL=EasBuild.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"EasBuild.js","sourceRoot":"","sources":["../../src/android/EasBuild.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,wDAA0B;AAC1B,gDAAwB;AAExB,kFAAkD;AAClD,+CAAiC;AAEjC,MAAM,gBAAgB,GAAG,kCAAkC,CAAC;AAE5D,SAAS,YAAY,CAAC,OAAe,EAAE,SAAiB;IACtD,OAAO,CACL,OAAO;SACJ,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC;SACtB,KAAK,CAAC,IAAI,CAAC;QACZ,0CAA0C;SACzC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAC7E,CAAC;AACJ,CAAC;AAED,SAAgB,qBAAqB,CAAC,WAAmB;IACvD,OAAO,cAAI,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,kBAAkB,CAAC,CAAC;AACtE,CAAC;AAFD,sDAEC;AAEM,KAAK,UAAU,sBAAsB,CAAC,WAAmB;IAC9D,MAAM,eAAe,GAAG,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAC7D,MAAM,aAAa,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;IAEzD,MAAM,kBAAE,CAAC,SAAS,CAAC,aAAa,EAAE,8BAAY,CAAC,CAAC;IAEhD,MAAM,kBAAkB,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,cAAI,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC,CAAC;IAEjF,MAAM,iBAAiB,GAAG,YAAY,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,CAAC;IAE7E,IAAI,CAAC,iBAAiB,EAAE;QACtB,MAAM,kBAAE,CAAC,SAAS,CAAC,eAAe,EAAE,GAAG,kBAAkB,CAAC,IAAI,EAAE,KAAK,gBAAgB,IAAI,CAAC,CAAC;KAC5F;AACH,CAAC;AAbD,wDAaC;AAEM,KAAK,UAAU,+BAA+B,CAAC,WAAmB;IACvE,MAAM,eAAe,GAAG,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAC7D,MAAM,aAAa,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;IAEzD,MAAM,gBAAgB,GAAG,MAAM,kBAAE,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IAE5D,MAAM,kBAAkB,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,cAAI,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC,CAAC;IACjF,MAAM,iBAAiB,GAAG,YAAY,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,CAAC;IAE7E,OAAO,iBAAiB,IAAI,gBAAgB,CAAC;AAC/C,CAAC;AAVD,0EAUC","sourcesContent":["import fs from 'fs-extra';\nimport path from 'path';\n\nimport gradleScript from './EasBuildGradleScript';\nimport * as Paths from './Paths';\n\nconst APPLY_EAS_GRADLE = 'apply from: \"./eas-build.gradle\"';\n\nfunction hasApplyLine(content: string, applyLine: string): boolean {\n return (\n content\n .replace(/\\r\\n/g, '\\n')\n .split('\\n')\n // Check for both single and double quotes\n .some(line => line === applyLine || line === applyLine.replace(/\"/g, \"'\"))\n );\n}\n\nexport function getEasBuildGradlePath(projectRoot: string): string {\n return path.join(projectRoot, 'android', 'app', 'eas-build.gradle');\n}\n\nexport async function configureEasBuildAsync(projectRoot: string): Promise<void> {\n const buildGradlePath = Paths.getAppBuildGradle(projectRoot);\n const easGradlePath = getEasBuildGradlePath(projectRoot);\n\n await fs.writeFile(easGradlePath, gradleScript);\n\n const buildGradleContent = await fs.readFile(path.join(buildGradlePath), 'utf8');\n\n const hasEasGradleApply = hasApplyLine(buildGradleContent, APPLY_EAS_GRADLE);\n\n if (!hasEasGradleApply) {\n await fs.writeFile(buildGradlePath, `${buildGradleContent.trim()}\\n${APPLY_EAS_GRADLE}\\n`);\n }\n}\n\nexport async function isEasBuildGradleConfiguredAsync(projectRoot: string): Promise<boolean> {\n const buildGradlePath = Paths.getAppBuildGradle(projectRoot);\n const easGradlePath = getEasBuildGradlePath(projectRoot);\n\n const hasEasGradleFile = await fs.pathExists(easGradlePath);\n\n const buildGradleContent = await fs.readFile(path.join(buildGradlePath), 'utf8');\n const hasEasGradleApply = hasApplyLine(buildGradleContent, APPLY_EAS_GRADLE);\n\n return hasEasGradleApply && hasEasGradleFile;\n}\n"]}

View File

@ -0,0 +1,2 @@
declare const _default: "// Build integration with EAS\n\nimport java.nio.file.Paths\n\nandroid {\n signingConfigs {\n release {\n // This is necessary to avoid needing the user to define a release signing config manually\n // If no release config is defined, and this is not present, build for assembleRelease will crash\n }\n }\n\n buildTypes {\n release {\n // This is necessary to avoid needing the user to define a release build type manually\n }\n }\n}\n\ndef isEasBuildConfigured = false\n\ntasks.whenTaskAdded {\n def debug = gradle.startParameter.taskNames.any { it.toLowerCase().contains('debug') }\n\n if (debug) {\n return\n }\n\n // We only need to configure EAS build once\n if (isEasBuildConfigured) {\n return\n }\n\n isEasBuildConfigured = true;\n\n android.signingConfigs.release {\n def credentialsJson = rootProject.file(\"../credentials.json\");\n\n if (credentialsJson.exists()) {\n if (storeFile && System.getenv(\"EAS_BUILD\") != \"true\") {\n println(\"Path to release keystore file is already set, ignoring 'credentials.json'\")\n } else {\n try {\n def credentials = new groovy.json.JsonSlurper().parse(credentialsJson)\n def keystorePath = Paths.get(credentials.android.keystore.keystorePath);\n def storeFilePath = keystorePath.isAbsolute()\n ? keystorePath\n : rootProject.file(\"..\").toPath().resolve(keystorePath);\n\n storeFile storeFilePath.toFile()\n storePassword credentials.android.keystore.keystorePassword\n keyAlias credentials.android.keystore.keyAlias\n keyPassword credentials.android.keystore.keyPassword\n } catch (Exception e) {\n println(\"An error occurred while parsing 'credentials.json': \" + e.message)\n }\n }\n } else {\n if (storeFile == null) {\n println(\"Couldn't find a 'credentials.json' file, skipping release keystore configuration\")\n }\n }\n }\n\n android.buildTypes.release {\n signingConfig android.signingConfigs.release\n }\n}\n";
export default _default;

View File

@ -0,0 +1,72 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = `// Build integration with EAS
import java.nio.file.Paths
android {
signingConfigs {
release {
// This is necessary to avoid needing the user to define a release signing config manually
// If no release config is defined, and this is not present, build for assembleRelease will crash
}
}
buildTypes {
release {
// This is necessary to avoid needing the user to define a release build type manually
}
}
}
def isEasBuildConfigured = false
tasks.whenTaskAdded {
def debug = gradle.startParameter.taskNames.any { it.toLowerCase().contains('debug') }
if (debug) {
return
}
// We only need to configure EAS build once
if (isEasBuildConfigured) {
return
}
isEasBuildConfigured = true;
android.signingConfigs.release {
def credentialsJson = rootProject.file("../credentials.json");
if (credentialsJson.exists()) {
if (storeFile && System.getenv("EAS_BUILD") != "true") {
println("Path to release keystore file is already set, ignoring 'credentials.json'")
} else {
try {
def credentials = new groovy.json.JsonSlurper().parse(credentialsJson)
def keystorePath = Paths.get(credentials.android.keystore.keystorePath);
def storeFilePath = keystorePath.isAbsolute()
? keystorePath
: rootProject.file("..").toPath().resolve(keystorePath);
storeFile storeFilePath.toFile()
storePassword credentials.android.keystore.keystorePassword
keyAlias credentials.android.keystore.keyAlias
keyPassword credentials.android.keystore.keyPassword
} catch (Exception e) {
println("An error occurred while parsing 'credentials.json': " + e.message)
}
}
} else {
if (storeFile == null) {
println("Couldn't find a 'credentials.json' file, skipping release keystore configuration")
}
}
}
android.buildTypes.release {
signingConfig android.signingConfigs.release
}
}
`;
//# sourceMappingURL=EasBuildGradleScript.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"EasBuildGradleScript.js","sourceRoot":"","sources":["../../src/android/EasBuildGradleScript.ts"],"names":[],"mappings":";;AAAA,kBAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoEd,CAAC","sourcesContent":["export default `// Build integration with EAS\n\nimport java.nio.file.Paths\n\nandroid {\n signingConfigs {\n release {\n // This is necessary to avoid needing the user to define a release signing config manually\n // If no release config is defined, and this is not present, build for assembleRelease will crash\n }\n }\n\n buildTypes {\n release {\n // This is necessary to avoid needing the user to define a release build type manually\n }\n }\n}\n\ndef isEasBuildConfigured = false\n\ntasks.whenTaskAdded {\n def debug = gradle.startParameter.taskNames.any { it.toLowerCase().contains('debug') }\n\n if (debug) {\n return\n }\n\n // We only need to configure EAS build once\n if (isEasBuildConfigured) {\n return\n }\n\n isEasBuildConfigured = true;\n\n android.signingConfigs.release {\n def credentialsJson = rootProject.file(\"../credentials.json\");\n\n if (credentialsJson.exists()) {\n if (storeFile && System.getenv(\"EAS_BUILD\") != \"true\") {\n println(\"Path to release keystore file is already set, ignoring 'credentials.json'\")\n } else {\n try {\n def credentials = new groovy.json.JsonSlurper().parse(credentialsJson)\n def keystorePath = Paths.get(credentials.android.keystore.keystorePath);\n def storeFilePath = keystorePath.isAbsolute()\n ? keystorePath\n : rootProject.file(\"..\").toPath().resolve(keystorePath);\n\n storeFile storeFilePath.toFile()\n storePassword credentials.android.keystore.keystorePassword\n keyAlias credentials.android.keystore.keyAlias\n keyPassword credentials.android.keystore.keyPassword\n } catch (Exception e) {\n println(\"An error occurred while parsing 'credentials.json': \" + e.message)\n }\n }\n } else {\n if (storeFile == null) {\n println(\"Couldn't find a 'credentials.json' file, skipping release keystore configuration\")\n }\n }\n }\n\n android.buildTypes.release {\n signingConfig android.signingConfigs.release\n }\n}\n`;\n"]}

View File

@ -0,0 +1,14 @@
import { ExpoConfig } from '@expo/config-types';
import { AndroidManifest } from './Manifest';
export declare const withFacebookAppIdString: import("..").ConfigPlugin<void>;
export declare const withFacebookManifest: import("..").ConfigPlugin<void>;
declare type ExpoConfigFacebook = Pick<ExpoConfig, 'facebookScheme' | 'facebookAdvertiserIDCollectionEnabled' | 'facebookAppId' | 'facebookAutoInitEnabled' | 'facebookAutoLogAppEventsEnabled' | 'facebookDisplayName'>;
export declare function getFacebookScheme(config: ExpoConfigFacebook): string | null;
export declare function getFacebookAppId(config: ExpoConfigFacebook): string | null;
export declare function getFacebookDisplayName(config: ExpoConfigFacebook): string | null;
export declare function getFacebookAutoInitEnabled(config: ExpoConfigFacebook): boolean | null;
export declare function getFacebookAutoLogAppEvents(config: ExpoConfigFacebook): boolean | null;
export declare function getFacebookAdvertiserIDCollection(config: ExpoConfigFacebook): boolean | null;
export declare function setFacebookAppIdString(config: ExpoConfigFacebook, projectRoot: string): Promise<boolean>;
export declare function setFacebookConfig(config: ExpoConfigFacebook, androidManifest: AndroidManifest): AndroidManifest;
export {};

View File

@ -0,0 +1,171 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const android_plugins_1 = require("../plugins/android-plugins");
const XML_1 = require("../utils/XML");
const errors_1 = require("../utils/errors");
const Manifest_1 = require("./Manifest");
const Resources_1 = require("./Resources");
const Strings_1 = require("./Strings");
const CUSTOM_TAB_ACTIVITY = 'com.facebook.CustomTabActivity';
const STRING_FACEBOOK_APP_ID = 'facebook_app_id';
const META_APP_ID = 'com.facebook.sdk.ApplicationId';
const META_APP_NAME = 'com.facebook.sdk.ApplicationName';
const META_AUTO_INIT = 'com.facebook.sdk.AutoInitEnabled';
const META_AUTO_LOG_APP_EVENTS = 'com.facebook.sdk.AutoLogAppEventsEnabled';
const META_AD_ID_COLLECTION = 'com.facebook.sdk.AdvertiserIDCollectionEnabled';
exports.withFacebookAppIdString = android_plugins_1.createStringsXmlPlugin(applyFacebookAppIdString, 'withFacebookAppIdString');
exports.withFacebookManifest = android_plugins_1.createAndroidManifestPlugin(setFacebookConfig, 'withFacebookManifest');
function buildXMLItem({ head, children, }) {
return Object.assign(Object.assign({}, (children !== null && children !== void 0 ? children : {})), { $: head });
}
function buildAndroidItem(datum) {
const item = typeof datum === 'string' ? { name: datum } : datum;
const head = Manifest_1.prefixAndroidKeys(item);
return buildXMLItem({ head });
}
function getFacebookSchemeActivity(scheme) {
/**
<activity
android:name="com.facebook.CustomTabActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="${scheme}" />
</intent-filter>
</activity>
*/
return buildXMLItem({
head: Manifest_1.prefixAndroidKeys({
name: CUSTOM_TAB_ACTIVITY,
exported: 'true',
}),
children: {
'intent-filter': [
{
action: [buildAndroidItem('android.intent.action.VIEW')],
category: [
buildAndroidItem('android.intent.category.DEFAULT'),
buildAndroidItem('android.intent.category.BROWSABLE'),
],
data: [buildAndroidItem({ scheme })],
},
],
},
});
}
function getFacebookScheme(config) {
var _a;
return (_a = config.facebookScheme) !== null && _a !== void 0 ? _a : null;
}
exports.getFacebookScheme = getFacebookScheme;
function getFacebookAppId(config) {
var _a;
return (_a = config.facebookAppId) !== null && _a !== void 0 ? _a : null;
}
exports.getFacebookAppId = getFacebookAppId;
function getFacebookDisplayName(config) {
var _a;
return (_a = config.facebookDisplayName) !== null && _a !== void 0 ? _a : null;
}
exports.getFacebookDisplayName = getFacebookDisplayName;
function getFacebookAutoInitEnabled(config) {
var _a;
return (_a = config.facebookAutoInitEnabled) !== null && _a !== void 0 ? _a : null;
}
exports.getFacebookAutoInitEnabled = getFacebookAutoInitEnabled;
function getFacebookAutoLogAppEvents(config) {
var _a;
return (_a = config.facebookAutoLogAppEventsEnabled) !== null && _a !== void 0 ? _a : null;
}
exports.getFacebookAutoLogAppEvents = getFacebookAutoLogAppEvents;
function getFacebookAdvertiserIDCollection(config) {
var _a;
return (_a = config.facebookAdvertiserIDCollectionEnabled) !== null && _a !== void 0 ? _a : null;
}
exports.getFacebookAdvertiserIDCollection = getFacebookAdvertiserIDCollection;
function ensureFacebookActivity({ mainApplication, scheme, }) {
if (Array.isArray(mainApplication.activity)) {
// Remove all Facebook CustomTabActivities first
mainApplication.activity = mainApplication.activity.filter(activity => {
var _a;
return ((_a = activity.$) === null || _a === void 0 ? void 0 : _a['android:name']) !== CUSTOM_TAB_ACTIVITY;
});
}
else {
mainApplication.activity = [];
}
// If a new scheme is defined, append it to the activity.
if (scheme) {
mainApplication.activity.push(getFacebookSchemeActivity(scheme));
}
return mainApplication;
}
async function setFacebookAppIdString(config, projectRoot) {
const stringsPath = await Strings_1.getProjectStringsXMLPathAsync(projectRoot);
errors_1.assert(stringsPath, `There was a problem setting your Facebook App ID in "${stringsPath}"`);
let stringsJSON = await Resources_1.readResourcesXMLAsync({ path: stringsPath });
stringsJSON = applyFacebookAppIdString(config, stringsJSON);
try {
await XML_1.writeXMLAsync({ path: stringsPath, xml: stringsJSON });
}
catch (_a) {
throw new Error(`Error setting facebookAppId. Cannot write strings.xml to "${stringsPath}"`);
}
return true;
}
exports.setFacebookAppIdString = setFacebookAppIdString;
function applyFacebookAppIdString(config, stringsJSON) {
const appId = getFacebookAppId(config);
if (appId) {
return Strings_1.setStringItem([Resources_1.buildResourceItem({ name: STRING_FACEBOOK_APP_ID, value: appId })], stringsJSON);
}
return Strings_1.removeStringItem(STRING_FACEBOOK_APP_ID, stringsJSON);
}
function setFacebookConfig(config, androidManifest) {
const scheme = getFacebookScheme(config);
const appId = getFacebookAppId(config);
const displayName = getFacebookDisplayName(config);
const autoInitEnabled = getFacebookAutoInitEnabled(config);
const autoLogAppEvents = getFacebookAutoLogAppEvents(config);
const advertiserIdCollection = getFacebookAdvertiserIDCollection(config);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
let mainApplication = Manifest_1.getMainApplicationOrThrow(androidManifest);
mainApplication = ensureFacebookActivity({ scheme, mainApplication });
if (appId) {
mainApplication = Manifest_1.addMetaDataItemToMainApplication(mainApplication, META_APP_ID,
// The corresponding string is set in setFacebookAppIdString
`@string/${STRING_FACEBOOK_APP_ID}`);
}
else {
mainApplication = Manifest_1.removeMetaDataItemFromMainApplication(mainApplication, META_APP_ID);
}
if (displayName) {
mainApplication = Manifest_1.addMetaDataItemToMainApplication(mainApplication, META_APP_NAME, displayName);
}
else {
mainApplication = Manifest_1.removeMetaDataItemFromMainApplication(mainApplication, META_APP_NAME);
}
if (autoInitEnabled !== null) {
mainApplication = Manifest_1.addMetaDataItemToMainApplication(mainApplication, META_AUTO_INIT, autoInitEnabled ? 'true' : 'false');
}
else {
mainApplication = Manifest_1.removeMetaDataItemFromMainApplication(mainApplication, META_AUTO_INIT);
}
if (autoLogAppEvents !== null) {
mainApplication = Manifest_1.addMetaDataItemToMainApplication(mainApplication, META_AUTO_LOG_APP_EVENTS, autoLogAppEvents ? 'true' : 'false');
}
else {
mainApplication = Manifest_1.removeMetaDataItemFromMainApplication(mainApplication, META_AUTO_LOG_APP_EVENTS);
}
if (advertiserIdCollection !== null) {
mainApplication = Manifest_1.addMetaDataItemToMainApplication(mainApplication, META_AD_ID_COLLECTION, advertiserIdCollection ? 'true' : 'false');
}
else {
mainApplication = Manifest_1.removeMetaDataItemFromMainApplication(mainApplication, META_AD_ID_COLLECTION);
}
return androidManifest;
}
exports.setFacebookConfig = setFacebookConfig;
//# sourceMappingURL=Facebook.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
import { ExpoConfig } from '@expo/config-types';
import { AndroidManifest } from './Manifest';
export declare const withGoogleMapsApiKey: import("..").ConfigPlugin<void>;
export declare function getGoogleMapsApiKey(config: Pick<ExpoConfig, 'android'>): string | null;
export declare function setGoogleMapsApiKey(config: Pick<ExpoConfig, 'android'>, androidManifest: AndroidManifest): AndroidManifest;

View File

@ -0,0 +1,32 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const android_plugins_1 = require("../plugins/android-plugins");
const Manifest_1 = require("./Manifest");
const META_API_KEY = 'com.google.android.geo.API_KEY';
const LIB_HTTP = 'org.apache.http.legacy';
exports.withGoogleMapsApiKey = android_plugins_1.createAndroidManifestPlugin(setGoogleMapsApiKey, 'withGoogleMapsApiKey');
function getGoogleMapsApiKey(config) {
var _a, _b, _c, _d;
return (_d = (_c = (_b = (_a = config.android) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.googleMaps) === null || _c === void 0 ? void 0 : _c.apiKey) !== null && _d !== void 0 ? _d : null;
}
exports.getGoogleMapsApiKey = getGoogleMapsApiKey;
function setGoogleMapsApiKey(config, androidManifest) {
const apiKey = getGoogleMapsApiKey(config);
const mainApplication = Manifest_1.getMainApplicationOrThrow(androidManifest);
if (apiKey) {
// If the item exists, add it back
Manifest_1.addMetaDataItemToMainApplication(mainApplication, META_API_KEY, apiKey);
Manifest_1.addUsesLibraryItemToMainApplication(mainApplication, {
name: LIB_HTTP,
required: false,
});
}
else {
// Remove any existing item
Manifest_1.removeMetaDataItemFromMainApplication(mainApplication, META_API_KEY);
Manifest_1.removeUsesLibraryItemFromMainApplication(mainApplication, LIB_HTTP);
}
return androidManifest;
}
exports.setGoogleMapsApiKey = setGoogleMapsApiKey;
//# sourceMappingURL=GoogleMapsApiKey.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"GoogleMapsApiKey.js","sourceRoot":"","sources":["../../src/android/GoogleMapsApiKey.ts"],"names":[],"mappings":";;AAEA,gEAAyE;AACzE,yCAOoB;AAEpB,MAAM,YAAY,GAAG,gCAAgC,CAAC;AACtD,MAAM,QAAQ,GAAG,wBAAwB,CAAC;AAE7B,QAAA,oBAAoB,GAAG,6CAA2B,CAC7D,mBAAmB,EACnB,sBAAsB,CACvB,CAAC;AAEF,SAAgB,mBAAmB,CAAC,MAAmC;;IACrE,+BAAO,MAAM,CAAC,OAAO,0CAAE,MAAM,0CAAE,UAAU,0CAAE,MAAM,mCAAI,IAAI,CAAC;AAC5D,CAAC;AAFD,kDAEC;AAED,SAAgB,mBAAmB,CACjC,MAAmC,EACnC,eAAgC;IAEhC,MAAM,MAAM,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,eAAe,GAAG,oCAAyB,CAAC,eAAe,CAAC,CAAC;IAEnE,IAAI,MAAM,EAAE;QACV,kCAAkC;QAClC,2CAAgC,CAAC,eAAe,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;QACxE,8CAAmC,CAAC,eAAe,EAAE;YACnD,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,KAAK;SAChB,CAAC,CAAC;KACJ;SAAM;QACL,2BAA2B;QAC3B,gDAAqC,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC;QACrE,mDAAwC,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;KACrE;IAED,OAAO,eAAe,CAAC;AACzB,CAAC;AArBD,kDAqBC","sourcesContent":["import { ExpoConfig } from '@expo/config-types';\n\nimport { createAndroidManifestPlugin } from '../plugins/android-plugins';\nimport {\n addMetaDataItemToMainApplication,\n addUsesLibraryItemToMainApplication,\n AndroidManifest,\n getMainApplicationOrThrow,\n removeMetaDataItemFromMainApplication,\n removeUsesLibraryItemFromMainApplication,\n} from './Manifest';\n\nconst META_API_KEY = 'com.google.android.geo.API_KEY';\nconst LIB_HTTP = 'org.apache.http.legacy';\n\nexport const withGoogleMapsApiKey = createAndroidManifestPlugin(\n setGoogleMapsApiKey,\n 'withGoogleMapsApiKey'\n);\n\nexport function getGoogleMapsApiKey(config: Pick<ExpoConfig, 'android'>) {\n return config.android?.config?.googleMaps?.apiKey ?? null;\n}\n\nexport function setGoogleMapsApiKey(\n config: Pick<ExpoConfig, 'android'>,\n androidManifest: AndroidManifest\n) {\n const apiKey = getGoogleMapsApiKey(config);\n const mainApplication = getMainApplicationOrThrow(androidManifest);\n\n if (apiKey) {\n // If the item exists, add it back\n addMetaDataItemToMainApplication(mainApplication, META_API_KEY, apiKey);\n addUsesLibraryItemToMainApplication(mainApplication, {\n name: LIB_HTTP,\n required: false,\n });\n } else {\n // Remove any existing item\n removeMetaDataItemFromMainApplication(mainApplication, META_API_KEY);\n removeUsesLibraryItemFromMainApplication(mainApplication, LIB_HTTP);\n }\n\n return androidManifest;\n}\n"]}

View File

@ -0,0 +1,17 @@
import { ExpoConfig } from '@expo/config-types';
import { ConfigPlugin } from '../Plugin.types';
export declare const withClassPath: ConfigPlugin;
export declare const withApplyPlugin: ConfigPlugin;
/**
* Add `google-services.json` to project
*/
export declare const withGoogleServicesFile: ConfigPlugin;
export declare function getGoogleServicesFilePath(config: Pick<ExpoConfig, 'android'>): string | null;
export declare function setGoogleServicesFile(config: Pick<ExpoConfig, 'android'>, projectRoot: string, targetPath?: string): Promise<boolean>;
/**
* Adding the Google Services plugin
* NOTE(brentvatne): string replacement is a fragile approach! we need a
* better solution than this.
*/
export declare function setClassPath(config: Pick<ExpoConfig, 'android'>, buildGradle: string): string;
export declare function applyPlugin(config: Pick<ExpoConfig, 'android'>, appBuildGradle: string): string;

View File

@ -0,0 +1,110 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = require("path");
const android_plugins_1 = require("../plugins/android-plugins");
const core_plugins_1 = require("../plugins/core-plugins");
const WarningAggregator = __importStar(require("../utils/warnings"));
const DEFAULT_TARGET_PATH = './android/app/google-services.json';
const googleServicesClassPath = 'com.google.gms:google-services';
const googleServicesPlugin = 'com.google.gms.google-services';
// NOTE(brentvatne): This may be annoying to keep up to date...
const googleServicesVersion = '4.3.3';
exports.withClassPath = config => {
return android_plugins_1.withProjectBuildGradle(config, config => {
if (config.modResults.language === 'groovy') {
config.modResults.contents = setClassPath(config, config.modResults.contents);
}
else {
WarningAggregator.addWarningAndroid('android-google-services', `Cannot automatically configure project build.gradle if it's not groovy`);
}
return config;
});
};
exports.withApplyPlugin = config => {
return android_plugins_1.withAppBuildGradle(config, config => {
if (config.modResults.language === 'groovy') {
config.modResults.contents = applyPlugin(config, config.modResults.contents);
}
else {
WarningAggregator.addWarningAndroid('android-google-services', `Cannot automatically configure app build.gradle if it's not groovy`);
}
return config;
});
};
/**
* Add `google-services.json` to project
*/
exports.withGoogleServicesFile = config => {
return core_plugins_1.withDangerousMod(config, [
'android',
async (config) => {
await setGoogleServicesFile(config, config.modRequest.projectRoot);
return config;
},
]);
};
function getGoogleServicesFilePath(config) {
var _a, _b;
return (_b = (_a = config.android) === null || _a === void 0 ? void 0 : _a.googleServicesFile) !== null && _b !== void 0 ? _b : null;
}
exports.getGoogleServicesFilePath = getGoogleServicesFilePath;
async function setGoogleServicesFile(config, projectRoot, targetPath = DEFAULT_TARGET_PATH) {
const partialSourcePath = getGoogleServicesFilePath(config);
if (!partialSourcePath) {
return false;
}
const completeSourcePath = path_1.resolve(projectRoot, partialSourcePath);
const destinationPath = path_1.resolve(projectRoot, targetPath);
try {
await fs_extra_1.default.copy(completeSourcePath, destinationPath);
}
catch (e) {
throw new Error(`Cannot copy google-services.json from ${completeSourcePath} to ${destinationPath}. Please make sure the source and destination paths exist.`);
}
return true;
}
exports.setGoogleServicesFile = setGoogleServicesFile;
/**
* Adding the Google Services plugin
* NOTE(brentvatne): string replacement is a fragile approach! we need a
* better solution than this.
*/
function setClassPath(config, buildGradle) {
const googleServicesFile = getGoogleServicesFilePath(config);
if (!googleServicesFile) {
return buildGradle;
}
if (buildGradle.includes(googleServicesClassPath)) {
return buildGradle;
}
//
return buildGradle.replace(/dependencies\s?{/, `dependencies {
classpath '${googleServicesClassPath}:${googleServicesVersion}'`);
}
exports.setClassPath = setClassPath;
function applyPlugin(config, appBuildGradle) {
const googleServicesFile = getGoogleServicesFilePath(config);
if (!googleServicesFile) {
return appBuildGradle;
}
// Make sure the project does not have the plugin already
const pattern = new RegExp(`apply\\s+plugin:\\s+['"]${googleServicesPlugin}['"]`);
if (appBuildGradle.match(pattern)) {
return appBuildGradle;
}
// Add it to the end of the file
return appBuildGradle + `\napply plugin: '${googleServicesPlugin}'`;
}
exports.applyPlugin = applyPlugin;
//# sourceMappingURL=GoogleServices.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,31 @@
import { ExpoConfig } from '@expo/config-types';
import { ConfigPlugin } from '../Plugin.types';
declare type DPIString = 'mdpi' | 'hdpi' | 'xhdpi' | 'xxhdpi' | 'xxxhdpi';
declare type dpiMap = Record<DPIString, {
folderName: string;
scale: number;
}>;
export declare const dpiValues: dpiMap;
export declare const ANDROID_RES_PATH = "android/app/src/main/res/";
export declare const withIcons: ConfigPlugin;
export declare function getIcon(config: ExpoConfig): string | null;
export declare function getAdaptiveIcon(config: ExpoConfig): {
foregroundImage: string | null;
backgroundColor: string | null;
backgroundImage: string | null;
};
/**
* Resizes the user-provided icon to create a set of legacy icon files in
* their respective "mipmap" directories for <= Android 7, and creates a set of adaptive
* icon files for > Android 7 from the adaptive icon files (if provided).
*/
export declare function setIconAsync(config: ExpoConfig, projectRoot: string): Promise<true | null>;
/**
* Configures adaptive icon files to be used on Android 8 and up. A foreground image must be provided,
* and will have a transparent background unless:
* - A backgroundImage is provided, or
* - A backgroundColor was specified
*/
export declare function configureAdaptiveIconAsync(projectRoot: string, foregroundImage: string, backgroundImage: string | null, backgroundColor: string): Promise<void>;
export declare const createAdaptiveIconXmlString: (backgroundImage: string | null) => string;
export {};

213
node_modules/@expo/config-plugins/build/android/Icon.js generated vendored Normal file
View File

@ -0,0 +1,213 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const image_utils_1 = require("@expo/image-utils");
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const core_plugins_1 = require("../plugins/core-plugins");
const XML_1 = require("../utils/XML");
const Colors = __importStar(require("./Colors"));
const Resources_1 = require("./Resources");
exports.dpiValues = {
mdpi: { folderName: 'mipmap-mdpi', scale: 1 },
hdpi: { folderName: 'mipmap-hdpi', scale: 1.5 },
xhdpi: { folderName: 'mipmap-xhdpi', scale: 2 },
xxhdpi: { folderName: 'mipmap-xxhdpi', scale: 3 },
xxxhdpi: { folderName: 'mipmap-xxxhdpi', scale: 4 },
};
const BASELINE_PIXEL_SIZE = 48;
exports.ANDROID_RES_PATH = 'android/app/src/main/res/';
const MIPMAP_ANYDPI_V26 = 'mipmap-anydpi-v26';
const ICON_BACKGROUND = 'iconBackground';
const IC_LAUNCHER_PNG = 'ic_launcher.png';
const IC_LAUNCHER_ROUND_PNG = 'ic_launcher_round.png';
const IC_LAUNCHER_BACKGROUND_PNG = 'ic_launcher_background.png';
const IC_LAUNCHER_FOREGROUND_PNG = 'ic_launcher_foreground.png';
const IC_LAUNCHER_XML = 'ic_launcher.xml';
const IC_LAUNCHER_ROUND_XML = 'ic_launcher_round.xml';
exports.withIcons = config => {
return core_plugins_1.withDangerousMod(config, [
'android',
async (config) => {
await setIconAsync(config, config.modRequest.projectRoot);
return config;
},
]);
};
function getIcon(config) {
var _a;
return config.icon || ((_a = config.android) === null || _a === void 0 ? void 0 : _a.icon) || null;
}
exports.getIcon = getIcon;
function getAdaptiveIcon(config) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
return {
foregroundImage: (_c = (_b = (_a = config.android) === null || _a === void 0 ? void 0 : _a.adaptiveIcon) === null || _b === void 0 ? void 0 : _b.foregroundImage) !== null && _c !== void 0 ? _c : null,
backgroundColor: (_f = (_e = (_d = config.android) === null || _d === void 0 ? void 0 : _d.adaptiveIcon) === null || _e === void 0 ? void 0 : _e.backgroundColor) !== null && _f !== void 0 ? _f : null,
backgroundImage: (_j = (_h = (_g = config.android) === null || _g === void 0 ? void 0 : _g.adaptiveIcon) === null || _h === void 0 ? void 0 : _h.backgroundImage) !== null && _j !== void 0 ? _j : null,
};
}
exports.getAdaptiveIcon = getAdaptiveIcon;
/**
* Resizes the user-provided icon to create a set of legacy icon files in
* their respective "mipmap" directories for <= Android 7, and creates a set of adaptive
* icon files for > Android 7 from the adaptive icon files (if provided).
*/
async function setIconAsync(config, projectRoot) {
const { foregroundImage, backgroundColor, backgroundImage } = getAdaptiveIcon(config);
const icon = foregroundImage !== null && foregroundImage !== void 0 ? foregroundImage : getIcon(config);
if (!icon) {
return null;
}
await configureLegacyIconAsync(projectRoot, icon, backgroundImage, backgroundColor);
await configureAdaptiveIconAsync(projectRoot, icon, backgroundImage, backgroundColor !== null && backgroundColor !== void 0 ? backgroundColor : '#FFFFFF');
return true;
}
exports.setIconAsync = setIconAsync;
/**
* Configures legacy icon files to be used on Android 7 and earlier. If adaptive icon configuration
* was provided, we create a pseudo-adaptive icon by layering the provided files (or background
* color if no backgroundImage is provided. If no backgroundImage and no backgroundColor are provided,
* the background is set to transparent.)
*/
async function configureLegacyIconAsync(projectRoot, icon, backgroundImage, backgroundColor) {
await Promise.all(Object.values(exports.dpiValues).map(async ({ folderName, scale }) => {
const dpiFolderPath = path_1.default.resolve(projectRoot, exports.ANDROID_RES_PATH, folderName);
const iconSizePx = BASELINE_PIXEL_SIZE * scale;
// backgroundImage overrides backgroundColor
backgroundColor = backgroundImage ? 'transparent' : backgroundColor !== null && backgroundColor !== void 0 ? backgroundColor : 'transparent';
let squareIconImage = (await image_utils_1.generateImageAsync({ projectRoot, cacheType: 'android-standard-square' }, {
src: icon,
width: iconSizePx,
height: iconSizePx,
resizeMode: 'cover',
backgroundColor,
})).source;
let roundIconImage = (await image_utils_1.generateImageAsync({ projectRoot, cacheType: 'android-standard-circle' }, {
src: icon,
width: iconSizePx,
height: iconSizePx,
resizeMode: 'cover',
backgroundColor,
borderRadius: iconSizePx / 2,
})).source;
if (backgroundImage) {
// Layer the buffers we just created on top of the background image that's provided
const squareBackgroundLayer = (await image_utils_1.generateImageAsync({ projectRoot, cacheType: 'android-standard-square-background' }, {
src: backgroundImage,
width: iconSizePx,
height: iconSizePx,
resizeMode: 'cover',
backgroundColor: 'transparent',
})).source;
const roundBackgroundLayer = (await image_utils_1.generateImageAsync({ projectRoot, cacheType: 'android-standard-round-background' }, {
src: backgroundImage,
width: iconSizePx,
height: iconSizePx,
resizeMode: 'cover',
backgroundColor: 'transparent',
borderRadius: iconSizePx / 2,
})).source;
squareIconImage = await image_utils_1.compositeImagesAsync({
foreground: squareIconImage,
background: squareBackgroundLayer,
});
roundIconImage = await image_utils_1.compositeImagesAsync({
foreground: roundIconImage,
background: roundBackgroundLayer,
});
}
await fs_extra_1.default.ensureDir(dpiFolderPath);
await fs_extra_1.default.writeFile(path_1.default.resolve(dpiFolderPath, IC_LAUNCHER_PNG), squareIconImage);
await fs_extra_1.default.writeFile(path_1.default.resolve(dpiFolderPath, IC_LAUNCHER_ROUND_PNG), roundIconImage);
}));
}
/**
* Configures adaptive icon files to be used on Android 8 and up. A foreground image must be provided,
* and will have a transparent background unless:
* - A backgroundImage is provided, or
* - A backgroundColor was specified
*/
async function configureAdaptiveIconAsync(projectRoot, foregroundImage, backgroundImage, backgroundColor) {
await setBackgroundColorAsync(projectRoot, backgroundColor);
await Promise.all(Object.values(exports.dpiValues).map(async ({ folderName, scale }) => {
const dpiFolderPath = path_1.default.resolve(projectRoot, exports.ANDROID_RES_PATH, folderName);
const iconSizePx = BASELINE_PIXEL_SIZE * scale;
try {
const adpativeIconForeground = (await image_utils_1.generateImageAsync({ projectRoot, cacheType: 'android-adaptive-foreground' }, {
src: foregroundImage,
width: iconSizePx,
height: iconSizePx,
resizeMode: 'cover',
backgroundColor: 'transparent',
})).source;
await fs_extra_1.default.writeFile(path_1.default.resolve(dpiFolderPath, IC_LAUNCHER_FOREGROUND_PNG), adpativeIconForeground);
if (backgroundImage) {
const adpativeIconBackground = (await image_utils_1.generateImageAsync({ projectRoot, cacheType: 'android-adaptive-background' }, {
src: backgroundImage,
width: iconSizePx,
height: iconSizePx,
resizeMode: 'cover',
backgroundColor: 'transparent',
})).source;
await fs_extra_1.default.writeFile(path_1.default.resolve(dpiFolderPath, IC_LAUNCHER_BACKGROUND_PNG), adpativeIconBackground);
}
else {
// Remove any instances of ic_launcher_background.png that are there from previous icons
await removeBackgroundImageFilesAsync(projectRoot);
}
}
catch (e) {
throw new Error('Encountered an issue resizing adaptive app icon: ' + e);
}
}));
// create ic_launcher.xml and ic_launcher_round.xml
const icLauncherXmlString = exports.createAdaptiveIconXmlString(backgroundImage);
await createAdaptiveIconXmlFiles(projectRoot, icLauncherXmlString);
}
exports.configureAdaptiveIconAsync = configureAdaptiveIconAsync;
async function setBackgroundColorAsync(projectRoot, backgroundColor) {
const colorsXmlPath = await Colors.getProjectColorsXMLPathAsync(projectRoot);
let colorsJson = await Resources_1.readResourcesXMLAsync({ path: colorsXmlPath });
if (backgroundColor) {
const colorItemToAdd = Resources_1.buildResourceItem({ name: ICON_BACKGROUND, value: backgroundColor });
colorsJson = Colors.setColorItem(colorItemToAdd, colorsJson);
}
else {
colorsJson = Colors.removeColorItem(ICON_BACKGROUND, colorsJson);
}
await XML_1.writeXMLAsync({ path: colorsXmlPath, xml: colorsJson });
}
exports.createAdaptiveIconXmlString = (backgroundImage) => {
let background = `<background android:drawable="@color/iconBackground"/>`;
if (backgroundImage) {
background = `<background android:drawable="@mipmap/ic_launcher_background"/>`;
}
return `<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
${background}
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>`;
};
async function createAdaptiveIconXmlFiles(projectRoot, icLauncherXmlString) {
const anyDpiV26Directory = path_1.default.resolve(projectRoot, exports.ANDROID_RES_PATH, MIPMAP_ANYDPI_V26);
await fs_extra_1.default.ensureDir(anyDpiV26Directory);
await fs_extra_1.default.writeFile(path_1.default.resolve(anyDpiV26Directory, IC_LAUNCHER_XML), icLauncherXmlString);
await fs_extra_1.default.writeFile(path_1.default.resolve(anyDpiV26Directory, IC_LAUNCHER_ROUND_XML), icLauncherXmlString);
}
async function removeBackgroundImageFilesAsync(projectRoot) {
return await Promise.all(Object.values(exports.dpiValues).map(async ({ folderName }) => {
const dpiFolderPath = path_1.default.resolve(projectRoot, exports.ANDROID_RES_PATH, folderName);
await fs_extra_1.default.remove(path_1.default.resolve(dpiFolderPath, IC_LAUNCHER_BACKGROUND_PNG));
}));
}
//# sourceMappingURL=Icon.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
import { Android, ExpoConfig } from '@expo/config-types';
import { AndroidManifest } from './Manifest';
declare type AndroidIntentFilters = NonNullable<Android['intentFilters']>;
export declare const withAndroidIntentFilters: import("..").ConfigPlugin<void>;
export declare function getIntentFilters(config: Pick<ExpoConfig, 'android'>): AndroidIntentFilters;
export declare function setAndroidIntentFilters(config: Pick<ExpoConfig, 'android'>, androidManifest: AndroidManifest): Promise<AndroidManifest>;
export default function renderIntentFilters(intentFilters: AndroidIntentFilters): string[];
export {};

View File

@ -0,0 +1,70 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const xml2js_1 = require("xml2js");
const android_plugins_1 = require("../plugins/android-plugins");
const Manifest_1 = require("./Manifest");
// TODO: make it so intent filters aren't written again if you run the command again
exports.withAndroidIntentFilters = android_plugins_1.createAndroidManifestPlugin(setAndroidIntentFilters, 'withAndroidIntentFilters');
function getIntentFilters(config) {
var _a, _b;
return (_b = (_a = config.android) === null || _a === void 0 ? void 0 : _a.intentFilters) !== null && _b !== void 0 ? _b : [];
}
exports.getIntentFilters = getIntentFilters;
async function setAndroidIntentFilters(config, androidManifest) {
var _a;
const intentFilters = getIntentFilters(config);
if (!intentFilters.length) {
return androidManifest;
}
const intentFiltersXML = renderIntentFilters(intentFilters).join('');
const parser = new xml2js_1.Parser();
const intentFiltersJSON = await parser.parseStringPromise(intentFiltersXML);
const mainActivity = Manifest_1.getMainActivityOrThrow(androidManifest);
mainActivity['intent-filter'] = (_a = mainActivity['intent-filter']) === null || _a === void 0 ? void 0 : _a.concat(intentFiltersJSON['intent-filter']);
return androidManifest;
}
exports.setAndroidIntentFilters = setAndroidIntentFilters;
function renderIntentFilters(intentFilters) {
// returns an array of <intent-filter> tags:
// [
// `<intent-filter>
// <data android:scheme="exp"/>
// <data android:scheme="exps"/>
//
// <action android:name="android.intent.action.VIEW"/>
//
// <category android:name="android.intent.category.DEFAULT"/>
// <category android:name="android.intent.category.BROWSABLE"/>
// </intent-filter>`,
// ...
// ]
return intentFilters.map(intentFilter => {
const autoVerify = intentFilter.autoVerify ? ' android:autoVerify="true"' : '';
return `<intent-filter${autoVerify}>
${renderIntentFilterData(intentFilter.data)}
<action android:name="android.intent.action.${intentFilter.action}"/>
${renderIntentFilterCategory(intentFilter.category)}
</intent-filter>`;
});
}
exports.default = renderIntentFilters;
function renderIntentFilterDatumEntries(datum = {}) {
const entries = [];
for (const [key, value] of Object.entries(datum)) {
entries.push(`android:${key}="${value}"`);
}
return entries.join(' ');
}
function renderIntentFilterData(data) {
return (Array.isArray(data) ? data : [data])
.filter(Boolean)
.map(datum => `<data ${renderIntentFilterDatumEntries(datum)}/>`)
.join('\n');
}
function renderIntentFilterCategory(category) {
return (Array.isArray(category) ? category : [category])
.filter(Boolean)
.map(cat => `<category android:name="android.intent.category.${cat}"/>`)
.join('\n');
}
//# sourceMappingURL=IntentFilters.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,124 @@
export declare type StringBoolean = 'true' | 'false';
declare type ManifestMetaDataAttributes = AndroidManifestAttributes & {
'android:value'?: string;
'android:resource'?: string;
};
declare type AndroidManifestAttributes = {
'android:name': string | 'android.intent.action.VIEW';
};
declare type ManifestAction = {
$: AndroidManifestAttributes;
};
declare type ManifestCategory = {
$: AndroidManifestAttributes;
};
declare type ManifestData = {
$: {
[key: string]: string | undefined;
'android:host'?: string;
'android:pathPrefix'?: string;
'android:scheme'?: string;
};
};
declare type ManifestReciever = {
$: AndroidManifestAttributes & {
'android:exported'?: StringBoolean;
'android:enabled'?: StringBoolean;
};
'intent-filter'?: ManifestIntentFilter[];
};
declare type ManifestIntentFilter = {
action?: ManifestAction[];
data?: ManifestData[];
category?: ManifestCategory[];
};
export declare type ManifestMetaData = {
$: ManifestMetaDataAttributes;
};
declare type ManifestServiceAttributes = AndroidManifestAttributes & {
'android:enabled'?: StringBoolean;
'android:exported'?: StringBoolean;
'android:permission'?: string;
};
declare type ManifestService = {
$: ManifestServiceAttributes;
'intent-filter'?: ManifestIntentFilter[];
};
declare type ManifestApplicationAttributes = {
'android:name': string | '.MainApplication';
'android:icon'?: string;
'android:label'?: string;
'android:allowBackup'?: StringBoolean;
'android:largeHeap'?: StringBoolean;
'android:requestLegacyExternalStorage'?: StringBoolean;
'android:usesCleartextTraffic'?: StringBoolean;
[key: string]: string | undefined;
};
export declare type ManifestActivity = {
$: ManifestApplicationAttributes & {
'android:exported'?: StringBoolean;
'android:launchMode'?: string;
'android:theme'?: string;
[key: string]: string | undefined;
};
'intent-filter'?: ManifestIntentFilter[];
};
export declare type ManifestUsesLibrary = {
$: AndroidManifestAttributes & {
'android:required'?: StringBoolean;
};
};
export declare type ManifestApplication = {
$: ManifestApplicationAttributes;
activity?: ManifestActivity[];
service?: ManifestService[];
receiver?: ManifestReciever[];
'meta-data'?: ManifestMetaData[];
'uses-library'?: ManifestUsesLibrary[];
};
declare type ManifestPermission = {
$: AndroidManifestAttributes & {
'android:protectionLevel'?: string | 'signature';
};
};
export declare type ManifestUsesPermission = {
$: AndroidManifestAttributes;
};
declare type ManifestUsesFeature = {
$: AndroidManifestAttributes & {
'android:glEsVersion'?: string;
'android:required': StringBoolean;
};
};
export declare type AndroidManifest = {
manifest: {
$: {
'xmlns:android': string;
package?: string;
[key: string]: string | undefined;
};
permission?: ManifestPermission[];
'uses-permission'?: ManifestUsesPermission[];
'uses-permission-sdk-23'?: ManifestUsesPermission[];
'uses-feature'?: ManifestUsesFeature[];
application?: ManifestApplication[];
};
};
export declare function writeAndroidManifestAsync(manifestPath: string, androidManifest: AndroidManifest): Promise<void>;
export declare function readAndroidManifestAsync(manifestPath: string): Promise<AndroidManifest>;
export declare function getMainApplication(androidManifest: AndroidManifest): ManifestApplication | null;
export declare function getMainApplicationOrThrow(androidManifest: AndroidManifest): ManifestApplication;
export declare function getMainActivityOrThrow(androidManifest: AndroidManifest): ManifestActivity;
export declare function getMainActivity(androidManifest: AndroidManifest): ManifestActivity | null;
export declare function addMetaDataItemToMainApplication(mainApplication: ManifestApplication, itemName: string, itemValue: string, itemType?: 'resource' | 'value'): ManifestApplication;
export declare function removeMetaDataItemFromMainApplication(mainApplication: any, itemName: string): any;
export declare function findMetaDataItem(mainApplication: any, itemName: string): number;
export declare function findUsesLibraryItem(mainApplication: any, itemName: string): number;
export declare function getMainApplicationMetaDataValue(androidManifest: AndroidManifest, name: string): string | null;
export declare function addUsesLibraryItemToMainApplication(mainApplication: ManifestApplication, item: {
name: string;
required?: boolean;
}): ManifestApplication;
export declare function removeUsesLibraryItemFromMainApplication(mainApplication: ManifestApplication, itemName: string): ManifestApplication;
export declare function prefixAndroidKeys<T extends Record<string, any> = Record<string, string>>(head: T): Record<string, any>;
export {};

View File

@ -0,0 +1,145 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const XML = __importStar(require("../utils/XML"));
const errors_1 = require("../utils/errors");
async function writeAndroidManifestAsync(manifestPath, androidManifest) {
const manifestXml = XML.format(androidManifest);
await fs_extra_1.default.ensureDir(path_1.default.dirname(manifestPath));
await fs_extra_1.default.writeFile(manifestPath, manifestXml);
}
exports.writeAndroidManifestAsync = writeAndroidManifestAsync;
async function readAndroidManifestAsync(manifestPath) {
const xml = await XML.readXMLAsync({ path: manifestPath });
if (!isManifest(xml)) {
throw new Error('Invalid manifest found at: ' + manifestPath);
}
return xml;
}
exports.readAndroidManifestAsync = readAndroidManifestAsync;
function isManifest(xml) {
// TODO: Maybe more validation
return !!xml.manifest;
}
function getMainApplication(androidManifest) {
var _a, _b, _c;
return ((_c = (_b = (_a = androidManifest === null || androidManifest === void 0 ? void 0 : androidManifest.manifest) === null || _a === void 0 ? void 0 : _a.application) === null || _b === void 0 ? void 0 : _b.filter(e => { var _a; return ((_a = e === null || e === void 0 ? void 0 : e.$) === null || _a === void 0 ? void 0 : _a['android:name']) === '.MainApplication'; })[0]) !== null && _c !== void 0 ? _c : null);
}
exports.getMainApplication = getMainApplication;
function getMainApplicationOrThrow(androidManifest) {
const mainApplication = getMainApplication(androidManifest);
errors_1.assert(mainApplication, 'AndroidManifest.xml is missing the required MainApplication element');
return mainApplication;
}
exports.getMainApplicationOrThrow = getMainApplicationOrThrow;
function getMainActivityOrThrow(androidManifest) {
const mainActivity = getMainActivity(androidManifest);
errors_1.assert(mainActivity, 'AndroidManifest.xml is missing the required MainActivity element');
return mainActivity;
}
exports.getMainActivityOrThrow = getMainActivityOrThrow;
function getMainActivity(androidManifest) {
var _a, _b, _c, _d, _e, _f;
const mainActivity = (_e = (_d = (_c = (_b = (_a = androidManifest === null || androidManifest === void 0 ? void 0 : androidManifest.manifest) === null || _a === void 0 ? void 0 : _a.application) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.activity) === null || _d === void 0 ? void 0 : _d.filter) === null || _e === void 0 ? void 0 : _e.call(_d, (e) => e.$['android:name'] === '.MainActivity');
return (_f = mainActivity === null || mainActivity === void 0 ? void 0 : mainActivity[0]) !== null && _f !== void 0 ? _f : null;
}
exports.getMainActivity = getMainActivity;
function addMetaDataItemToMainApplication(mainApplication, itemName, itemValue, itemType = 'value') {
let existingMetaDataItem;
const newItem = {
$: prefixAndroidKeys({ name: itemName, [itemType]: itemValue }),
};
if (mainApplication['meta-data']) {
existingMetaDataItem = mainApplication['meta-data'].filter((e) => e.$['android:name'] === itemName);
if (existingMetaDataItem.length) {
existingMetaDataItem[0].$[`android:${itemType}`] = itemValue;
}
else {
mainApplication['meta-data'].push(newItem);
}
}
else {
mainApplication['meta-data'] = [newItem];
}
return mainApplication;
}
exports.addMetaDataItemToMainApplication = addMetaDataItemToMainApplication;
function removeMetaDataItemFromMainApplication(mainApplication, itemName) {
const index = findMetaDataItem(mainApplication, itemName);
if ((mainApplication === null || mainApplication === void 0 ? void 0 : mainApplication['meta-data']) && index > -1) {
mainApplication['meta-data'].splice(index, 1);
}
return mainApplication;
}
exports.removeMetaDataItemFromMainApplication = removeMetaDataItemFromMainApplication;
function findApplicationSubItem(mainApplication, category, itemName) {
const parent = mainApplication[category];
if (Array.isArray(parent)) {
const index = parent.findIndex((e) => e.$['android:name'] === itemName);
return index;
}
return -1;
}
function findMetaDataItem(mainApplication, itemName) {
return findApplicationSubItem(mainApplication, 'meta-data', itemName);
}
exports.findMetaDataItem = findMetaDataItem;
function findUsesLibraryItem(mainApplication, itemName) {
return findApplicationSubItem(mainApplication, 'uses-library', itemName);
}
exports.findUsesLibraryItem = findUsesLibraryItem;
function getMainApplicationMetaDataValue(androidManifest, name) {
var _a, _b;
const mainApplication = getMainApplication(androidManifest);
if (mainApplication === null || mainApplication === void 0 ? void 0 : mainApplication.hasOwnProperty('meta-data')) {
const item = (_a = mainApplication === null || mainApplication === void 0 ? void 0 : mainApplication['meta-data']) === null || _a === void 0 ? void 0 : _a.find((e) => e.$['android:name'] === name);
return (_b = item === null || item === void 0 ? void 0 : item.$['android:value']) !== null && _b !== void 0 ? _b : null;
}
return null;
}
exports.getMainApplicationMetaDataValue = getMainApplicationMetaDataValue;
function addUsesLibraryItemToMainApplication(mainApplication, item) {
let existingMetaDataItem;
const newItem = {
$: prefixAndroidKeys(item),
};
if (mainApplication['uses-library']) {
existingMetaDataItem = mainApplication['uses-library'].filter(e => e.$['android:name'] === item.name);
if (existingMetaDataItem.length) {
existingMetaDataItem[0].$ = newItem.$;
}
else {
mainApplication['uses-library'].push(newItem);
}
}
else {
mainApplication['uses-library'] = [newItem];
}
return mainApplication;
}
exports.addUsesLibraryItemToMainApplication = addUsesLibraryItemToMainApplication;
function removeUsesLibraryItemFromMainApplication(mainApplication, itemName) {
const index = findUsesLibraryItem(mainApplication, itemName);
if ((mainApplication === null || mainApplication === void 0 ? void 0 : mainApplication['uses-library']) && index > -1) {
mainApplication['uses-library'].splice(index, 1);
}
return mainApplication;
}
exports.removeUsesLibraryItemFromMainApplication = removeUsesLibraryItemFromMainApplication;
function prefixAndroidKeys(head) {
// prefix all keys with `android:`
return Object.entries(head).reduce((prev, [key, curr]) => (Object.assign(Object.assign({}, prev), { [`android:${key}`]: curr })), {});
}
exports.prefixAndroidKeys = prefixAndroidKeys;
//# sourceMappingURL=Manifest.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,24 @@
import { ExpoConfig } from '@expo/config-types';
import { ConfigPlugin } from '../Plugin.types';
/**
* Sanitize a name, this should be used for files and gradle names.
* - `[/, \, :, <, >, ", ?, *, |]` are not allowed https://bit.ly/3l6xqKL
*
* @param name
*/
export declare function sanitizeNameForGradle(name: string): string;
export declare const withName: ConfigPlugin<void>;
export declare const withNameSettingsGradle: ConfigPlugin;
export declare function getName(config: Pick<ExpoConfig, 'name'>): string | null;
/**
* Changes the display name on the home screen,
* notifications, and others.
*/
export declare function setName(config: Pick<ExpoConfig, 'name'>, projectRoot: string): Promise<boolean>;
/**
* Regex a name change -- fragile.
*
* @param config
* @param settingsGradle
*/
export declare function applyNameSettingsGradle(config: Pick<ExpoConfig, 'name'>, settingsGradle: string): string;

View File

@ -0,0 +1,75 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const android_plugins_1 = require("../plugins/android-plugins");
const XML_1 = require("../utils/XML");
const errors_1 = require("../utils/errors");
const warnings_1 = require("../utils/warnings");
const Resources_1 = require("./Resources");
const Strings_1 = require("./Strings");
/**
* Sanitize a name, this should be used for files and gradle names.
* - `[/, \, :, <, >, ", ?, *, |]` are not allowed https://bit.ly/3l6xqKL
*
* @param name
*/
function sanitizeNameForGradle(name) {
// Gradle disallows these:
// The project name 'My-Special 😃 Co/ol_Project' must not contain any of the following characters: [/, \, :, <, >, ", ?, *, |]. Set the 'rootProject.name' or adjust the 'include' statement (see https://docs.gradle.org/6.2/dsl/org.gradle.api.initialization.Settings.html#org.gradle.api.initialization.Settings:include(java.lang.String[]) for more details).
return name.replace(/(\/|\\|:|<|>|"|\?|\*|\|)/g, '');
}
exports.sanitizeNameForGradle = sanitizeNameForGradle;
exports.withName = android_plugins_1.createStringsXmlPlugin(applyNameFromConfig, 'withName');
exports.withNameSettingsGradle = config => {
return android_plugins_1.withSettingsGradle(config, config => {
if (config.modResults.language === 'groovy') {
config.modResults.contents = applyNameSettingsGradle(config, config.modResults.contents);
}
else {
warnings_1.addWarningAndroid('android-name-settings-gradle', `Cannot automatically configure settings.gradle if it's not groovy`);
}
return config;
});
};
function getName(config) {
return typeof config.name === 'string' ? config.name : null;
}
exports.getName = getName;
/**
* Changes the display name on the home screen,
* notifications, and others.
*/
async function setName(config, projectRoot) {
const stringsPath = await Strings_1.getProjectStringsXMLPathAsync(projectRoot);
errors_1.assert(stringsPath);
let stringsJSON = await Resources_1.readResourcesXMLAsync({ path: stringsPath });
stringsJSON = applyNameFromConfig(config, stringsJSON);
try {
await XML_1.writeXMLAsync({ path: stringsPath, xml: stringsJSON });
}
catch (_a) {
throw new Error(`Error setting name. Cannot write strings.xml to ${stringsPath}.`);
}
return true;
}
exports.setName = setName;
function applyNameFromConfig(config, stringsJSON) {
const name = getName(config);
if (name) {
return Strings_1.setStringItem([Resources_1.buildResourceItem({ name: 'app_name', value: name })], stringsJSON);
}
return Strings_1.removeStringItem('app_name', stringsJSON);
}
/**
* Regex a name change -- fragile.
*
* @param config
* @param settingsGradle
*/
function applyNameSettingsGradle(config, settingsGradle) {
var _a;
const name = sanitizeNameForGradle((_a = getName(config)) !== null && _a !== void 0 ? _a : '');
// Select rootProject.name = '***' and replace the contents between the quotes.
return settingsGradle.replace(/rootProject.name\s?=\s?(["'])(?:(?=(\\?))\2.)*?\1/g, `rootProject.name = '${name}'`);
}
exports.applyNameSettingsGradle = applyNameSettingsGradle;
//# sourceMappingURL=Name.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,7 @@
import { ExpoConfig } from '@expo/config-types';
import { ConfigPlugin } from '../Plugin.types';
export declare const withNavigationBar: ConfigPlugin;
export declare function getNavigationBarImmersiveMode(config: Pick<ExpoConfig, 'androidNavigationBar'>): "leanback" | "immersive" | "sticky-immersive" | null;
export declare function getNavigationBarColor(config: Pick<ExpoConfig, 'androidNavigationBar'>): string | null;
export declare function getNavigationBarStyle(config: Pick<ExpoConfig, 'androidNavigationBar'>): "light-content" | "dark-content";
export declare function setNavigationBarConfig(config: Pick<ExpoConfig, 'androidNavigationBar'>, projectRoot: string): Promise<boolean>;

View File

@ -0,0 +1,90 @@
"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const core_plugins_1 = require("../plugins/core-plugins");
const XML_1 = require("../utils/XML");
const WarningAggregator = __importStar(require("../utils/warnings"));
const Colors_1 = require("./Colors");
const Resources_1 = require("./Resources");
const Styles_1 = require("./Styles");
const NAVIGATION_BAR_COLOR = 'navigationBarColor';
const WINDOW_LIGHT_NAVIGATION_BAR = 'android:windowLightNavigationBar';
exports.withNavigationBar = config => {
return core_plugins_1.withDangerousMod(config, [
'android',
async (config) => {
await setNavigationBarConfig(config, config.modRequest.projectRoot);
return config;
},
]);
};
function getNavigationBarImmersiveMode(config) {
var _a;
return ((_a = config.androidNavigationBar) === null || _a === void 0 ? void 0 : _a.visible) || null;
}
exports.getNavigationBarImmersiveMode = getNavigationBarImmersiveMode;
function getNavigationBarColor(config) {
var _a;
return ((_a = config.androidNavigationBar) === null || _a === void 0 ? void 0 : _a.backgroundColor) || null;
}
exports.getNavigationBarColor = getNavigationBarColor;
function getNavigationBarStyle(config) {
var _a;
return ((_a = config.androidNavigationBar) === null || _a === void 0 ? void 0 : _a.barStyle) || 'light-content';
}
exports.getNavigationBarStyle = getNavigationBarStyle;
async function setNavigationBarConfig(config, projectRoot) {
const immersiveMode = getNavigationBarImmersiveMode(config);
const hexString = getNavigationBarColor(config);
const barStyle = getNavigationBarStyle(config);
const stylesPath = await Styles_1.getProjectStylesXMLPathAsync(projectRoot);
const colorsPath = await Colors_1.getProjectColorsXMLPathAsync(projectRoot);
let stylesJSON = await Resources_1.readResourcesXMLAsync({ path: stylesPath });
let colorsJSON = await Resources_1.readResourcesXMLAsync({ path: colorsPath });
if (immersiveMode) {
// Immersive mode needs to be set programatically
WarningAggregator.addWarningAndroid('androidNavigationBar.visible', 'Hiding the navigation bar must be done programmatically. Refer to the Android documentation - https://developer.android.com/training/system-ui/immersive - for instructions.');
}
if (hexString) {
const colorItemToAdd = Resources_1.buildResourceItem({ name: NAVIGATION_BAR_COLOR, value: hexString });
colorsJSON = Colors_1.setColorItem(colorItemToAdd, colorsJSON);
const styleItemToAdd = Resources_1.buildResourceItem({
name: `android:${NAVIGATION_BAR_COLOR}`,
value: `@color/${NAVIGATION_BAR_COLOR}`,
});
stylesJSON = Styles_1.setStylesItem({
item: styleItemToAdd,
xml: stylesJSON,
parent: { name: 'AppTheme', parent: 'Theme.AppCompat.Light.NoActionBar' },
});
}
if (barStyle === 'dark-content') {
const navigationBarStyleItem = Resources_1.buildResourceItem({
name: WINDOW_LIGHT_NAVIGATION_BAR,
value: 'true',
});
stylesJSON = Styles_1.setStylesItem({
item: navigationBarStyleItem,
xml: stylesJSON,
parent: { name: 'AppTheme', parent: 'Theme.AppCompat.Light.NoActionBar' },
});
}
try {
await Promise.all([
XML_1.writeXMLAsync({ path: colorsPath, xml: colorsJSON }),
XML_1.writeXMLAsync({ path: stylesPath, xml: stylesJSON }),
]);
}
catch (e) {
throw new Error(`Error setting Android navigation bar color. Cannot write colors.xml to ${colorsPath}, or styles.xml to ${stylesPath}.`);
}
return true;
}
exports.setNavigationBarConfig = setNavigationBarConfig;
//# sourceMappingURL=NavigationBar.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,21 @@
import { ExpoConfig } from '@expo/config-types';
import { ConfigPlugin } from '../Plugin.types';
import { AndroidManifest } from './Manifest';
export declare const META_DATA_NOTIFICATION_ICON = "expo.modules.notifications.default_notification_icon";
export declare const META_DATA_NOTIFICATION_ICON_COLOR = "expo.modules.notifications.default_notification_color";
export declare const NOTIFICATION_ICON = "notification_icon";
export declare const NOTIFICATION_ICON_RESOURCE: string;
export declare const NOTIFICATION_ICON_COLOR = "notification_icon_color";
export declare const NOTIFICATION_ICON_COLOR_RESOURCE: string;
export declare const withNotificationIcons: ConfigPlugin;
export declare const withNotificationIconColor: ConfigPlugin;
export declare const withNotificationManifest: ConfigPlugin<void>;
export declare function getNotificationIcon(config: ExpoConfig): string | null;
export declare function getNotificationColor(config: ExpoConfig): string | null;
/**
* Applies configuration for expo-notifications, including
* the notification icon and notification color.
*/
export declare function setNotificationIconAsync(config: ExpoConfig, projectRoot: string): Promise<void>;
export declare function setNotificationConfigAsync(config: ExpoConfig, manifest: AndroidManifest): Promise<AndroidManifest>;
export declare function setNotificationIconColorAsync(config: ExpoConfig, projectRoot: string): Promise<void>;

View File

@ -0,0 +1,134 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const image_utils_1 = require("@expo/image-utils");
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const android_plugins_1 = require("../plugins/android-plugins");
const core_plugins_1 = require("../plugins/core-plugins");
const XML_1 = require("../utils/XML");
const Colors = __importStar(require("./Colors"));
const Icon_1 = require("./Icon");
const Manifest_1 = require("./Manifest");
const Resources_1 = require("./Resources");
const BASELINE_PIXEL_SIZE = 24;
exports.META_DATA_NOTIFICATION_ICON = 'expo.modules.notifications.default_notification_icon';
exports.META_DATA_NOTIFICATION_ICON_COLOR = 'expo.modules.notifications.default_notification_color';
exports.NOTIFICATION_ICON = 'notification_icon';
exports.NOTIFICATION_ICON_RESOURCE = `@drawable/${exports.NOTIFICATION_ICON}`;
exports.NOTIFICATION_ICON_COLOR = 'notification_icon_color';
exports.NOTIFICATION_ICON_COLOR_RESOURCE = `@color/${exports.NOTIFICATION_ICON_COLOR}`;
exports.withNotificationIcons = config => {
return core_plugins_1.withDangerousMod(config, [
'android',
async (config) => {
await setNotificationIconAsync(config, config.modRequest.projectRoot);
return config;
},
]);
};
exports.withNotificationIconColor = config => {
return core_plugins_1.withDangerousMod(config, [
'android',
async (config) => {
await setNotificationIconColorAsync(config, config.modRequest.projectRoot);
return config;
},
]);
};
exports.withNotificationManifest = android_plugins_1.createAndroidManifestPlugin(setNotificationConfigAsync, 'withNotificationManifest');
function getNotificationIcon(config) {
var _a;
return ((_a = config.notification) === null || _a === void 0 ? void 0 : _a.icon) || null;
}
exports.getNotificationIcon = getNotificationIcon;
function getNotificationColor(config) {
var _a;
return ((_a = config.notification) === null || _a === void 0 ? void 0 : _a.color) || null;
}
exports.getNotificationColor = getNotificationColor;
/**
* Applies configuration for expo-notifications, including
* the notification icon and notification color.
*/
async function setNotificationIconAsync(config, projectRoot) {
const icon = getNotificationIcon(config);
if (icon) {
await writeNotificationIconImageFilesAsync(icon, projectRoot);
}
else {
await removeNotificationIconImageFilesAsync(projectRoot);
}
}
exports.setNotificationIconAsync = setNotificationIconAsync;
async function setNotificationConfigAsync(config, manifest) {
const icon = getNotificationIcon(config);
const color = getNotificationColor(config);
const mainApplication = Manifest_1.getMainApplicationOrThrow(manifest);
if (icon) {
Manifest_1.addMetaDataItemToMainApplication(mainApplication, exports.META_DATA_NOTIFICATION_ICON, exports.NOTIFICATION_ICON_RESOURCE, 'resource');
}
else {
Manifest_1.removeMetaDataItemFromMainApplication(mainApplication, exports.META_DATA_NOTIFICATION_ICON);
}
if (color) {
Manifest_1.addMetaDataItemToMainApplication(mainApplication, exports.META_DATA_NOTIFICATION_ICON_COLOR, exports.NOTIFICATION_ICON_COLOR_RESOURCE, 'resource');
}
else {
Manifest_1.removeMetaDataItemFromMainApplication(mainApplication, exports.META_DATA_NOTIFICATION_ICON_COLOR);
}
return manifest;
}
exports.setNotificationConfigAsync = setNotificationConfigAsync;
async function setNotificationIconColorAsync(config, projectRoot) {
const color = getNotificationColor(config);
const colorsXmlPath = await Colors.getProjectColorsXMLPathAsync(projectRoot);
let colorsJson = await Resources_1.readResourcesXMLAsync({ path: colorsXmlPath });
if (color) {
const colorItemToAdd = Resources_1.buildResourceItem({ name: exports.NOTIFICATION_ICON_COLOR, value: color });
colorsJson = Colors.setColorItem(colorItemToAdd, colorsJson);
}
else {
colorsJson = Colors.removeColorItem(exports.NOTIFICATION_ICON_COLOR, colorsJson);
}
await XML_1.writeXMLAsync({ path: colorsXmlPath, xml: colorsJson });
}
exports.setNotificationIconColorAsync = setNotificationIconColorAsync;
async function writeNotificationIconImageFilesAsync(icon, projectRoot) {
await Promise.all(Object.values(Icon_1.dpiValues).map(async ({ folderName, scale }) => {
const drawableFolderName = folderName.replace('mipmap', 'drawable');
const dpiFolderPath = path_1.default.resolve(projectRoot, Icon_1.ANDROID_RES_PATH, drawableFolderName);
await fs_extra_1.default.ensureDir(dpiFolderPath);
const iconSizePx = BASELINE_PIXEL_SIZE * scale;
try {
const resizedIcon = (await image_utils_1.generateImageAsync({ projectRoot, cacheType: 'android-notification' }, {
src: icon,
width: iconSizePx,
height: iconSizePx,
resizeMode: 'cover',
backgroundColor: 'transparent',
})).source;
await fs_extra_1.default.writeFile(path_1.default.resolve(dpiFolderPath, exports.NOTIFICATION_ICON + '.png'), resizedIcon);
}
catch (e) {
throw new Error('Encountered an issue resizing Android notification icon: ' + e);
}
}));
}
async function removeNotificationIconImageFilesAsync(projectRoot) {
await Promise.all(Object.values(Icon_1.dpiValues).map(async ({ folderName }) => {
const drawableFolderName = folderName.replace('mipmap', 'drawable');
const dpiFolderPath = path_1.default.resolve(projectRoot, Icon_1.ANDROID_RES_PATH, drawableFolderName);
await fs_extra_1.default.remove(path_1.default.resolve(dpiFolderPath, exports.NOTIFICATION_ICON + '.png'));
}));
}
//# sourceMappingURL=Notifications.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,6 @@
import { ExpoConfig } from '@expo/config-types';
import { AndroidManifest } from './Manifest';
export declare const SCREEN_ORIENTATION_ATTRIBUTE = "android:screenOrientation";
export declare const withOrientation: import("..").ConfigPlugin<void>;
export declare function getOrientation(config: Pick<ExpoConfig, 'orientation'>): "default" | "portrait" | "landscape" | null;
export declare function setAndroidOrientation(config: Pick<ExpoConfig, 'orientation'>, androidManifest: AndroidManifest): AndroidManifest;

View File

@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const android_plugins_1 = require("../plugins/android-plugins");
const Manifest_1 = require("./Manifest");
exports.SCREEN_ORIENTATION_ATTRIBUTE = 'android:screenOrientation';
exports.withOrientation = android_plugins_1.createAndroidManifestPlugin(setAndroidOrientation, 'withOrientation');
function getOrientation(config) {
return typeof config.orientation === 'string' ? config.orientation : null;
}
exports.getOrientation = getOrientation;
function setAndroidOrientation(config, androidManifest) {
const orientation = getOrientation(config);
// TODO: Remove this if we decide to remove any orientation configuration when not specified
if (!orientation) {
return androidManifest;
}
const mainActivity = Manifest_1.getMainActivityOrThrow(androidManifest);
mainActivity.$[exports.SCREEN_ORIENTATION_ATTRIBUTE] =
orientation !== 'default' ? orientation : 'unspecified';
return androidManifest;
}
exports.setAndroidOrientation = setAndroidOrientation;
//# sourceMappingURL=Orientation.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Orientation.js","sourceRoot":"","sources":["../../src/android/Orientation.ts"],"names":[],"mappings":";;AAEA,gEAAyE;AACzE,yCAAqE;AAExD,QAAA,4BAA4B,GAAG,2BAA2B,CAAC;AAE3D,QAAA,eAAe,GAAG,6CAA2B,CACxD,qBAAqB,EACrB,iBAAiB,CAClB,CAAC;AAEF,SAAgB,cAAc,CAAC,MAAuC;IACpE,OAAO,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5E,CAAC;AAFD,wCAEC;AAED,SAAgB,qBAAqB,CACnC,MAAuC,EACvC,eAAgC;IAEhC,MAAM,WAAW,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IAC3C,4FAA4F;IAC5F,IAAI,CAAC,WAAW,EAAE;QAChB,OAAO,eAAe,CAAC;KACxB;IAED,MAAM,YAAY,GAAG,iCAAsB,CAAC,eAAe,CAAC,CAAC;IAE7D,YAAY,CAAC,CAAC,CAAC,oCAA4B,CAAC;QAC1C,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,CAAC;IAE1D,OAAO,eAAe,CAAC;AACzB,CAAC;AAhBD,sDAgBC","sourcesContent":["import { ExpoConfig } from '@expo/config-types';\n\nimport { createAndroidManifestPlugin } from '../plugins/android-plugins';\nimport { AndroidManifest, getMainActivityOrThrow } from './Manifest';\n\nexport const SCREEN_ORIENTATION_ATTRIBUTE = 'android:screenOrientation';\n\nexport const withOrientation = createAndroidManifestPlugin(\n setAndroidOrientation,\n 'withOrientation'\n);\n\nexport function getOrientation(config: Pick<ExpoConfig, 'orientation'>) {\n return typeof config.orientation === 'string' ? config.orientation : null;\n}\n\nexport function setAndroidOrientation(\n config: Pick<ExpoConfig, 'orientation'>,\n androidManifest: AndroidManifest\n) {\n const orientation = getOrientation(config);\n // TODO: Remove this if we decide to remove any orientation configuration when not specified\n if (!orientation) {\n return androidManifest;\n }\n\n const mainActivity = getMainActivityOrThrow(androidManifest);\n\n mainActivity.$[SCREEN_ORIENTATION_ATTRIBUTE] =\n orientation !== 'default' ? orientation : 'unspecified';\n\n return androidManifest;\n}\n"]}

View File

@ -0,0 +1,11 @@
import { ExpoConfig } from '@expo/config-types';
import { ConfigPlugin } from '../Plugin.types';
import { AndroidManifest } from './Manifest';
export declare const withPackageManifest: ConfigPlugin<void>;
export declare const withPackageGradle: ConfigPlugin;
export declare const withPackageRefactor: ConfigPlugin;
export declare function getPackage(config: Pick<ExpoConfig, 'android'>): string | null;
export declare function renamePackageOnDisk(config: Pick<ExpoConfig, 'android'>, projectRoot: string): Promise<void>;
export declare function setPackageInBuildGradle(config: Pick<ExpoConfig, 'android'>, buildGradle: string): string;
export declare function setPackageInAndroidManifest(config: Pick<ExpoConfig, 'android'>, androidManifest: AndroidManifest): AndroidManifest;
export declare function getApplicationIdAsync(projectRoot: string): Promise<string | null>;

View File

@ -0,0 +1,148 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_extra_1 = __importDefault(require("fs-extra"));
const glob_1 = require("glob");
const path_1 = __importDefault(require("path"));
const android_plugins_1 = require("../plugins/android-plugins");
const core_plugins_1 = require("../plugins/core-plugins");
const WarningAggregator = __importStar(require("../utils/warnings"));
const Paths_1 = require("./Paths");
exports.withPackageManifest = android_plugins_1.createAndroidManifestPlugin(setPackageInAndroidManifest, 'withPackageManifest');
exports.withPackageGradle = config => {
return android_plugins_1.withAppBuildGradle(config, config => {
if (config.modResults.language === 'groovy') {
config.modResults.contents = setPackageInBuildGradle(config, config.modResults.contents);
}
else {
WarningAggregator.addWarningAndroid('android-package', `Cannot automatically configure app build.gradle if it's not groovy`);
}
return config;
});
};
exports.withPackageRefactor = config => {
return core_plugins_1.withDangerousMod(config, [
'android',
async (config) => {
await renamePackageOnDisk(config, config.modRequest.projectRoot);
return config;
},
]);
};
function getPackage(config) {
var _a, _b;
return (_b = (_a = config.android) === null || _a === void 0 ? void 0 : _a.package) !== null && _b !== void 0 ? _b : null;
}
exports.getPackage = getPackage;
function getPackageRoot(projectRoot) {
return path_1.default.join(projectRoot, 'android', 'app', 'src', 'main', 'java');
}
async function getCurrentPackageName(projectRoot) {
const packageRoot = getPackageRoot(projectRoot);
const mainApplication = await Paths_1.getMainApplicationAsync(projectRoot);
const packagePath = path_1.default.dirname(mainApplication.path);
const packagePathParts = path_1.default.relative(packageRoot, packagePath).split(path_1.default.sep).filter(Boolean);
return packagePathParts.join('.');
}
// NOTE(brentvatne): this assumes that our MainApplication.java file is in the root of the package
// this makes sense for standard react-native projects but may not apply in customized projects, so if
// we want this to be runnable in any app we need to handle other possibilities
async function renamePackageOnDisk(config, projectRoot) {
const newPackageName = getPackage(config);
if (newPackageName === null) {
return;
}
const currentPackageName = await getCurrentPackageName(projectRoot);
if (currentPackageName === newPackageName) {
return;
}
// Set up our paths
const packageRoot = getPackageRoot(projectRoot);
const currentPackagePath = path_1.default.join(packageRoot, ...currentPackageName.split('.'));
const newPackagePath = path_1.default.join(packageRoot, ...newPackageName.split('.'));
// Create the new directory
fs_extra_1.default.mkdirpSync(newPackagePath);
// Move everything from the old directory over
glob_1.sync('**/*', { cwd: currentPackagePath }).forEach(relativePath => {
const filepath = path_1.default.join(currentPackagePath, relativePath);
if (fs_extra_1.default.lstatSync(filepath).isFile()) {
fs_extra_1.default.moveSync(filepath, path_1.default.join(newPackagePath, relativePath));
}
else {
fs_extra_1.default.mkdirpSync(filepath);
}
});
// Remove the old directory recursively from com/old/package to com/old and com,
// as long as the directories are empty
const oldPathParts = currentPackageName.split('.');
while (oldPathParts.length) {
const pathToCheck = path_1.default.join(packageRoot, ...oldPathParts);
try {
const files = fs_extra_1.default.readdirSync(pathToCheck);
if (files.length === 0) {
fs_extra_1.default.rmdirSync(pathToCheck);
}
}
finally {
oldPathParts.pop();
}
}
const filesToUpdate = [
...glob_1.sync('**/*', { cwd: newPackagePath, absolute: true }),
path_1.default.join(projectRoot, 'android', 'app', 'BUCK'),
];
// Replace all occurrences of the path in the project
filesToUpdate.forEach((filepath) => {
try {
if (fs_extra_1.default.lstatSync(filepath).isFile()) {
let contents = fs_extra_1.default.readFileSync(filepath).toString();
contents = contents.replace(new RegExp(currentPackageName, 'g'), newPackageName);
fs_extra_1.default.writeFileSync(filepath, contents);
}
}
catch (_a) { }
});
}
exports.renamePackageOnDisk = renamePackageOnDisk;
function setPackageInBuildGradle(config, buildGradle) {
const packageName = getPackage(config);
if (packageName === null) {
return buildGradle;
}
const pattern = new RegExp(`applicationId ['"].*['"]`);
return buildGradle.replace(pattern, `applicationId '${packageName}'`);
}
exports.setPackageInBuildGradle = setPackageInBuildGradle;
function setPackageInAndroidManifest(config, androidManifest) {
const packageName = getPackage(config);
if (packageName) {
androidManifest.manifest.$.package = packageName;
}
else {
delete androidManifest.manifest.$.package;
}
return androidManifest;
}
exports.setPackageInAndroidManifest = setPackageInAndroidManifest;
async function getApplicationIdAsync(projectRoot) {
var _a;
const buildGradlePath = Paths_1.getAppBuildGradle(projectRoot);
if (!(await fs_extra_1.default.pathExists(buildGradlePath))) {
return null;
}
const buildGradle = await fs_extra_1.default.readFile(buildGradlePath, 'utf8');
const matchResult = buildGradle.match(/applicationId ['"](.*)['"]/);
// TODO add fallback for legacy cases to read from AndroidManifest.xml
return (_a = matchResult === null || matchResult === void 0 ? void 0 : matchResult[1]) !== null && _a !== void 0 ? _a : null;
}
exports.getApplicationIdAsync = getApplicationIdAsync;
//# sourceMappingURL=Package.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,21 @@
import { ResourceKind } from './Resources';
export interface ProjectFile<L extends string = string> {
path: string;
language: L;
contents: string;
}
export declare type GradleProjectFile = ProjectFile<'groovy' | 'kt'>;
export declare type ApplicationProjectFile = ProjectFile<'java' | 'kt'>;
export declare function getMainApplicationAsync(projectRoot: string): Promise<ApplicationProjectFile>;
export declare function getMainActivityAsync(projectRoot: string): Promise<ApplicationProjectFile>;
export declare function getProjectBuildGradleAsync(projectRoot: string): Promise<GradleProjectFile>;
export declare function getSettingsGradleAsync(projectRoot: string): Promise<GradleProjectFile>;
export declare function getAppBuildGradleAsync(projectRoot: string): Promise<GradleProjectFile>;
export declare function getAppBuildGradle(projectRoot: string): string;
export declare function getProjectPathOrThrowAsync(projectRoot: string): Promise<string>;
export declare function getAndroidManifestAsync(projectRoot: string): Promise<string>;
export declare function getResourceFolderAsync(projectRoot: string): Promise<string>;
export declare function getResourceXMLPathAsync(projectRoot: string, { kind, name }: {
kind?: ResourceKind;
name: 'colors' | 'strings' | 'styles' | string;
}): Promise<string>;

View File

@ -0,0 +1,99 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_extra_1 = __importDefault(require("fs-extra"));
const glob_1 = require("glob");
const path = __importStar(require("path"));
const errors_1 = require("../utils/errors");
const modules_1 = require("../utils/modules");
async function getProjectFileAsync(projectRoot, name) {
const mainActivityJavaPath = glob_1.sync(path.join(projectRoot, `android/app/src/main/java/**/${name}.@(java|kt)`))[0];
errors_1.assert(mainActivityJavaPath, `Project file "${name}" does not exist in android project for root "${projectRoot}"`);
const mainActivityPathJava = path.resolve(mainActivityJavaPath, `../${name}.java`);
const mainActivityPathKotlin = path.resolve(mainActivityJavaPath, `../${name}.kt`);
const isJava = await fs_extra_1.default.pathExists(mainActivityPathJava);
const isKotlin = !isJava && (await fs_extra_1.default.pathExists(mainActivityPathKotlin));
if (!isJava && !isKotlin) {
throw new Error(`Failed to find '${name}' file for project: ${projectRoot}.`);
}
const filePath = isJava ? mainActivityPathJava : mainActivityPathKotlin;
return {
path: path.normalize(filePath),
contents: fs_extra_1.default.readFileSync(filePath, 'utf8'),
language: isJava ? 'java' : 'kt',
};
}
async function getMainApplicationAsync(projectRoot) {
return getProjectFileAsync(projectRoot, 'MainApplication');
}
exports.getMainApplicationAsync = getMainApplicationAsync;
async function getMainActivityAsync(projectRoot) {
return getProjectFileAsync(projectRoot, 'MainActivity');
}
exports.getMainActivityAsync = getMainActivityAsync;
async function getGradleFileAsync(projectRoot, gradleName) {
const groovyPath = path.resolve(projectRoot, `${gradleName}.gradle`);
const ktPath = path.resolve(projectRoot, `${gradleName}.gradle.kts`);
const isGroovy = await fs_extra_1.default.pathExists(groovyPath);
const isKotlin = !isGroovy && (await fs_extra_1.default.pathExists(ktPath));
if (!isGroovy && !isKotlin) {
throw new Error(`Failed to find '${gradleName}.gradle' file for project: ${projectRoot}.`);
}
const filePath = isGroovy ? groovyPath : ktPath;
return {
path: path.normalize(filePath),
contents: fs_extra_1.default.readFileSync(filePath, 'utf8'),
language: isGroovy ? 'groovy' : 'kt',
};
}
async function getProjectBuildGradleAsync(projectRoot) {
return getGradleFileAsync(path.join(projectRoot, 'android'), 'build');
}
exports.getProjectBuildGradleAsync = getProjectBuildGradleAsync;
async function getSettingsGradleAsync(projectRoot) {
return getGradleFileAsync(path.join(projectRoot, 'android'), 'settings');
}
exports.getSettingsGradleAsync = getSettingsGradleAsync;
async function getAppBuildGradleAsync(projectRoot) {
return getGradleFileAsync(path.join(projectRoot, 'android', 'app'), 'build');
}
exports.getAppBuildGradleAsync = getAppBuildGradleAsync;
function getAppBuildGradle(projectRoot) {
return path.join(projectRoot, 'android', 'app', 'build.gradle');
}
exports.getAppBuildGradle = getAppBuildGradle;
async function getProjectPathOrThrowAsync(projectRoot) {
const projectPath = path.join(projectRoot, 'android');
if (await modules_1.directoryExistsAsync(projectPath)) {
return projectPath;
}
throw new Error(`Android project folder is missing in project: ${projectRoot}`);
}
exports.getProjectPathOrThrowAsync = getProjectPathOrThrowAsync;
async function getAndroidManifestAsync(projectRoot) {
const projectPath = await getProjectPathOrThrowAsync(projectRoot);
const filePath = path.join(projectPath, 'app/src/main/AndroidManifest.xml');
return filePath;
}
exports.getAndroidManifestAsync = getAndroidManifestAsync;
async function getResourceFolderAsync(projectRoot) {
const projectPath = await getProjectPathOrThrowAsync(projectRoot);
return path.join(projectPath, `app/src/main/res`);
}
exports.getResourceFolderAsync = getResourceFolderAsync;
async function getResourceXMLPathAsync(projectRoot, { kind = 'values', name }) {
const resourcePath = await getResourceFolderAsync(projectRoot);
const filePath = path.join(resourcePath, `${kind}/${name}.xml`);
return filePath;
}
exports.getResourceXMLPathAsync = getResourceXMLPathAsync;
//# sourceMappingURL=Paths.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,18 @@
import { ExpoConfig } from '@expo/config-types';
import { ConfigPlugin } from '../Plugin.types';
import { AndroidManifest, ManifestUsesPermission } from './Manifest';
export declare const requiredPermissions: string[];
export declare const allPermissions: string[];
export declare const withPermissions: ConfigPlugin<string[] | void>;
export declare function getAndroidPermissions(config: Pick<ExpoConfig, 'android'>): string[];
export declare function setAndroidPermissions(config: Pick<ExpoConfig, 'android'>, androidManifest: AndroidManifest): AndroidManifest;
export declare function isPermissionAlreadyRequested(permission: string, manifestPermissions: ManifestUsesPermission[]): boolean;
export declare function addPermissionToManifest(permission: string, manifestPermissions: ManifestUsesPermission[]): ManifestUsesPermission[];
export declare function removePermissions(androidManifest: AndroidManifest, permissionNames?: string[]): void;
export declare function addPermission(androidManifest: AndroidManifest, permissionName: string): void;
export declare function ensurePermissions(androidManifest: AndroidManifest, permissionNames: string[]): {
[permission: string]: boolean;
};
export declare function ensurePermission(androidManifest: AndroidManifest, permissionName: string): boolean;
export declare function ensurePermissionNameFormat(permissionName: string): string;
export declare function getPermissions(androidManifest: AndroidManifest): string[];

View File

@ -0,0 +1,177 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const android_plugins_1 = require("../plugins/android-plugins");
const USES_PERMISSION = 'uses-permission';
exports.requiredPermissions = [
'android.permission.INTERNET',
'android.permission.ACCESS_NETWORK_STATE',
'android.permission.SYSTEM_ALERT_WINDOW',
'android.permission.WAKE_LOCK',
'com.google.android.c2dm.permission.RECEIVE',
];
exports.allPermissions = [
...exports.requiredPermissions,
'android.permission.ACCESS_WIFI_STATE',
'android.permission.ACCESS_COARSE_LOCATION',
'android.permission.ACCESS_FINE_LOCATION',
'android.permission.CAMERA',
'android.permission.MANAGE_DOCUMENTS',
'android.permission.READ_CONTACTS',
'android.permission.WRITE_CONTACTS',
'android.permission.READ_CALENDAR',
'android.permission.WRITE_CALENDAR',
'android.permission.READ_EXTERNAL_STORAGE',
'android.permission.READ_INTERNAL_STORAGE',
'android.permission.READ_PHONE_STATE',
'android.permission.RECORD_AUDIO',
'android.permission.USE_FINGERPRINT',
'android.permission.VIBRATE',
'android.permission.WRITE_EXTERNAL_STORAGE',
'android.permission.READ_SMS',
'com.anddoes.launcher.permission.UPDATE_COUNT',
'com.android.launcher.permission.INSTALL_SHORTCUT',
'com.google.android.gms.permission.ACTIVITY_RECOGNITION',
'com.google.android.providers.gsf.permission.READ_GSERVICES',
'com.htc.launcher.permission.READ_SETTINGS',
'com.htc.launcher.permission.UPDATE_SHORTCUT',
'com.majeur.launcher.permission.UPDATE_BADGE',
'com.sec.android.provider.badge.permission.READ',
'com.sec.android.provider.badge.permission.WRITE',
'com.sonyericsson.home.permission.BROADCAST_BADGE',
];
exports.withPermissions = (config, permissions) => {
if (Array.isArray(permissions)) {
permissions = permissions.filter(Boolean);
if (!config.android)
config.android = {};
if (!config.android.permissions)
config.android.permissions = [];
config.android.permissions = [
// @ts-ignore
...new Set(config.android.permissions.concat(permissions)),
];
}
return android_plugins_1.withAndroidManifest(config, async (config) => {
config.modResults = await setAndroidPermissions(config, config.modResults);
return config;
});
};
function prefixAndroidPermissionsIfNecessary(permissions) {
return permissions.map(permission => {
if (!permission.includes('.')) {
return `android.permission.${permission}`;
}
return permission;
});
}
function getAndroidPermissions(config) {
var _a, _b;
return (_b = (_a = config.android) === null || _a === void 0 ? void 0 : _a.permissions) !== null && _b !== void 0 ? _b : [];
}
exports.getAndroidPermissions = getAndroidPermissions;
function setAndroidPermissions(config, androidManifest) {
var _a;
const permissions = getAndroidPermissions(config);
let permissionsToAdd = [];
if (permissions === null) {
// Use all Expo permissions
permissionsToAdd = exports.allPermissions;
}
else {
// Use minimum required, plus any specified in permissions array
const providedPermissions = prefixAndroidPermissionsIfNecessary(permissions);
permissionsToAdd = [...providedPermissions, ...exports.requiredPermissions];
}
if (!androidManifest.manifest.hasOwnProperty('uses-permission')) {
androidManifest.manifest['uses-permission'] = [];
}
// manifest.manifest['uses-permission'] = [];
const manifestPermissions = (_a = androidManifest.manifest['uses-permission']) !== null && _a !== void 0 ? _a : [];
permissionsToAdd.forEach(permission => {
if (!isPermissionAlreadyRequested(permission, manifestPermissions)) {
addPermissionToManifest(permission, manifestPermissions);
}
});
return androidManifest;
}
exports.setAndroidPermissions = setAndroidPermissions;
function isPermissionAlreadyRequested(permission, manifestPermissions) {
return manifestPermissions.some(e => e.$['android:name'] === permission);
}
exports.isPermissionAlreadyRequested = isPermissionAlreadyRequested;
function addPermissionToManifest(permission, manifestPermissions) {
manifestPermissions.push({ $: { 'android:name': permission } });
return manifestPermissions;
}
exports.addPermissionToManifest = addPermissionToManifest;
function removePermissions(androidManifest, permissionNames) {
const targetNames = permissionNames ? permissionNames.map(ensurePermissionNameFormat) : null;
const permissions = androidManifest.manifest[USES_PERMISSION] || [];
const nextPermissions = [];
for (const attribute of permissions) {
if (targetNames) {
// @ts-ignore: name isn't part of the type
const value = attribute.$['android:name'] || attribute.$.name;
if (!targetNames.includes(value)) {
nextPermissions.push(attribute);
}
}
}
androidManifest.manifest[USES_PERMISSION] = nextPermissions;
}
exports.removePermissions = removePermissions;
function addPermission(androidManifest, permissionName) {
const usesPermissions = androidManifest.manifest[USES_PERMISSION] || [];
usesPermissions.push({
$: { 'android:name': permissionName },
});
androidManifest.manifest[USES_PERMISSION] = usesPermissions;
}
exports.addPermission = addPermission;
function ensurePermissions(androidManifest, permissionNames) {
const permissions = getPermissions(androidManifest);
const results = {};
for (const permissionName of permissionNames) {
const targetName = ensurePermissionNameFormat(permissionName);
if (!permissions.includes(targetName)) {
addPermission(androidManifest, targetName);
results[permissionName] = true;
}
else {
results[permissionName] = false;
}
}
return results;
}
exports.ensurePermissions = ensurePermissions;
function ensurePermission(androidManifest, permissionName) {
const permissions = getPermissions(androidManifest);
const targetName = ensurePermissionNameFormat(permissionName);
if (!permissions.includes(targetName)) {
addPermission(androidManifest, targetName);
return true;
}
return false;
}
exports.ensurePermission = ensurePermission;
function ensurePermissionNameFormat(permissionName) {
if (permissionName.includes('.')) {
const com = permissionName.split('.');
const name = com.pop();
return [...com, name.toUpperCase()].join('.');
}
else {
// If shorthand form like `WRITE_CONTACTS` is provided, expand it to `android.permission.WRITE_CONTACTS`.
return ensurePermissionNameFormat(`android.permission.${permissionName}`);
}
}
exports.ensurePermissionNameFormat = ensurePermissionNameFormat;
function getPermissions(androidManifest) {
const usesPermissions = androidManifest.manifest[USES_PERMISSION] || [];
const permissions = usesPermissions.map(permissionObject => {
return permissionObject.$['android:name'] || permissionObject.$.name;
});
return permissions;
}
exports.getPermissions = getPermissions;
//# sourceMappingURL=Permissions.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
import { ExpoConfig } from '@expo/config-types';
import { ConfigPlugin } from '../Plugin.types';
export declare const withPrimaryColor: ConfigPlugin;
export declare function getPrimaryColor(config: Pick<ExpoConfig, 'primaryColor'>): string;
export declare function setPrimaryColor(config: Pick<ExpoConfig, 'primaryColor'>, projectRoot: string): Promise<boolean>;

View File

@ -0,0 +1,53 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const core_plugins_1 = require("../plugins/core-plugins");
const XML_1 = require("../utils/XML");
const Colors_1 = require("./Colors");
const Resources_1 = require("./Resources");
const Styles_1 = require("./Styles");
const COLOR_PRIMARY_KEY = 'colorPrimary';
const DEFAULT_PRIMARY_COLOR = '#023c69';
exports.withPrimaryColor = config => {
return core_plugins_1.withDangerousMod(config, [
'android',
async (config) => {
await setPrimaryColor(config, config.modRequest.projectRoot);
return config;
},
]);
};
function getPrimaryColor(config) {
var _a;
return (_a = config.primaryColor) !== null && _a !== void 0 ? _a : DEFAULT_PRIMARY_COLOR;
}
exports.getPrimaryColor = getPrimaryColor;
async function setPrimaryColor(config, projectRoot) {
const hexString = getPrimaryColor(config);
const stylesPath = await Styles_1.getProjectStylesXMLPathAsync(projectRoot);
const colorsPath = await Colors_1.getProjectColorsXMLPathAsync(projectRoot);
let stylesJSON = await Resources_1.readResourcesXMLAsync({ path: stylesPath });
let colorsJSON = await Resources_1.readResourcesXMLAsync({ path: colorsPath });
const colorItemToAdd = Resources_1.buildResourceItem({ name: COLOR_PRIMARY_KEY, value: hexString });
const styleItemToAdd = Resources_1.buildResourceItem({
name: COLOR_PRIMARY_KEY,
value: `@color/${COLOR_PRIMARY_KEY}`,
});
colorsJSON = Colors_1.setColorItem(colorItemToAdd, colorsJSON);
stylesJSON = Styles_1.setStylesItem({
item: styleItemToAdd,
xml: stylesJSON,
parent: { name: 'AppTheme', parent: 'Theme.AppCompat.Light.NoActionBar' },
});
try {
await Promise.all([
XML_1.writeXMLAsync({ path: colorsPath, xml: colorsJSON }),
XML_1.writeXMLAsync({ path: stylesPath, xml: stylesJSON }),
]);
}
catch (e) {
throw new Error(`Error setting Android primary color. Cannot write new styles.xml to ${stylesPath}.`);
}
return true;
}
exports.setPrimaryColor = setPrimaryColor;
//# sourceMappingURL=PrimaryColor.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"PrimaryColor.js","sourceRoot":"","sources":["../../src/android/PrimaryColor.ts"],"names":[],"mappings":";;AAGA,0DAA2D;AAC3D,sCAA6C;AAC7C,qCAAsE;AACtE,2CAAuE;AACvE,qCAAuE;AAEvE,MAAM,iBAAiB,GAAG,cAAc,CAAC;AACzC,MAAM,qBAAqB,GAAG,SAAS,CAAC;AAE3B,QAAA,gBAAgB,GAAiB,MAAM,CAAC,EAAE;IACrD,OAAO,+BAAgB,CAAC,MAAM,EAAE;QAC9B,SAAS;QACT,KAAK,EAAC,MAAM,EAAC,EAAE;YACb,MAAM,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;YAC7D,OAAO,MAAM,CAAC;QAChB,CAAC;KACF,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,SAAgB,eAAe,CAAC,MAAwC;;IACtE,aAAO,MAAM,CAAC,YAAY,mCAAI,qBAAqB,CAAC;AACtD,CAAC;AAFD,0CAEC;AAEM,KAAK,UAAU,eAAe,CACnC,MAAwC,EACxC,WAAmB;IAEnB,MAAM,SAAS,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAE1C,MAAM,UAAU,GAAG,MAAM,qCAA4B,CAAC,WAAW,CAAC,CAAC;IACnE,MAAM,UAAU,GAAG,MAAM,qCAA4B,CAAC,WAAW,CAAC,CAAC;IAEnE,IAAI,UAAU,GAAG,MAAM,iCAAqB,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;IACnE,IAAI,UAAU,GAAG,MAAM,iCAAqB,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;IAEnE,MAAM,cAAc,GAAG,6BAAiB,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IACxF,MAAM,cAAc,GAAG,6BAAiB,CAAC;QACvC,IAAI,EAAE,iBAAiB;QACvB,KAAK,EAAE,UAAU,iBAAiB,EAAE;KACrC,CAAC,CAAC;IAEH,UAAU,GAAG,qBAAY,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;IACtD,UAAU,GAAG,sBAAa,CAAC;QACzB,IAAI,EAAE,cAAc;QACpB,GAAG,EAAE,UAAU;QACf,MAAM,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,mCAAmC,EAAE;KAC1E,CAAC,CAAC;IAEH,IAAI;QACF,MAAM,OAAO,CAAC,GAAG,CAAC;YAChB,mBAAa,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC;YACpD,mBAAa,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC;SACrD,CAAC,CAAC;KACJ;IAAC,OAAO,CAAC,EAAE;QACV,MAAM,IAAI,KAAK,CACb,uEAAuE,UAAU,GAAG,CACrF,CAAC;KACH;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AApCD,0CAoCC","sourcesContent":["import { ExpoConfig } from '@expo/config-types';\n\nimport { ConfigPlugin } from '../Plugin.types';\nimport { withDangerousMod } from '../plugins/core-plugins';\nimport { writeXMLAsync } from '../utils/XML';\nimport { getProjectColorsXMLPathAsync, setColorItem } from './Colors';\nimport { buildResourceItem, readResourcesXMLAsync } from './Resources';\nimport { getProjectStylesXMLPathAsync, setStylesItem } from './Styles';\n\nconst COLOR_PRIMARY_KEY = 'colorPrimary';\nconst DEFAULT_PRIMARY_COLOR = '#023c69';\n\nexport const withPrimaryColor: ConfigPlugin = config => {\n return withDangerousMod(config, [\n 'android',\n async config => {\n await setPrimaryColor(config, config.modRequest.projectRoot);\n return config;\n },\n ]);\n};\n\nexport function getPrimaryColor(config: Pick<ExpoConfig, 'primaryColor'>) {\n return config.primaryColor ?? DEFAULT_PRIMARY_COLOR;\n}\n\nexport async function setPrimaryColor(\n config: Pick<ExpoConfig, 'primaryColor'>,\n projectRoot: string\n) {\n const hexString = getPrimaryColor(config);\n\n const stylesPath = await getProjectStylesXMLPathAsync(projectRoot);\n const colorsPath = await getProjectColorsXMLPathAsync(projectRoot);\n\n let stylesJSON = await readResourcesXMLAsync({ path: stylesPath });\n let colorsJSON = await readResourcesXMLAsync({ path: colorsPath });\n\n const colorItemToAdd = buildResourceItem({ name: COLOR_PRIMARY_KEY, value: hexString });\n const styleItemToAdd = buildResourceItem({\n name: COLOR_PRIMARY_KEY,\n value: `@color/${COLOR_PRIMARY_KEY}`,\n });\n\n colorsJSON = setColorItem(colorItemToAdd, colorsJSON);\n stylesJSON = setStylesItem({\n item: styleItemToAdd,\n xml: stylesJSON,\n parent: { name: 'AppTheme', parent: 'Theme.AppCompat.Light.NoActionBar' },\n });\n\n try {\n await Promise.all([\n writeXMLAsync({ path: colorsPath, xml: colorsJSON }),\n writeXMLAsync({ path: stylesPath, xml: stylesJSON }),\n ]);\n } catch (e) {\n throw new Error(\n `Error setting Android primary color. Cannot write new styles.xml to ${stylesPath}.`\n );\n }\n return true;\n}\n"]}

View File

@ -0,0 +1,12 @@
export declare type PropertiesItem = {
type: 'comment';
value: string;
} | {
type: 'empty';
} | {
type: 'property';
key: string;
value: string;
};
export declare function parsePropertiesFile(contents: string): PropertiesItem[];
export declare function propertiesListToString(props: PropertiesItem[]): string;

View File

@ -0,0 +1,48 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function parsePropertiesFile(contents) {
const propertiesList = [];
const lines = contents.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (!line) {
propertiesList.push({ type: 'empty' });
}
else if (line.startsWith('#')) {
propertiesList.push({ type: 'comment', value: line.substring(1).trimStart() });
}
else {
const eok = line.indexOf('=');
const key = line.slice(0, eok);
const value = line.slice(eok + 1, line.length);
propertiesList.push({ type: 'property', key, value });
}
}
return propertiesList;
}
exports.parsePropertiesFile = parsePropertiesFile;
function propertiesListToString(props) {
let output = '';
for (let i = 0; i < props.length; i++) {
const prop = props[i];
if (prop.type === 'empty') {
output += '';
}
else if (prop.type === 'comment') {
output += '# ' + prop.value;
}
else if (prop.type === 'property') {
output += `${prop.key}=${prop.value}`;
}
else {
// @ts-ignore: assertion
throw new Error(`Invalid properties type "${prop.type}"`);
}
if (i < props.length - 1) {
output += '\n';
}
}
return output;
}
exports.propertiesListToString = propertiesListToString;
//# sourceMappingURL=Properties.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Properties.js","sourceRoot":"","sources":["../../src/android/Properties.ts"],"names":[],"mappings":";;AAcA,SAAgB,mBAAmB,CAAC,QAAgB;IAClD,MAAM,cAAc,GAAqB,EAAE,CAAC;IAC5C,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QACrC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,EAAE;YACT,cAAc,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;SACxC;aAAM,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE;YAC/B,cAAc,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;SAChF;aAAM;YACL,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YAC/C,cAAc,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;SACvD;KACF;IAED,OAAO,cAAc,CAAC;AACxB,CAAC;AAlBD,kDAkBC;AAED,SAAgB,sBAAsB,CAAC,KAAuB;IAC5D,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QACrC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE;YACzB,MAAM,IAAI,EAAE,CAAC;SACd;aAAM,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE;YAClC,MAAM,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC;SAC7B;aAAM,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE;YACnC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;SACvC;aAAM;YACL,wBAAwB;YACxB,MAAM,IAAI,KAAK,CAAC,4BAA4B,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;SAC3D;QACD,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;YACxB,MAAM,IAAI,IAAI,CAAC;SAChB;KACF;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAnBD,wDAmBC","sourcesContent":["export type PropertiesItem =\n | {\n type: 'comment';\n value: string;\n }\n | {\n type: 'empty';\n }\n | {\n type: 'property';\n key: string;\n value: string;\n };\n\nexport function parsePropertiesFile(contents: string): PropertiesItem[] {\n const propertiesList: PropertiesItem[] = [];\n const lines = contents.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n if (!line) {\n propertiesList.push({ type: 'empty' });\n } else if (line.startsWith('#')) {\n propertiesList.push({ type: 'comment', value: line.substring(1).trimStart() });\n } else {\n const eok = line.indexOf('=');\n const key = line.slice(0, eok);\n const value = line.slice(eok + 1, line.length);\n propertiesList.push({ type: 'property', key, value });\n }\n }\n\n return propertiesList;\n}\n\nexport function propertiesListToString(props: PropertiesItem[]): string {\n let output = '';\n for (let i = 0; i < props.length; i++) {\n const prop = props[i];\n if (prop.type === 'empty') {\n output += '';\n } else if (prop.type === 'comment') {\n output += '# ' + prop.value;\n } else if (prop.type === 'property') {\n output += `${prop.key}=${prop.value}`;\n } else {\n // @ts-ignore: assertion\n throw new Error(`Invalid properties type \"${prop.type}\"`);\n }\n if (i < props.length - 1) {\n output += '\\n';\n }\n }\n return output;\n}\n"]}

View File

@ -0,0 +1,57 @@
import { XMLObject } from '../utils/XML';
export declare type ResourceGroupXML = {
$: {
name: string;
parent: string;
};
item: ResourceItemXML[];
};
export declare type ResourceXML = {
resources: {
color?: ResourceItemXML[];
string?: ResourceItemXML[];
style?: ResourceGroupXML[];
};
};
export declare type ResourceItemXML = {
_: string;
$: {
name: string;
};
};
/**
* Name of the resource folder.
*/
export declare type ResourceKind = 'values' | 'values-night' | 'values-v23';
/**
* Read an XML file while providing a default fallback for resource files.
*
* @param options path to the XML file, returns a fallback XML if the path doesn't exist.
*/
export declare function readResourcesXMLAsync({ path, fallback, }: {
path: string;
fallback?: string | null;
}): Promise<ResourceXML>;
/**
* Ensure the provided xml has a `resources` object (the expected shape).
*
* @param xml
*/
export declare function ensureDefaultResourceXML(xml: XMLObject): ResourceXML;
/**
* Build a `ResourceItemXML` given its `name` and `value`. This makes things a bit more readable.
*
* - JSON: `{ $: { name }, _: value }`
* - XML: `<item name="NAME">VALUE</item>`
*
* @param props name and value strings.
*/
export declare function buildResourceItem({ name, value, }: {
name: string;
value: string;
}): ResourceItemXML;
export declare function buildResourceGroup(parent: {
name: string;
parent: string;
items?: ResourceItemXML[];
}): ResourceGroupXML;

View File

@ -0,0 +1,54 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const XML_1 = require("../utils/XML");
const fallbackResourceString = `<?xml version="1.0" encoding="utf-8"?><resources></resources>`;
/**
* Read an XML file while providing a default fallback for resource files.
*
* @param options path to the XML file, returns a fallback XML if the path doesn't exist.
*/
async function readResourcesXMLAsync({ path, fallback = fallbackResourceString, }) {
const xml = await XML_1.readXMLAsync({ path, fallback });
// Ensure the type is expected.
if (!xml.resources) {
xml.resources = {};
}
return xml;
}
exports.readResourcesXMLAsync = readResourcesXMLAsync;
/**
* Ensure the provided xml has a `resources` object (the expected shape).
*
* @param xml
*/
function ensureDefaultResourceXML(xml) {
if (!xml) {
xml = { resources: {} };
}
if (!xml.resources) {
xml.resources = {};
}
return xml;
}
exports.ensureDefaultResourceXML = ensureDefaultResourceXML;
/**
* Build a `ResourceItemXML` given its `name` and `value`. This makes things a bit more readable.
*
* - JSON: `{ $: { name }, _: value }`
* - XML: `<item name="NAME">VALUE</item>`
*
* @param props name and value strings.
*/
function buildResourceItem({ name, value, }) {
return { $: { name }, _: value };
}
exports.buildResourceItem = buildResourceItem;
function buildResourceGroup(parent) {
var _a;
return {
$: { name: parent.name, parent: parent.parent },
item: (_a = parent.items) !== null && _a !== void 0 ? _a : [],
};
}
exports.buildResourceGroup = buildResourceGroup;
//# sourceMappingURL=Resources.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Resources.js","sourceRoot":"","sources":["../../src/android/Resources.ts"],"names":[],"mappings":";;AAAA,sCAAuD;AA8BvD,MAAM,sBAAsB,GAAG,+DAA+D,CAAC;AAE/F;;;;GAIG;AACI,KAAK,UAAU,qBAAqB,CAAC,EAC1C,IAAI,EACJ,QAAQ,GAAG,sBAAsB,GAIlC;IACC,MAAM,GAAG,GAAG,MAAM,kBAAY,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;IACnD,+BAA+B;IAC/B,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE;QAClB,GAAG,CAAC,SAAS,GAAG,EAAE,CAAC;KACpB;IACD,OAAO,GAAkB,CAAC;AAC5B,CAAC;AAbD,sDAaC;AAED;;;;GAIG;AACH,SAAgB,wBAAwB,CAAC,GAAc;IACrD,IAAI,CAAC,GAAG,EAAE;QACR,GAAG,GAAG,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;KACzB;IACD,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE;QAClB,GAAG,CAAC,SAAS,GAAG,EAAE,CAAC;KACpB;IAED,OAAO,GAAkB,CAAC;AAC5B,CAAC;AATD,4DASC;AAED;;;;;;;GAOG;AACH,SAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,KAAK,GAIN;IACC,OAAO,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC;AACnC,CAAC;AARD,8CAQC;AAED,SAAgB,kBAAkB,CAAC,MAIlC;;IACC,OAAO;QACL,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;QAC/C,IAAI,QAAE,MAAM,CAAC,KAAK,mCAAI,EAAE;KACzB,CAAC;AACJ,CAAC;AATD,gDASC","sourcesContent":["import { readXMLAsync, XMLObject } from '../utils/XML';\n\nexport type ResourceGroupXML = {\n $: {\n name: string;\n parent: string;\n };\n item: ResourceItemXML[];\n};\n\nexport type ResourceXML = {\n resources: {\n color?: ResourceItemXML[];\n string?: ResourceItemXML[];\n style?: ResourceGroupXML[];\n // Add more if needed...\n };\n};\n\nexport type ResourceItemXML = {\n _: string;\n $: {\n name: string;\n };\n};\n/**\n * Name of the resource folder.\n */\nexport type ResourceKind = 'values' | 'values-night' | 'values-v23';\n\nconst fallbackResourceString = `<?xml version=\"1.0\" encoding=\"utf-8\"?><resources></resources>`;\n\n/**\n * Read an XML file while providing a default fallback for resource files.\n *\n * @param options path to the XML file, returns a fallback XML if the path doesn't exist.\n */\nexport async function readResourcesXMLAsync({\n path,\n fallback = fallbackResourceString,\n}: {\n path: string;\n fallback?: string | null;\n}): Promise<ResourceXML> {\n const xml = await readXMLAsync({ path, fallback });\n // Ensure the type is expected.\n if (!xml.resources) {\n xml.resources = {};\n }\n return xml as ResourceXML;\n}\n\n/**\n * Ensure the provided xml has a `resources` object (the expected shape).\n *\n * @param xml\n */\nexport function ensureDefaultResourceXML(xml: XMLObject): ResourceXML {\n if (!xml) {\n xml = { resources: {} };\n }\n if (!xml.resources) {\n xml.resources = {};\n }\n\n return xml as ResourceXML;\n}\n\n/**\n * Build a `ResourceItemXML` given its `name` and `value`. This makes things a bit more readable.\n *\n * - JSON: `{ $: { name }, _: value }`\n * - XML: `<item name=\"NAME\">VALUE</item>`\n *\n * @param props name and value strings.\n */\nexport function buildResourceItem({\n name,\n value,\n}: {\n name: string;\n value: string;\n}): ResourceItemXML {\n return { $: { name }, _: value };\n}\n\nexport function buildResourceGroup(parent: {\n name: string;\n parent: string;\n items?: ResourceItemXML[];\n}): ResourceGroupXML {\n return {\n $: { name: parent.name, parent: parent.parent },\n item: parent.items ?? [],\n };\n}\n"]}

View File

@ -0,0 +1,5 @@
import { ExpoConfig } from '@expo/config-types';
import { ConfigPlugin } from '../Plugin.types';
export declare const withRootViewBackgroundColor: ConfigPlugin;
export declare function getRootViewBackgroundColor(config: Pick<ExpoConfig, 'android' | 'backgroundColor'>): string | null;
export declare function setRootViewBackgroundColor(config: Pick<ExpoConfig, 'android' | 'backgroundColor'>, projectRoot: string): Promise<boolean>;

View File

@ -0,0 +1,62 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const core_plugins_1 = require("../plugins/core-plugins");
const XML_1 = require("../utils/XML");
const Colors_1 = require("./Colors");
const Resources_1 = require("./Resources");
const Styles_1 = require("./Styles");
const ANDROID_WINDOW_BACKGROUND = 'android:windowBackground';
const WINDOW_BACKGROUND_COLOR = 'activityBackground';
exports.withRootViewBackgroundColor = config => {
return core_plugins_1.withDangerousMod(config, [
'android',
async (config) => {
await setRootViewBackgroundColor(config, config.modRequest.projectRoot);
return config;
},
]);
};
function getRootViewBackgroundColor(config) {
var _a;
if ((_a = config.android) === null || _a === void 0 ? void 0 : _a.backgroundColor) {
return config.android.backgroundColor;
}
if (config.backgroundColor) {
return config.backgroundColor;
}
return null;
}
exports.getRootViewBackgroundColor = getRootViewBackgroundColor;
async function setRootViewBackgroundColor(config, projectRoot) {
const hexString = getRootViewBackgroundColor(config);
if (!hexString) {
return false;
}
const stylesPath = await Styles_1.getProjectStylesXMLPathAsync(projectRoot);
const colorsPath = await Colors_1.getProjectColorsXMLPathAsync(projectRoot);
let stylesJSON = await Resources_1.readResourcesXMLAsync({ path: stylesPath });
let colorsJSON = await Resources_1.readResourcesXMLAsync({ path: colorsPath });
const colorItemToAdd = Resources_1.buildResourceItem({ name: WINDOW_BACKGROUND_COLOR, value: hexString });
const styleItemToAdd = Resources_1.buildResourceItem({
name: ANDROID_WINDOW_BACKGROUND,
value: `@color/${WINDOW_BACKGROUND_COLOR}`,
});
colorsJSON = Colors_1.setColorItem(colorItemToAdd, colorsJSON);
stylesJSON = Styles_1.setStylesItem({
item: styleItemToAdd,
xml: stylesJSON,
parent: { name: 'AppTheme', parent: 'Theme.AppCompat.Light.NoActionBar' },
});
try {
await Promise.all([
XML_1.writeXMLAsync({ path: colorsPath, xml: colorsJSON }),
XML_1.writeXMLAsync({ path: stylesPath, xml: stylesJSON }),
]);
}
catch (e) {
throw new Error(`Error setting Android root view background color. Cannot write new styles.xml to ${stylesPath}.`);
}
return true;
}
exports.setRootViewBackgroundColor = setRootViewBackgroundColor;
//# sourceMappingURL=RootViewBackgroundColor.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"RootViewBackgroundColor.js","sourceRoot":"","sources":["../../src/android/RootViewBackgroundColor.ts"],"names":[],"mappings":";;AAGA,0DAA2D;AAC3D,sCAA6C;AAC7C,qCAAsE;AACtE,2CAAuE;AACvE,qCAAuE;AAEvE,MAAM,yBAAyB,GAAG,0BAA0B,CAAC;AAC7D,MAAM,uBAAuB,GAAG,oBAAoB,CAAC;AAExC,QAAA,2BAA2B,GAAiB,MAAM,CAAC,EAAE;IAChE,OAAO,+BAAgB,CAAC,MAAM,EAAE;QAC9B,SAAS;QACT,KAAK,EAAC,MAAM,EAAC,EAAE;YACb,MAAM,0BAA0B,CAAC,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;YACxE,OAAO,MAAM,CAAC;QAChB,CAAC;KACF,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,SAAgB,0BAA0B,CACxC,MAAuD;;IAEvD,UAAI,MAAM,CAAC,OAAO,0CAAE,eAAe,EAAE;QACnC,OAAO,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC;KACvC;IACD,IAAI,MAAM,CAAC,eAAe,EAAE;QAC1B,OAAO,MAAM,CAAC,eAAe,CAAC;KAC/B;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAXD,gEAWC;AAEM,KAAK,UAAU,0BAA0B,CAC9C,MAAuD,EACvD,WAAmB;IAEnB,MAAM,SAAS,GAAG,0BAA0B,CAAC,MAAM,CAAC,CAAC;IACrD,IAAI,CAAC,SAAS,EAAE;QACd,OAAO,KAAK,CAAC;KACd;IAED,MAAM,UAAU,GAAG,MAAM,qCAA4B,CAAC,WAAW,CAAC,CAAC;IACnE,MAAM,UAAU,GAAG,MAAM,qCAA4B,CAAC,WAAW,CAAC,CAAC;IAEnE,IAAI,UAAU,GAAG,MAAM,iCAAqB,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;IACnE,IAAI,UAAU,GAAG,MAAM,iCAAqB,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;IAEnE,MAAM,cAAc,GAAG,6BAAiB,CAAC,EAAE,IAAI,EAAE,uBAAuB,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAC9F,MAAM,cAAc,GAAG,6BAAiB,CAAC;QACvC,IAAI,EAAE,yBAAyB;QAC/B,KAAK,EAAE,UAAU,uBAAuB,EAAE;KAC3C,CAAC,CAAC;IAEH,UAAU,GAAG,qBAAY,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;IACtD,UAAU,GAAG,sBAAa,CAAC;QACzB,IAAI,EAAE,cAAc;QACpB,GAAG,EAAE,UAAU;QACf,MAAM,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,mCAAmC,EAAE;KAC1E,CAAC,CAAC;IAEH,IAAI;QACF,MAAM,OAAO,CAAC,GAAG,CAAC;YAChB,mBAAa,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC;YACpD,mBAAa,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC;SACrD,CAAC,CAAC;KACJ;IAAC,OAAO,CAAC,EAAE;QACV,MAAM,IAAI,KAAK,CACb,oFAAoF,UAAU,GAAG,CAClG,CAAC;KACH;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAvCD,gEAuCC","sourcesContent":["import { ExpoConfig } from '@expo/config-types';\n\nimport { ConfigPlugin } from '../Plugin.types';\nimport { withDangerousMod } from '../plugins/core-plugins';\nimport { writeXMLAsync } from '../utils/XML';\nimport { getProjectColorsXMLPathAsync, setColorItem } from './Colors';\nimport { buildResourceItem, readResourcesXMLAsync } from './Resources';\nimport { getProjectStylesXMLPathAsync, setStylesItem } from './Styles';\n\nconst ANDROID_WINDOW_BACKGROUND = 'android:windowBackground';\nconst WINDOW_BACKGROUND_COLOR = 'activityBackground';\n\nexport const withRootViewBackgroundColor: ConfigPlugin = config => {\n return withDangerousMod(config, [\n 'android',\n async config => {\n await setRootViewBackgroundColor(config, config.modRequest.projectRoot);\n return config;\n },\n ]);\n};\n\nexport function getRootViewBackgroundColor(\n config: Pick<ExpoConfig, 'android' | 'backgroundColor'>\n) {\n if (config.android?.backgroundColor) {\n return config.android.backgroundColor;\n }\n if (config.backgroundColor) {\n return config.backgroundColor;\n }\n\n return null;\n}\n\nexport async function setRootViewBackgroundColor(\n config: Pick<ExpoConfig, 'android' | 'backgroundColor'>,\n projectRoot: string\n) {\n const hexString = getRootViewBackgroundColor(config);\n if (!hexString) {\n return false;\n }\n\n const stylesPath = await getProjectStylesXMLPathAsync(projectRoot);\n const colorsPath = await getProjectColorsXMLPathAsync(projectRoot);\n\n let stylesJSON = await readResourcesXMLAsync({ path: stylesPath });\n let colorsJSON = await readResourcesXMLAsync({ path: colorsPath });\n\n const colorItemToAdd = buildResourceItem({ name: WINDOW_BACKGROUND_COLOR, value: hexString });\n const styleItemToAdd = buildResourceItem({\n name: ANDROID_WINDOW_BACKGROUND,\n value: `@color/${WINDOW_BACKGROUND_COLOR}`,\n });\n\n colorsJSON = setColorItem(colorItemToAdd, colorsJSON);\n stylesJSON = setStylesItem({\n item: styleItemToAdd,\n xml: stylesJSON,\n parent: { name: 'AppTheme', parent: 'Theme.AppCompat.Light.NoActionBar' },\n });\n\n try {\n await Promise.all([\n writeXMLAsync({ path: colorsPath, xml: colorsJSON }),\n writeXMLAsync({ path: stylesPath, xml: stylesJSON }),\n ]);\n } catch (e) {\n throw new Error(\n `Error setting Android root view background color. Cannot write new styles.xml to ${stylesPath}.`\n );\n }\n return true;\n}\n"]}

View File

@ -0,0 +1,17 @@
import { ExpoConfig } from '@expo/config-types';
import { AndroidManifest } from './Manifest';
export declare type IntentFilterProps = {
actions: string[];
categories: string[];
schemes: string[];
};
export declare const withScheme: import("..").ConfigPlugin<void>;
export declare function getScheme(config: {
scheme?: string | string[];
}): string[];
export declare function setScheme(config: Pick<ExpoConfig, 'scheme' | 'android'>, androidManifest: AndroidManifest): AndroidManifest;
export declare function getSchemesFromManifest(androidManifest: AndroidManifest): string[];
export declare function ensureManifestHasValidIntentFilter(androidManifest: AndroidManifest): boolean;
export declare function hasScheme(scheme: string, androidManifest: AndroidManifest): boolean;
export declare function appendScheme(scheme: string, androidManifest: AndroidManifest): AndroidManifest;
export declare function removeScheme(scheme: string, androidManifest: AndroidManifest): AndroidManifest;

View File

@ -0,0 +1,191 @@
"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const android_plugins_1 = require("../plugins/android-plugins");
const WarningAggregator = __importStar(require("../utils/warnings"));
exports.withScheme = android_plugins_1.createAndroidManifestPlugin(setScheme, 'withScheme');
function getScheme(config) {
if (Array.isArray(config.scheme)) {
const validate = (value) => typeof value === 'string';
return config.scheme.filter(validate);
}
else if (typeof config.scheme === 'string') {
return [config.scheme];
}
return [];
}
exports.getScheme = getScheme;
// This plugin used to remove the unused schemes but this is unpredictable because other plugins could add schemes.
// The only way to reliably remove schemes from the project is to nuke the file and regenerate the code (`expo prebuild --clean`).
// Regardless, having extra schemes isn't a fatal issue and therefore a tolerable compromise is to just add new schemes that aren't currently present.
function setScheme(config, androidManifest) {
var _a, _b;
const schemes = [
...getScheme(config),
// @ts-ignore: TODO: android.scheme is an unreleased -- harder to add to turtle v1.
...getScheme((_a = config.android) !== null && _a !== void 0 ? _a : {}),
];
// Add the package name to the list of schemes for easier Google auth and parity with Turtle v1.
if ((_b = config.android) === null || _b === void 0 ? void 0 : _b.package) {
schemes.push(config.android.package);
}
if (schemes.length === 0) {
return androidManifest;
}
if (!ensureManifestHasValidIntentFilter(androidManifest)) {
WarningAggregator.addWarningAndroid('scheme', `Cannot add schemes because the provided manifest does not have a valid Activity with \`android:launchMode="singleTask"\`.\nThis guide can help you get setup properly https://expo.fyi/setup-android-uri-scheme`);
return androidManifest;
}
// Get the current schemes and remove them from the list of schemes to add.
const currentSchemes = getSchemesFromManifest(androidManifest);
for (const uri of currentSchemes) {
const index = schemes.indexOf(uri);
if (index > -1)
schemes.splice(index, 1);
}
// Now add all of the remaining schemes.
for (const uri of schemes) {
androidManifest = appendScheme(uri, androidManifest);
}
return androidManifest;
}
exports.setScheme = setScheme;
function isValidRedirectIntentFilter({ actions, categories, schemes }) {
return (actions.includes('android.intent.action.VIEW') &&
!categories.includes('android.intent.category.LAUNCHER'));
}
function propertiesFromIntentFilter(intentFilter) {
var _a, _b, _c, _d, _e, _f;
const actions = (_b = (_a = intentFilter === null || intentFilter === void 0 ? void 0 : intentFilter.action) === null || _a === void 0 ? void 0 : _a.map((data) => { var _a; return (_a = data === null || data === void 0 ? void 0 : data.$) === null || _a === void 0 ? void 0 : _a['android:name']; })) !== null && _b !== void 0 ? _b : [];
const categories = (_d = (_c = intentFilter === null || intentFilter === void 0 ? void 0 : intentFilter.category) === null || _c === void 0 ? void 0 : _c.map((data) => { var _a; return (_a = data === null || data === void 0 ? void 0 : data.$) === null || _a === void 0 ? void 0 : _a['android:name']; })) !== null && _d !== void 0 ? _d : [];
const schemes = (_f = (_e = intentFilter === null || intentFilter === void 0 ? void 0 : intentFilter.data) === null || _e === void 0 ? void 0 : _e.map((data) => { var _a; return (_a = data === null || data === void 0 ? void 0 : data.$) === null || _a === void 0 ? void 0 : _a['android:scheme']; })) !== null && _f !== void 0 ? _f : [];
return {
schemes,
actions,
categories,
};
}
function getSingleTaskIntentFilters(androidManifest) {
if (!Array.isArray(androidManifest.manifest.application))
return [];
let outputSchemes = [];
for (const application of androidManifest.manifest.application) {
const { activity } = application;
// @ts-ignore
const activities = Array.isArray(activity) ? activity : [activity];
const singleTaskActivities = activities.filter(activity => { var _a; return ((_a = activity === null || activity === void 0 ? void 0 : activity.$) === null || _a === void 0 ? void 0 : _a['android:launchMode']) === 'singleTask'; });
for (const activity of singleTaskActivities) {
const intentFilters = activity['intent-filter'];
outputSchemes = outputSchemes.concat(intentFilters);
}
}
return outputSchemes;
}
function getSchemesFromManifest(androidManifest) {
const outputSchemes = [];
const singleTaskIntentFilters = getSingleTaskIntentFilters(androidManifest);
for (const intentFilter of singleTaskIntentFilters) {
const properties = propertiesFromIntentFilter(intentFilter);
if (isValidRedirectIntentFilter(properties)) {
outputSchemes.push(properties);
}
}
return outputSchemes.reduce((prev, { schemes }) => [...prev, ...schemes], []);
}
exports.getSchemesFromManifest = getSchemesFromManifest;
function ensureManifestHasValidIntentFilter(androidManifest) {
var _a;
if (!Array.isArray(androidManifest.manifest.application)) {
return false;
}
for (const application of androidManifest.manifest.application) {
for (const activity of application.activity || []) {
if (((_a = activity === null || activity === void 0 ? void 0 : activity.$) === null || _a === void 0 ? void 0 : _a['android:launchMode']) === 'singleTask') {
for (const intentFilter of activity['intent-filter'] || []) {
// Parse valid intent filters...
const properties = propertiesFromIntentFilter(intentFilter);
if (isValidRedirectIntentFilter(properties)) {
return true;
}
}
if (!activity['intent-filter']) {
activity['intent-filter'] = [];
}
activity['intent-filter'].push({
action: [{ $: { 'android:name': 'android.intent.action.VIEW' } }],
category: [
{ $: { 'android:name': 'android.intent.category.DEFAULT' } },
{ $: { 'android:name': 'android.intent.category.BROWSABLE' } },
],
});
return true;
}
}
}
return false;
}
exports.ensureManifestHasValidIntentFilter = ensureManifestHasValidIntentFilter;
function hasScheme(scheme, androidManifest) {
const schemes = getSchemesFromManifest(androidManifest);
return schemes.includes(scheme);
}
exports.hasScheme = hasScheme;
function appendScheme(scheme, androidManifest) {
var _a;
if (!Array.isArray(androidManifest.manifest.application)) {
return androidManifest;
}
for (const application of androidManifest.manifest.application) {
for (const activity of application.activity || []) {
if (((_a = activity === null || activity === void 0 ? void 0 : activity.$) === null || _a === void 0 ? void 0 : _a['android:launchMode']) === 'singleTask') {
for (const intentFilter of activity['intent-filter'] || []) {
const properties = propertiesFromIntentFilter(intentFilter);
if (isValidRedirectIntentFilter(properties)) {
if (!intentFilter.data)
intentFilter.data = [];
intentFilter.data.push({
$: { 'android:scheme': scheme },
});
}
}
break;
}
}
}
return androidManifest;
}
exports.appendScheme = appendScheme;
function removeScheme(scheme, androidManifest) {
var _a, _b, _c, _d;
if (!Array.isArray(androidManifest.manifest.application)) {
return androidManifest;
}
for (const application of androidManifest.manifest.application) {
for (const activity of application.activity || []) {
if (((_a = activity === null || activity === void 0 ? void 0 : activity.$) === null || _a === void 0 ? void 0 : _a['android:launchMode']) === 'singleTask') {
for (const intentFilter of activity['intent-filter'] || []) {
// Parse valid intent filters...
const properties = propertiesFromIntentFilter(intentFilter);
if (isValidRedirectIntentFilter(properties)) {
for (const dataKey in (intentFilter === null || intentFilter === void 0 ? void 0 : intentFilter.data) || []) {
const data = (_b = intentFilter.data) === null || _b === void 0 ? void 0 : _b[dataKey];
if (((_c = data === null || data === void 0 ? void 0 : data.$) === null || _c === void 0 ? void 0 : _c['android:scheme']) === scheme) {
(_d = intentFilter.data) === null || _d === void 0 ? true : delete _d[dataKey];
}
}
}
}
break;
}
}
}
return androidManifest;
}
exports.removeScheme = removeScheme;
//# sourceMappingURL=Scheme.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,6 @@
import { ExpoConfig } from '@expo/config-types';
import { AndroidSplashScreenConfig } from '@expo/configure-splash-screen';
import { ConfigPlugin } from '../Plugin.types';
export declare const withSplashScreen: ConfigPlugin;
export declare function getSplashScreenConfig(config: ExpoConfig): AndroidSplashScreenConfig | undefined;
export declare function setSplashScreenAsync(config: ExpoConfig, projectRoot: string): Promise<void>;

View File

@ -0,0 +1,53 @@
"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const configure_splash_screen_1 = require("@expo/configure-splash-screen");
const core_plugins_1 = require("../plugins/core-plugins");
const WarningAggregator = __importStar(require("../utils/warnings"));
exports.withSplashScreen = config => {
return core_plugins_1.withDangerousMod(config, [
'android',
async (config) => {
await setSplashScreenAsync(config, config.modRequest.projectRoot);
return config;
},
]);
};
function getSplashScreenConfig(config) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2;
if (!config.splash && !((_a = config.android) === null || _a === void 0 ? void 0 : _a.splash)) {
return;
}
const result = {
imageResizeMode: (_f = (_d = (_c = (_b = config.android) === null || _b === void 0 ? void 0 : _b.splash) === null || _c === void 0 ? void 0 : _c.resizeMode) !== null && _d !== void 0 ? _d : (_e = config.splash) === null || _e === void 0 ? void 0 : _e.resizeMode) !== null && _f !== void 0 ? _f : configure_splash_screen_1.SplashScreenImageResizeMode.CONTAIN,
backgroundColor: (_l = (_j = (_h = (_g = config.android) === null || _g === void 0 ? void 0 : _g.splash) === null || _h === void 0 ? void 0 : _h.backgroundColor) !== null && _j !== void 0 ? _j : (_k = config.splash) === null || _k === void 0 ? void 0 : _k.backgroundColor) !== null && _l !== void 0 ? _l : '#FFFFFF',
image: (_1 = (_y = (_v = (_s = (_p = (_o = (_m = config.android) === null || _m === void 0 ? void 0 : _m.splash) === null || _o === void 0 ? void 0 : _o.xxxhdpi) !== null && _p !== void 0 ? _p : (_r = (_q = config.android) === null || _q === void 0 ? void 0 : _q.splash) === null || _r === void 0 ? void 0 : _r.xxhdpi) !== null && _s !== void 0 ? _s : (_u = (_t = config.android) === null || _t === void 0 ? void 0 : _t.splash) === null || _u === void 0 ? void 0 : _u.xhdpi) !== null && _v !== void 0 ? _v : (_x = (_w = config.android) === null || _w === void 0 ? void 0 : _w.splash) === null || _x === void 0 ? void 0 : _x.hdpi) !== null && _y !== void 0 ? _y : (_0 = (_z = config.android) === null || _z === void 0 ? void 0 : _z.splash) === null || _0 === void 0 ? void 0 : _0.mdpi) !== null && _1 !== void 0 ? _1 : (_2 = config.splash) === null || _2 === void 0 ? void 0 : _2.image,
};
return result;
}
exports.getSplashScreenConfig = getSplashScreenConfig;
async function setSplashScreenAsync(config, projectRoot) {
const splashScreenIsSupported = config.sdkVersion === '39.0.0' || config.sdkVersion === '40.0.0' || !config.sdkVersion;
if (!splashScreenIsSupported) {
WarningAggregator.addWarningAndroid('splash', 'Unable to automatically configure splash screen. Please refer to the expo-splash-screen README for more information: https://github.com/expo/expo/tree/master/packages/expo-splash-screen');
return;
}
const splashConfig = getSplashScreenConfig(config);
if (!splashConfig) {
return;
}
try {
await configure_splash_screen_1.configureAndroidSplashScreen(projectRoot, splashConfig);
}
catch (e) {
WarningAggregator.addWarningAndroid('splash', e);
}
}
exports.setSplashScreenAsync = setSplashScreenAsync;
//# sourceMappingURL=SplashScreen.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"SplashScreen.js","sourceRoot":"","sources":["../../src/android/SplashScreen.ts"],"names":[],"mappings":";;;;;;;;;AACA,2EAIuC;AAGvC,0DAA2D;AAC3D,qEAAuD;AAE1C,QAAA,gBAAgB,GAAiB,MAAM,CAAC,EAAE;IACrD,OAAO,+BAAgB,CAAC,MAAM,EAAE;QAC9B,SAAS;QACT,KAAK,EAAC,MAAM,EAAC,EAAE;YACb,MAAM,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;YAClE,OAAO,MAAM,CAAC;QAChB,CAAC;KACF,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,SAAgB,qBAAqB,CAAC,MAAkB;;IACtD,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,QAAC,MAAM,CAAC,OAAO,0CAAE,MAAM,CAAA,EAAE;QAC7C,OAAO;KACR;IAED,MAAM,MAAM,GAA8B;QACxC,eAAe,0BACb,MAAM,CAAC,OAAO,0CAAE,MAAM,0CAAE,UAAU,yCAClC,MAAM,CAAC,MAAM,0CAAE,UAAU,mCACzB,qDAA2B,CAAC,OAAO;QACrC,eAAe,0BACb,MAAM,CAAC,OAAO,0CAAE,MAAM,0CAAE,eAAe,yCAAI,MAAM,CAAC,MAAM,0CAAE,eAAe,mCAAI,SAAS;QACxF,KAAK,4CACH,MAAM,CAAC,OAAO,0CAAE,MAAM,0CAAE,OAAO,+CAC/B,MAAM,CAAC,OAAO,0CAAE,MAAM,0CAAE,MAAM,+CAC9B,MAAM,CAAC,OAAO,0CAAE,MAAM,0CAAE,KAAK,+CAC7B,MAAM,CAAC,OAAO,0CAAE,MAAM,0CAAE,IAAI,+CAC5B,MAAM,CAAC,OAAO,0CAAE,MAAM,0CAAE,IAAI,yCAC5B,MAAM,CAAC,MAAM,0CAAE,KAAK;KACvB,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC;AAtBD,sDAsBC;AAEM,KAAK,UAAU,oBAAoB,CAAC,MAAkB,EAAE,WAAmB;IAChF,MAAM,uBAAuB,GAC3B,MAAM,CAAC,UAAU,KAAK,QAAQ,IAAI,MAAM,CAAC,UAAU,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;IACzF,IAAI,CAAC,uBAAuB,EAAE;QAC5B,iBAAiB,CAAC,iBAAiB,CACjC,QAAQ,EACR,2LAA2L,CAC5L,CAAC;QAEF,OAAO;KACR;IAED,MAAM,YAAY,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACnD,IAAI,CAAC,YAAY,EAAE;QACjB,OAAO;KACR;IAED,IAAI;QACF,MAAM,sDAA4B,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;KAC/D;IAAC,OAAO,CAAC,EAAE;QACV,iBAAiB,CAAC,iBAAiB,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;KAClD;AACH,CAAC;AAtBD,oDAsBC","sourcesContent":["import { ExpoConfig } from '@expo/config-types';\nimport {\n AndroidSplashScreenConfig,\n configureAndroidSplashScreen,\n SplashScreenImageResizeMode,\n} from '@expo/configure-splash-screen';\n\nimport { ConfigPlugin } from '../Plugin.types';\nimport { withDangerousMod } from '../plugins/core-plugins';\nimport * as WarningAggregator from '../utils/warnings';\n\nexport const withSplashScreen: ConfigPlugin = config => {\n return withDangerousMod(config, [\n 'android',\n async config => {\n await setSplashScreenAsync(config, config.modRequest.projectRoot);\n return config;\n },\n ]);\n};\n\nexport function getSplashScreenConfig(config: ExpoConfig): AndroidSplashScreenConfig | undefined {\n if (!config.splash && !config.android?.splash) {\n return;\n }\n\n const result: AndroidSplashScreenConfig = {\n imageResizeMode:\n config.android?.splash?.resizeMode ??\n config.splash?.resizeMode ??\n SplashScreenImageResizeMode.CONTAIN,\n backgroundColor:\n config.android?.splash?.backgroundColor ?? config.splash?.backgroundColor ?? '#FFFFFF', // white\n image:\n config.android?.splash?.xxxhdpi ??\n config.android?.splash?.xxhdpi ??\n config.android?.splash?.xhdpi ??\n config.android?.splash?.hdpi ??\n config.android?.splash?.mdpi ??\n config.splash?.image,\n };\n\n return result;\n}\n\nexport async function setSplashScreenAsync(config: ExpoConfig, projectRoot: string) {\n const splashScreenIsSupported =\n config.sdkVersion === '39.0.0' || config.sdkVersion === '40.0.0' || !config.sdkVersion;\n if (!splashScreenIsSupported) {\n WarningAggregator.addWarningAndroid(\n 'splash',\n 'Unable to automatically configure splash screen. Please refer to the expo-splash-screen README for more information: https://github.com/expo/expo/tree/master/packages/expo-splash-screen'\n );\n\n return;\n }\n\n const splashConfig = getSplashScreenConfig(config);\n if (!splashConfig) {\n return;\n }\n\n try {\n await configureAndroidSplashScreen(projectRoot, splashConfig);\n } catch (e) {\n WarningAggregator.addWarningAndroid('splash', e);\n }\n}\n"]}

View File

@ -0,0 +1,6 @@
import { ExpoConfig } from '@expo/config-types';
import { ConfigPlugin } from '../Plugin.types';
export declare const withStatusBar: ConfigPlugin;
export declare function getStatusBarColor(config: Pick<ExpoConfig, 'androidStatusBarColor' | 'androidStatusBar'>): string;
export declare function getStatusBarStyle(config: Pick<ExpoConfig, 'androidStatusBar'>): "light-content" | "dark-content";
export declare function setStatusBarConfig(config: Pick<ExpoConfig, 'androidStatusBarColor' | 'androidStatusBar'>, projectRoot: string): Promise<boolean>;

View File

@ -0,0 +1,91 @@
"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const core_plugins_1 = require("../plugins/core-plugins");
const XML_1 = require("../utils/XML");
const WarningAggregator = __importStar(require("../utils/warnings"));
const Colors_1 = require("./Colors");
const Resources_1 = require("./Resources");
const Styles_1 = require("./Styles");
const COLOR_PRIMARY_DARK_KEY = 'colorPrimaryDark';
const WINDOW_TRANSLUCENT_STATUS = 'android:windowTranslucentStatus';
const WINDOW_LIGHT_STATUS_BAR = 'android:windowLightStatusBar';
exports.withStatusBar = config => {
return core_plugins_1.withDangerousMod(config, [
'android',
async (config) => {
await setStatusBarConfig(config, config.modRequest.projectRoot);
return config;
},
]);
};
function getStatusBarColor(config) {
var _a;
if (config.androidStatusBarColor != null) {
WarningAggregator.addWarningAndroid('status-bar', '`androidStatusBarColor` is deprecated, use `androidStatusBar.backgroundColor` instead.');
}
return ((_a = config.androidStatusBar) === null || _a === void 0 ? void 0 : _a.backgroundColor) || 'translucent';
}
exports.getStatusBarColor = getStatusBarColor;
function getStatusBarStyle(config) {
var _a;
return ((_a = config.androidStatusBar) === null || _a === void 0 ? void 0 : _a.barStyle) || 'light-content';
}
exports.getStatusBarStyle = getStatusBarStyle;
async function setStatusBarConfig(config, projectRoot) {
const hexString = getStatusBarColor(config);
const statusBarStyle = getStatusBarStyle(config);
const stylesPath = await Styles_1.getProjectStylesXMLPathAsync(projectRoot);
const colorsPath = await Colors_1.getProjectColorsXMLPathAsync(projectRoot);
let stylesJSON = await Resources_1.readResourcesXMLAsync({ path: stylesPath });
let colorsJSON = await Resources_1.readResourcesXMLAsync({ path: colorsPath });
let styleItemToAdd;
if (hexString === 'translucent') {
// translucent status bar set in theme
styleItemToAdd = Resources_1.buildResourceItem({ name: WINDOW_TRANSLUCENT_STATUS, value: 'true' });
}
else {
// Need to add a color key to colors.xml to use in styles.xml
const colorItemToAdd = Resources_1.buildResourceItem({ name: COLOR_PRIMARY_DARK_KEY, value: hexString });
colorsJSON = Colors_1.setColorItem(colorItemToAdd, colorsJSON);
styleItemToAdd = Resources_1.buildResourceItem({
name: COLOR_PRIMARY_DARK_KEY,
value: `@color/${COLOR_PRIMARY_DARK_KEY}`,
});
}
// Default is light-content, don't need to do anything to set it
if (statusBarStyle === 'dark-content') {
const statusBarStyleItem = Resources_1.buildResourceItem({
name: WINDOW_LIGHT_STATUS_BAR,
value: `true`,
});
stylesJSON = Styles_1.setStylesItem({
item: statusBarStyleItem,
xml: stylesJSON,
parent: { name: 'AppTheme', parent: 'Theme.AppCompat.Light.NoActionBar' },
});
}
stylesJSON = Styles_1.setStylesItem({
item: styleItemToAdd,
xml: stylesJSON,
parent: { name: 'AppTheme', parent: 'Theme.AppCompat.Light.NoActionBar' },
});
try {
await Promise.all([
XML_1.writeXMLAsync({ path: colorsPath, xml: colorsJSON }),
XML_1.writeXMLAsync({ path: stylesPath, xml: stylesJSON }),
]);
}
catch (e) {
throw new Error(`Error setting Android status bar config. Cannot write colors.xml to ${colorsPath}, or styles.xml to ${stylesPath}.`);
}
return true;
}
exports.setStatusBarConfig = setStatusBarConfig;
//# sourceMappingURL=StatusBar.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,6 @@
import { ResourceItemXML, ResourceKind, ResourceXML } from './Resources';
export declare function getProjectStringsXMLPathAsync(projectRoot: string, { kind }?: {
kind?: ResourceKind;
}): Promise<string>;
export declare function setStringItem(itemToAdd: ResourceItemXML[], stringFileContentsJSON: ResourceXML): ResourceXML;
export declare function removeStringItem(named: string, stringFileContentsJSON: ResourceXML): ResourceXML;

View File

@ -0,0 +1,42 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Paths_1 = require("./Paths");
async function getProjectStringsXMLPathAsync(projectRoot, { kind } = {}) {
return Paths_1.getResourceXMLPathAsync(projectRoot, { kind, name: 'strings' });
}
exports.getProjectStringsXMLPathAsync = getProjectStringsXMLPathAsync;
function setStringItem(itemToAdd, stringFileContentsJSON) {
var _a;
if ((_a = stringFileContentsJSON === null || stringFileContentsJSON === void 0 ? void 0 : stringFileContentsJSON.resources) === null || _a === void 0 ? void 0 : _a.string) {
const stringNameExists = stringFileContentsJSON.resources.string.filter((e) => e.$.name === itemToAdd[0].$.name)[0];
if (stringNameExists) {
// replace the previous value
stringNameExists._ = itemToAdd[0]._;
}
else {
stringFileContentsJSON.resources.string = stringFileContentsJSON.resources.string.concat(itemToAdd);
}
}
else {
if (!stringFileContentsJSON.resources || typeof stringFileContentsJSON.resources === 'string') {
// file was empty and JSON is `{resources : ''}`
stringFileContentsJSON.resources = {};
}
stringFileContentsJSON.resources.string = itemToAdd;
}
return stringFileContentsJSON;
}
exports.setStringItem = setStringItem;
function removeStringItem(named, stringFileContentsJSON) {
var _a;
if ((_a = stringFileContentsJSON === null || stringFileContentsJSON === void 0 ? void 0 : stringFileContentsJSON.resources) === null || _a === void 0 ? void 0 : _a.string) {
const stringNameExists = stringFileContentsJSON.resources.string.findIndex((e) => e.$.name === named);
if (stringNameExists > -1) {
// replace the previous value
stringFileContentsJSON.resources.string.splice(stringNameExists, 1);
}
}
return stringFileContentsJSON;
}
exports.removeStringItem = removeStringItem;
//# sourceMappingURL=Strings.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Strings.js","sourceRoot":"","sources":["../../src/android/Strings.ts"],"names":[],"mappings":";;AAAA,mCAAkD;AAG3C,KAAK,UAAU,6BAA6B,CACjD,WAAmB,EACnB,EAAE,IAAI,KAA8B,EAAE;IAEtC,OAAO,+BAAuB,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;AACzE,CAAC;AALD,sEAKC;AAED,SAAgB,aAAa,CAC3B,SAA4B,EAC5B,sBAAmC;;IAEnC,UAAI,sBAAsB,aAAtB,sBAAsB,uBAAtB,sBAAsB,CAAE,SAAS,0CAAE,MAAM,EAAE;QAC7C,MAAM,gBAAgB,GAAG,sBAAsB,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CACrE,CAAC,CAAkB,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CACzD,CAAC,CAAC,CAAC,CAAC;QACL,IAAI,gBAAgB,EAAE;YACpB,6BAA6B;YAC7B,gBAAgB,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACrC;aAAM;YACL,sBAAsB,CAAC,SAAS,CAAC,MAAM,GAAG,sBAAsB,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CACtF,SAAS,CACV,CAAC;SACH;KACF;SAAM;QACL,IAAI,CAAC,sBAAsB,CAAC,SAAS,IAAI,OAAO,sBAAsB,CAAC,SAAS,KAAK,QAAQ,EAAE;YAC7F,gDAAgD;YAChD,sBAAsB,CAAC,SAAS,GAAG,EAAE,CAAC;SACvC;QACD,sBAAsB,CAAC,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC;KACrD;IACD,OAAO,sBAAsB,CAAC;AAChC,CAAC;AAxBD,sCAwBC;AAED,SAAgB,gBAAgB,CAAC,KAAa,EAAE,sBAAmC;;IACjF,UAAI,sBAAsB,aAAtB,sBAAsB,uBAAtB,sBAAsB,CAAE,SAAS,0CAAE,MAAM,EAAE;QAC7C,MAAM,gBAAgB,GAAG,sBAAsB,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CACxE,CAAC,CAAkB,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAC3C,CAAC;QACF,IAAI,gBAAgB,GAAG,CAAC,CAAC,EAAE;YACzB,6BAA6B;YAC7B,sBAAsB,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC;SACrE;KACF;IACD,OAAO,sBAAsB,CAAC;AAChC,CAAC;AAXD,4CAWC","sourcesContent":["import { getResourceXMLPathAsync } from './Paths';\nimport { ResourceItemXML, ResourceKind, ResourceXML } from './Resources';\n\nexport async function getProjectStringsXMLPathAsync(\n projectRoot: string,\n { kind }: { kind?: ResourceKind } = {}\n): Promise<string> {\n return getResourceXMLPathAsync(projectRoot, { kind, name: 'strings' });\n}\n\nexport function setStringItem(\n itemToAdd: ResourceItemXML[],\n stringFileContentsJSON: ResourceXML\n): ResourceXML {\n if (stringFileContentsJSON?.resources?.string) {\n const stringNameExists = stringFileContentsJSON.resources.string.filter(\n (e: ResourceItemXML) => e.$.name === itemToAdd[0].$.name\n )[0];\n if (stringNameExists) {\n // replace the previous value\n stringNameExists._ = itemToAdd[0]._;\n } else {\n stringFileContentsJSON.resources.string = stringFileContentsJSON.resources.string.concat(\n itemToAdd\n );\n }\n } else {\n if (!stringFileContentsJSON.resources || typeof stringFileContentsJSON.resources === 'string') {\n // file was empty and JSON is `{resources : ''}`\n stringFileContentsJSON.resources = {};\n }\n stringFileContentsJSON.resources.string = itemToAdd;\n }\n return stringFileContentsJSON;\n}\n\nexport function removeStringItem(named: string, stringFileContentsJSON: ResourceXML): ResourceXML {\n if (stringFileContentsJSON?.resources?.string) {\n const stringNameExists = stringFileContentsJSON.resources.string.findIndex(\n (e: ResourceItemXML) => e.$.name === named\n );\n if (stringNameExists > -1) {\n // replace the previous value\n stringFileContentsJSON.resources.string.splice(stringNameExists, 1);\n }\n }\n return stringFileContentsJSON;\n}\n"]}

View File

@ -0,0 +1,32 @@
import { ResourceGroupXML, ResourceItemXML, ResourceKind, ResourceXML } from './Resources';
export declare function getProjectStylesXMLPathAsync(projectRoot: string, { kind }?: {
kind?: ResourceKind;
}): Promise<string>;
export declare function getStyleParent(xml: ResourceXML, parent: {
name: string;
parent?: string;
}): ResourceGroupXML | null;
export declare function getStylesItem({ name, xml, parent, }: {
name: string;
xml: ResourceXML;
parent: {
name: string;
parent: string;
};
}): ResourceItemXML | null;
export declare function setStylesItem({ item, xml, parent, }: {
item: ResourceItemXML;
xml: ResourceXML;
parent: {
name: string;
parent: string;
};
}): ResourceXML;
export declare function removeStylesItem({ name, xml, parent, }: {
name: string;
xml: ResourceXML;
parent: {
name: string;
parent: string;
};
}): ResourceXML;

View File

@ -0,0 +1,80 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Paths_1 = require("./Paths");
const Resources_1 = require("./Resources");
async function getProjectStylesXMLPathAsync(projectRoot, { kind } = {}) {
return Paths_1.getResourceXMLPathAsync(projectRoot, { kind, name: 'styles' });
}
exports.getProjectStylesXMLPathAsync = getProjectStylesXMLPathAsync;
function ensureDefaultStyleResourceXML(xml) {
var _a;
xml = Resources_1.ensureDefaultResourceXML(xml);
if (!Array.isArray((_a = xml === null || xml === void 0 ? void 0 : xml.resources) === null || _a === void 0 ? void 0 : _a.style)) {
xml.resources.style = [];
}
return xml;
}
function getStyleParent(xml, parent) {
var _a, _b, _c, _d;
const app = (_d = (_c = (_b = (_a = xml === null || xml === void 0 ? void 0 : xml.resources) === null || _a === void 0 ? void 0 : _a.style) === null || _b === void 0 ? void 0 : _b.filter) === null || _c === void 0 ? void 0 : _c.call(_b, (e) => {
let matches = e.$.name === parent.name;
if (parent.parent != null && matches) {
matches = e.$.parent === parent.parent;
}
return matches;
})) === null || _d === void 0 ? void 0 : _d[0];
return app !== null && app !== void 0 ? app : null;
}
exports.getStyleParent = getStyleParent;
function getStylesItem({ name, xml, parent, }) {
xml = ensureDefaultStyleResourceXML(xml);
const appTheme = getStyleParent(xml, parent);
if (!appTheme) {
return null;
}
if (appTheme.item) {
const existingItem = appTheme.item.filter(_item => _item.$.name === name)[0];
// Don't want to 2 of the same item, so if one exists, we overwrite it
if (existingItem) {
return existingItem;
}
}
return null;
}
exports.getStylesItem = getStylesItem;
function setStylesItem({ item, xml, parent, }) {
xml = ensureDefaultStyleResourceXML(xml);
let appTheme = getStyleParent(xml, parent);
if (!appTheme) {
appTheme = Resources_1.buildResourceGroup(parent);
xml.resources.style.push(appTheme);
}
if (appTheme.item) {
const existingItem = appTheme.item.filter(_item => _item.$.name === item.$.name)[0];
// Don't want to 2 of the same item, so if one exists, we overwrite it
if (existingItem) {
existingItem._ = item._;
}
else {
appTheme.item.push(item);
}
}
else {
appTheme.item = [item];
}
return xml;
}
exports.setStylesItem = setStylesItem;
function removeStylesItem({ name, xml, parent, }) {
xml = ensureDefaultStyleResourceXML(xml);
const appTheme = getStyleParent(xml, parent);
if (appTheme === null || appTheme === void 0 ? void 0 : appTheme.item) {
const index = appTheme.item.findIndex((e) => e.$.name === name);
if (index > -1) {
appTheme.item.splice(index, 1);
}
}
return xml;
}
exports.removeStylesItem = removeStylesItem;
//# sourceMappingURL=Styles.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,31 @@
import { ExpoConfig } from '@expo/config-types';
import { ConfigPlugin } from '../Plugin.types';
import { AndroidManifest } from './Manifest';
declare type ExpoConfigUpdates = Pick<ExpoConfig, 'sdkVersion' | 'owner' | 'runtimeVersion' | 'updates' | 'slug'>;
export declare enum Config {
ENABLED = "expo.modules.updates.ENABLED",
CHECK_ON_LAUNCH = "expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH",
LAUNCH_WAIT_MS = "expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS",
SDK_VERSION = "expo.modules.updates.EXPO_SDK_VERSION",
RUNTIME_VERSION = "expo.modules.updates.EXPO_RUNTIME_VERSION",
UPDATE_URL = "expo.modules.updates.EXPO_UPDATE_URL",
RELEASE_CHANNEL = "expo.modules.updates.EXPO_RELEASE_CHANNEL"
}
export declare const withUpdates: ConfigPlugin<{
expoUsername: string | null;
}>;
export declare function getUpdateUrl(config: Pick<ExpoConfigUpdates, 'owner' | 'slug'>, username: string | null): string | null;
export declare function getRuntimeVersion(config: Pick<ExpoConfigUpdates, 'runtimeVersion'>): string | null;
export declare function getSDKVersion(config: Pick<ExpoConfigUpdates, 'sdkVersion'>): string | null;
export declare function getUpdatesEnabled(config: Pick<ExpoConfigUpdates, 'updates'>): boolean;
export declare function getUpdatesTimeout(config: Pick<ExpoConfigUpdates, 'updates'>): number;
export declare function getUpdatesCheckOnLaunch(config: Pick<ExpoConfigUpdates, 'updates'>): 'NEVER' | 'ALWAYS';
export declare function setUpdatesConfig(config: ExpoConfigUpdates, androidManifest: AndroidManifest, username: string | null): AndroidManifest;
export declare function setVersionsConfig(config: Pick<ExpoConfigUpdates, 'sdkVersion' | 'runtimeVersion'>, androidManifest: AndroidManifest): AndroidManifest;
export declare function ensureBuildGradleContainsConfigurationScript(projectRoot: string, buildGradleContents: string): string;
export declare function formatApplyLineForBuildGradle(projectRoot: string): string;
export declare function isBuildGradleConfigured(projectRoot: string, buildGradleContents: string): boolean;
export declare function isMainApplicationMetaDataSet(androidManifest: AndroidManifest): boolean;
export declare function isMainApplicationMetaDataSynced(config: ExpoConfigUpdates, androidManifest: AndroidManifest, username: string | null): boolean;
export declare function areVersionsSynced(config: Pick<ExpoConfigUpdates, 'runtimeVersion' | 'sdkVersion'>, androidManifest: AndroidManifest): boolean;
export {};

View File

@ -0,0 +1,164 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = __importDefault(require("path"));
const resolve_from_1 = __importDefault(require("resolve-from"));
const android_plugins_1 = require("../plugins/android-plugins");
const Manifest_1 = require("./Manifest");
const CREATE_MANIFEST_ANDROID_PATH = 'expo-updates/scripts/create-manifest-android.gradle';
var Config;
(function (Config) {
Config["ENABLED"] = "expo.modules.updates.ENABLED";
Config["CHECK_ON_LAUNCH"] = "expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH";
Config["LAUNCH_WAIT_MS"] = "expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS";
Config["SDK_VERSION"] = "expo.modules.updates.EXPO_SDK_VERSION";
Config["RUNTIME_VERSION"] = "expo.modules.updates.EXPO_RUNTIME_VERSION";
Config["UPDATE_URL"] = "expo.modules.updates.EXPO_UPDATE_URL";
Config["RELEASE_CHANNEL"] = "expo.modules.updates.EXPO_RELEASE_CHANNEL";
})(Config = exports.Config || (exports.Config = {}));
exports.withUpdates = (config, { expoUsername }) => {
return android_plugins_1.withAndroidManifest(config, config => {
config.modResults = setUpdatesConfig(config, config.modResults, expoUsername);
return config;
});
};
function getUpdateUrl(config, username) {
const user = typeof config.owner === 'string' ? config.owner : username;
if (!user) {
return null;
}
return `https://exp.host/@${user}/${config.slug}`;
}
exports.getUpdateUrl = getUpdateUrl;
function getRuntimeVersion(config) {
return typeof config.runtimeVersion === 'string' ? config.runtimeVersion : null;
}
exports.getRuntimeVersion = getRuntimeVersion;
function getSDKVersion(config) {
return typeof config.sdkVersion === 'string' ? config.sdkVersion : null;
}
exports.getSDKVersion = getSDKVersion;
function getUpdatesEnabled(config) {
var _a;
return ((_a = config.updates) === null || _a === void 0 ? void 0 : _a.enabled) !== false;
}
exports.getUpdatesEnabled = getUpdatesEnabled;
function getUpdatesTimeout(config) {
var _a, _b;
return (_b = (_a = config.updates) === null || _a === void 0 ? void 0 : _a.fallbackToCacheTimeout) !== null && _b !== void 0 ? _b : 0;
}
exports.getUpdatesTimeout = getUpdatesTimeout;
function getUpdatesCheckOnLaunch(config) {
var _a, _b;
if (((_a = config.updates) === null || _a === void 0 ? void 0 : _a.checkAutomatically) === 'ON_ERROR_RECOVERY') {
return 'NEVER';
}
else if (((_b = config.updates) === null || _b === void 0 ? void 0 : _b.checkAutomatically) === 'ON_LOAD') {
return 'ALWAYS';
}
return 'ALWAYS';
}
exports.getUpdatesCheckOnLaunch = getUpdatesCheckOnLaunch;
function setUpdatesConfig(config, androidManifest, username) {
const mainApplication = Manifest_1.getMainApplicationOrThrow(androidManifest);
Manifest_1.addMetaDataItemToMainApplication(mainApplication, Config.ENABLED, String(getUpdatesEnabled(config)));
Manifest_1.addMetaDataItemToMainApplication(mainApplication, Config.CHECK_ON_LAUNCH, getUpdatesCheckOnLaunch(config));
Manifest_1.addMetaDataItemToMainApplication(mainApplication, Config.LAUNCH_WAIT_MS, String(getUpdatesTimeout(config)));
const updateUrl = getUpdateUrl(config, username);
if (updateUrl) {
Manifest_1.addMetaDataItemToMainApplication(mainApplication, Config.UPDATE_URL, updateUrl);
}
else {
Manifest_1.removeMetaDataItemFromMainApplication(mainApplication, Config.UPDATE_URL);
}
return setVersionsConfig(config, androidManifest);
}
exports.setUpdatesConfig = setUpdatesConfig;
function setVersionsConfig(config, androidManifest) {
const mainApplication = Manifest_1.getMainApplicationOrThrow(androidManifest);
const runtimeVersion = getRuntimeVersion(config);
const sdkVersion = getSDKVersion(config);
if (runtimeVersion) {
Manifest_1.removeMetaDataItemFromMainApplication(mainApplication, Config.SDK_VERSION);
Manifest_1.addMetaDataItemToMainApplication(mainApplication, Config.RUNTIME_VERSION, runtimeVersion);
}
else if (sdkVersion) {
Manifest_1.removeMetaDataItemFromMainApplication(mainApplication, Config.RUNTIME_VERSION);
Manifest_1.addMetaDataItemToMainApplication(mainApplication, Config.SDK_VERSION, sdkVersion);
}
else {
Manifest_1.removeMetaDataItemFromMainApplication(mainApplication, Config.RUNTIME_VERSION);
Manifest_1.removeMetaDataItemFromMainApplication(mainApplication, Config.SDK_VERSION);
}
return androidManifest;
}
exports.setVersionsConfig = setVersionsConfig;
function ensureBuildGradleContainsConfigurationScript(projectRoot, buildGradleContents) {
if (!isBuildGradleConfigured(projectRoot, buildGradleContents)) {
let cleanedUpBuildGradleContents;
const isBuildGradleMisconfigured = buildGradleContents
.split('\n')
.some(line => line.includes(CREATE_MANIFEST_ANDROID_PATH));
if (isBuildGradleMisconfigured) {
cleanedUpBuildGradleContents = buildGradleContents.replace(new RegExp(`(\n// Integration with Expo updates)?\n.*${CREATE_MANIFEST_ANDROID_PATH}.*\n`), '');
}
else {
cleanedUpBuildGradleContents = buildGradleContents;
}
const gradleScriptApply = formatApplyLineForBuildGradle(projectRoot);
return `${cleanedUpBuildGradleContents}\n// Integration with Expo updates\n${gradleScriptApply}\n`;
}
else {
return buildGradleContents;
}
}
exports.ensureBuildGradleContainsConfigurationScript = ensureBuildGradleContainsConfigurationScript;
function formatApplyLineForBuildGradle(projectRoot) {
const updatesGradleScriptPath = resolve_from_1.default.silent(projectRoot, CREATE_MANIFEST_ANDROID_PATH);
if (!updatesGradleScriptPath) {
throw new Error("Could not find the build script for Android. This could happen in case of outdated 'node_modules'. Run 'npm install' to make sure that it's up-to-date.");
}
const relativePath = path_1.default.relative(path_1.default.join(projectRoot, 'android', 'app'), updatesGradleScriptPath);
const posixPath = process.platform === 'win32' ? relativePath.replace(/\\/g, '/') : relativePath;
return `apply from: "${posixPath}"`;
}
exports.formatApplyLineForBuildGradle = formatApplyLineForBuildGradle;
function isBuildGradleConfigured(projectRoot, buildGradleContents) {
const androidBuildScript = formatApplyLineForBuildGradle(projectRoot);
return (buildGradleContents
.replace(/\r\n/g, '\n')
.split('\n')
// Check for both single and double quotes
.some(line => line === androidBuildScript || line === androidBuildScript.replace(/"/g, "'")));
}
exports.isBuildGradleConfigured = isBuildGradleConfigured;
function isMainApplicationMetaDataSet(androidManifest) {
const updateUrl = Manifest_1.getMainApplicationMetaDataValue(androidManifest, Config.UPDATE_URL);
const runtimeVersion = Manifest_1.getMainApplicationMetaDataValue(androidManifest, Config.RUNTIME_VERSION);
const sdkVersion = Manifest_1.getMainApplicationMetaDataValue(androidManifest, Config.SDK_VERSION);
return Boolean(updateUrl && (sdkVersion || runtimeVersion));
}
exports.isMainApplicationMetaDataSet = isMainApplicationMetaDataSet;
function isMainApplicationMetaDataSynced(config, androidManifest, username) {
return (getUpdateUrl(config, username) ===
Manifest_1.getMainApplicationMetaDataValue(androidManifest, Config.UPDATE_URL) &&
String(getUpdatesEnabled(config)) ===
Manifest_1.getMainApplicationMetaDataValue(androidManifest, Config.ENABLED) &&
String(getUpdatesTimeout(config)) ===
Manifest_1.getMainApplicationMetaDataValue(androidManifest, Config.LAUNCH_WAIT_MS) &&
getUpdatesCheckOnLaunch(config) ===
Manifest_1.getMainApplicationMetaDataValue(androidManifest, Config.CHECK_ON_LAUNCH) &&
areVersionsSynced(config, androidManifest));
}
exports.isMainApplicationMetaDataSynced = isMainApplicationMetaDataSynced;
function areVersionsSynced(config, androidManifest) {
const expectedRuntimeVersion = getRuntimeVersion(config);
const expectedSdkVersion = getSDKVersion(config);
const currentRuntimeVersion = Manifest_1.getMainApplicationMetaDataValue(androidManifest, Config.RUNTIME_VERSION);
const currentSdkVersion = Manifest_1.getMainApplicationMetaDataValue(androidManifest, Config.SDK_VERSION);
return (currentRuntimeVersion === expectedRuntimeVersion && currentSdkVersion === expectedSdkVersion);
}
exports.areVersionsSynced = areVersionsSynced;
//# sourceMappingURL=Updates.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,10 @@
import { ExpoConfig } from '@expo/config-types';
import { ConfigPlugin } from '../Plugin.types';
import { AndroidManifest } from './Manifest';
export declare const CONFIG_CHANGES_ATTRIBUTE = "android:configChanges";
export declare const ON_CONFIGURATION_CHANGED = "\npublic class MainActivity extends ReactActivity {\n\n // Added automatically by Expo Config\n @Override\n public void onConfigurationChanged(Configuration newConfig) {\n super.onConfigurationChanged(newConfig);\n Intent intent = new Intent(\"onConfigurationChanged\");\n intent.putExtra(\"newConfig\", newConfig);\n sendBroadcast(intent);\n }\n";
export declare const withUiModeManifest: ConfigPlugin<void>;
export declare const withUiModeMainActivity: ConfigPlugin;
export declare function getUserInterfaceStyle(config: Pick<ExpoConfig, 'android' | 'userInterfaceStyle'>): string | null;
export declare function setUiModeAndroidManifest(config: Pick<ExpoConfig, 'android' | 'userInterfaceStyle'>, androidManifest: AndroidManifest): AndroidManifest;
export declare function addOnConfigurationChangedMainActivity(config: Pick<ExpoConfig, 'android' | 'userInterfaceStyle'>, mainActivity: string): string;

View File

@ -0,0 +1,85 @@
"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const android_plugins_1 = require("../plugins/android-plugins");
const WarningAggregator = __importStar(require("../utils/warnings"));
const Manifest_1 = require("./Manifest");
exports.CONFIG_CHANGES_ATTRIBUTE = 'android:configChanges';
exports.ON_CONFIGURATION_CHANGED = `
public class MainActivity extends ReactActivity {
// Added automatically by Expo Config
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Intent intent = new Intent("onConfigurationChanged");
intent.putExtra("newConfig", newConfig);
sendBroadcast(intent);
}
`;
exports.withUiModeManifest = android_plugins_1.createAndroidManifestPlugin(setUiModeAndroidManifest, 'withUiModeManifest');
exports.withUiModeMainActivity = config => {
return android_plugins_1.withMainActivity(config, config => {
if (config.modResults.language === 'java') {
config.modResults.contents = addOnConfigurationChangedMainActivity(config, config.modResults.contents);
}
else {
WarningAggregator.addWarningAndroid('android-userInterfaceStyle', `Cannot automatically configure MainActivity if it's not java`);
}
return config;
});
};
function getUserInterfaceStyle(config) {
var _a, _b, _c;
return (_c = (_b = (_a = config.android) === null || _a === void 0 ? void 0 : _a.userInterfaceStyle) !== null && _b !== void 0 ? _b : config.userInterfaceStyle) !== null && _c !== void 0 ? _c : null;
}
exports.getUserInterfaceStyle = getUserInterfaceStyle;
function setUiModeAndroidManifest(config, androidManifest) {
const userInterfaceStyle = getUserInterfaceStyle(config);
// TODO: Remove this if we decide to remove any uiMode configuration when not specified
if (!userInterfaceStyle) {
return androidManifest;
}
const mainActivity = Manifest_1.getMainActivityOrThrow(androidManifest);
mainActivity.$[exports.CONFIG_CHANGES_ATTRIBUTE] =
'keyboard|keyboardHidden|orientation|screenSize|uiMode';
return androidManifest;
}
exports.setUiModeAndroidManifest = setUiModeAndroidManifest;
function addOnConfigurationChangedMainActivity(config, mainActivity) {
var _a;
const userInterfaceStyle = getUserInterfaceStyle(config);
if (!userInterfaceStyle) {
return mainActivity;
}
// Cruzan: this is not ideal, but I'm not sure of a better way to handle writing to MainActivity.java
if ((_a = mainActivity.match(`onConfigurationChanged`)) === null || _a === void 0 ? void 0 : _a.length) {
return mainActivity;
}
const MainActivityWithImports = addJavaImports(mainActivity, [
'android.content.Intent',
'android.content.res.Configuration',
]);
const pattern = new RegExp(`public class MainActivity extends ReactActivity {`);
return MainActivityWithImports.replace(pattern, exports.ON_CONFIGURATION_CHANGED);
}
exports.addOnConfigurationChangedMainActivity = addOnConfigurationChangedMainActivity;
// TODO: we should have a generic utility for doing this
function addJavaImports(javaSource, javaImports) {
const lines = javaSource.split('\n');
const lineIndexWithPackageDeclaration = lines.findIndex(line => line.match(/^package .*;$/));
for (const javaImport of javaImports) {
if (!javaSource.includes(javaImport)) {
const importStatement = `import ${javaImport};`;
lines.splice(lineIndexWithPackageDeclaration + 1, 0, importStatement);
}
}
return lines.join('\n');
}
//# sourceMappingURL=UserInterfaceStyle.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,7 @@
import { ExpoConfig } from '@expo/config-types';
import { ConfigPlugin } from '../Plugin.types';
export declare const withVersion: ConfigPlugin;
export declare function getVersionName(config: Pick<ExpoConfig, 'version'>): string | null;
export declare function setVersionName(config: Pick<ExpoConfig, 'version'>, buildGradle: string): string;
export declare function getVersionCode(config: Pick<ExpoConfig, 'android'>): number | null;
export declare function setVersionCode(config: Pick<ExpoConfig, 'android'>, buildGradle: string): string;

View File

@ -0,0 +1,52 @@
"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const android_plugins_1 = require("../plugins/android-plugins");
const WarningAggregator = __importStar(require("../utils/warnings"));
exports.withVersion = config => {
return android_plugins_1.withAppBuildGradle(config, config => {
if (config.modResults.language === 'groovy') {
config.modResults.contents = setVersionCode(config, config.modResults.contents);
config.modResults.contents = setVersionName(config, config.modResults.contents);
}
else {
WarningAggregator.addWarningAndroid('android-version', `Cannot automatically configure app build.gradle if it's not groovy`);
}
return config;
});
};
function getVersionName(config) {
var _a;
return (_a = config.version) !== null && _a !== void 0 ? _a : null;
}
exports.getVersionName = getVersionName;
function setVersionName(config, buildGradle) {
const versionName = getVersionName(config);
if (versionName === null) {
return buildGradle;
}
const pattern = new RegExp(`versionName ".*"`);
return buildGradle.replace(pattern, `versionName "${versionName}"`);
}
exports.setVersionName = setVersionName;
function getVersionCode(config) {
var _a, _b;
return (_b = (_a = config.android) === null || _a === void 0 ? void 0 : _a.versionCode) !== null && _b !== void 0 ? _b : null;
}
exports.getVersionCode = getVersionCode;
function setVersionCode(config, buildGradle) {
const versionCode = getVersionCode(config);
if (versionCode === null) {
return buildGradle;
}
const pattern = new RegExp(`versionCode.*`);
return buildGradle.replace(pattern, `versionCode ${versionCode}`);
}
exports.setVersionCode = setVersionCode;
//# sourceMappingURL=Version.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Version.js","sourceRoot":"","sources":["../../src/android/Version.ts"],"names":[],"mappings":";;;;;;;;;AAGA,gEAAgE;AAChE,qEAAuD;AAE1C,QAAA,WAAW,GAAiB,MAAM,CAAC,EAAE;IAChD,OAAO,oCAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE;QACzC,IAAI,MAAM,CAAC,UAAU,CAAC,QAAQ,KAAK,QAAQ,EAAE;YAC3C,MAAM,CAAC,UAAU,CAAC,QAAQ,GAAG,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAChF,MAAM,CAAC,UAAU,CAAC,QAAQ,GAAG,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;SACjF;aAAM;YACL,iBAAiB,CAAC,iBAAiB,CACjC,iBAAiB,EACjB,oEAAoE,CACrE,CAAC;SACH;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,SAAgB,cAAc,CAAC,MAAmC;;IAChE,aAAO,MAAM,CAAC,OAAO,mCAAI,IAAI,CAAC;AAChC,CAAC;AAFD,wCAEC;AAED,SAAgB,cAAc,CAAC,MAAmC,EAAE,WAAmB;IACrF,MAAM,WAAW,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,WAAW,KAAK,IAAI,EAAE;QACxB,OAAO,WAAW,CAAC;KACpB;IAED,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,kBAAkB,CAAC,CAAC;IAC/C,OAAO,WAAW,CAAC,OAAO,CAAC,OAAO,EAAE,gBAAgB,WAAW,GAAG,CAAC,CAAC;AACtE,CAAC;AARD,wCAQC;AAED,SAAgB,cAAc,CAAC,MAAmC;;IAChE,mBAAO,MAAM,CAAC,OAAO,0CAAE,WAAW,mCAAI,IAAI,CAAC;AAC7C,CAAC;AAFD,wCAEC;AAED,SAAgB,cAAc,CAAC,MAAmC,EAAE,WAAmB;IACrF,MAAM,WAAW,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,WAAW,KAAK,IAAI,EAAE;QACxB,OAAO,WAAW,CAAC;KACpB;IAED,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,eAAe,CAAC,CAAC;IAC5C,OAAO,WAAW,CAAC,OAAO,CAAC,OAAO,EAAE,eAAe,WAAW,EAAE,CAAC,CAAC;AACpE,CAAC;AARD,wCAQC","sourcesContent":["import { ExpoConfig } from '@expo/config-types';\n\nimport { ConfigPlugin } from '../Plugin.types';\nimport { withAppBuildGradle } from '../plugins/android-plugins';\nimport * as WarningAggregator from '../utils/warnings';\n\nexport const withVersion: ConfigPlugin = config => {\n return withAppBuildGradle(config, config => {\n if (config.modResults.language === 'groovy') {\n config.modResults.contents = setVersionCode(config, config.modResults.contents);\n config.modResults.contents = setVersionName(config, config.modResults.contents);\n } else {\n WarningAggregator.addWarningAndroid(\n 'android-version',\n `Cannot automatically configure app build.gradle if it's not groovy`\n );\n }\n return config;\n });\n};\n\nexport function getVersionName(config: Pick<ExpoConfig, 'version'>) {\n return config.version ?? null;\n}\n\nexport function setVersionName(config: Pick<ExpoConfig, 'version'>, buildGradle: string) {\n const versionName = getVersionName(config);\n if (versionName === null) {\n return buildGradle;\n }\n\n const pattern = new RegExp(`versionName \".*\"`);\n return buildGradle.replace(pattern, `versionName \"${versionName}\"`);\n}\n\nexport function getVersionCode(config: Pick<ExpoConfig, 'android'>) {\n return config.android?.versionCode ?? null;\n}\n\nexport function setVersionCode(config: Pick<ExpoConfig, 'android'>, buildGradle: string) {\n const versionCode = getVersionCode(config);\n if (versionCode === null) {\n return buildGradle;\n }\n\n const pattern = new RegExp(`versionCode.*`);\n return buildGradle.replace(pattern, `versionCode ${versionCode}`);\n}\n"]}

View File

@ -0,0 +1,29 @@
import * as AdMob from './AdMob';
import * as AllowBackup from './AllowBackup';
import * as Branch from './Branch';
import * as Colors from './Colors';
import * as EasBuild from './EasBuild';
import * as Facebook from './Facebook';
import * as GoogleMapsApiKey from './GoogleMapsApiKey';
import * as GoogleServices from './GoogleServices';
import * as Icon from './Icon';
import * as IntentFilters from './IntentFilters';
import * as Manifest from './Manifest';
import * as Name from './Name';
import * as NavigationBar from './NavigationBar';
import * as Notifications from './Notifications';
import * as Orientation from './Orientation';
import * as Package from './Package';
import * as Paths from './Paths';
import * as Permissions from './Permissions';
import * as PrimaryColor from './PrimaryColor';
import * as Properties from './Properties';
import * as RootViewBackgroundColor from './RootViewBackgroundColor';
import * as Scheme from './Scheme';
import * as SplashScreen from './SplashScreen';
import * as StatusBar from './StatusBar';
import * as Styles from './Styles';
import * as Updates from './Updates';
import * as UserInterfaceStyle from './UserInterfaceStyle';
import * as Version from './Version';
export { AllowBackup, EasBuild, Manifest, Branch, Colors, Facebook, GoogleMapsApiKey, AdMob, GoogleServices, Icon, IntentFilters, Name, NavigationBar, Notifications, Orientation, Package, Paths, Permissions, PrimaryColor, Properties, RootViewBackgroundColor, Scheme, SplashScreen, StatusBar, Styles, Updates, UserInterfaceStyle, Version, };

View File

@ -0,0 +1,66 @@
"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const AdMob = __importStar(require("./AdMob"));
exports.AdMob = AdMob;
const AllowBackup = __importStar(require("./AllowBackup"));
exports.AllowBackup = AllowBackup;
const Branch = __importStar(require("./Branch"));
exports.Branch = Branch;
const Colors = __importStar(require("./Colors"));
exports.Colors = Colors;
const EasBuild = __importStar(require("./EasBuild"));
exports.EasBuild = EasBuild;
const Facebook = __importStar(require("./Facebook"));
exports.Facebook = Facebook;
const GoogleMapsApiKey = __importStar(require("./GoogleMapsApiKey"));
exports.GoogleMapsApiKey = GoogleMapsApiKey;
const GoogleServices = __importStar(require("./GoogleServices"));
exports.GoogleServices = GoogleServices;
const Icon = __importStar(require("./Icon"));
exports.Icon = Icon;
const IntentFilters = __importStar(require("./IntentFilters"));
exports.IntentFilters = IntentFilters;
const Manifest = __importStar(require("./Manifest"));
exports.Manifest = Manifest;
const Name = __importStar(require("./Name"));
exports.Name = Name;
const NavigationBar = __importStar(require("./NavigationBar"));
exports.NavigationBar = NavigationBar;
const Notifications = __importStar(require("./Notifications"));
exports.Notifications = Notifications;
const Orientation = __importStar(require("./Orientation"));
exports.Orientation = Orientation;
const Package = __importStar(require("./Package"));
exports.Package = Package;
const Paths = __importStar(require("./Paths"));
exports.Paths = Paths;
const Permissions = __importStar(require("./Permissions"));
exports.Permissions = Permissions;
const PrimaryColor = __importStar(require("./PrimaryColor"));
exports.PrimaryColor = PrimaryColor;
const Properties = __importStar(require("./Properties"));
exports.Properties = Properties;
const RootViewBackgroundColor = __importStar(require("./RootViewBackgroundColor"));
exports.RootViewBackgroundColor = RootViewBackgroundColor;
const Scheme = __importStar(require("./Scheme"));
exports.Scheme = Scheme;
const SplashScreen = __importStar(require("./SplashScreen"));
exports.SplashScreen = SplashScreen;
const StatusBar = __importStar(require("./StatusBar"));
exports.StatusBar = StatusBar;
const Styles = __importStar(require("./Styles"));
exports.Styles = Styles;
const Updates = __importStar(require("./Updates"));
exports.Updates = Updates;
const UserInterfaceStyle = __importStar(require("./UserInterfaceStyle"));
exports.UserInterfaceStyle = UserInterfaceStyle;
const Version = __importStar(require("./Version"));
exports.Version = Version;
//# sourceMappingURL=index.js.map

Some files were not shown because too many files have changed in this diff Show More