1*082815dcSEvan Baconimport { ExpoConfig } from '@expo/config-types';
2*082815dcSEvan Baconimport path from 'path';
3*082815dcSEvan Bacon
4*082815dcSEvan Baconimport { ConfigPlugin } from '../Plugin.types';
5*082815dcSEvan Baconimport { withAppBuildGradle, withProjectBuildGradle } from '../plugins/android-plugins';
6*082815dcSEvan Baconimport { withDangerousMod } from '../plugins/withDangerousMod';
7*082815dcSEvan Baconimport { copyFilePathToPathAsync } from '../utils/fs';
8*082815dcSEvan Baconimport { addWarningAndroid } from '../utils/warnings';
9*082815dcSEvan Bacon
10*082815dcSEvan Baconconst DEFAULT_TARGET_PATH = './android/app/google-services.json';
11*082815dcSEvan Bacon
12*082815dcSEvan Baconconst googleServicesClassPath = 'com.google.gms:google-services';
13*082815dcSEvan Baconconst googleServicesPlugin = 'com.google.gms.google-services';
14*082815dcSEvan Bacon
15*082815dcSEvan Bacon// NOTE(brentvatne): This may be annoying to keep up to date...
16*082815dcSEvan Baconconst googleServicesVersion = '4.3.3';
17*082815dcSEvan Bacon
18*082815dcSEvan Baconexport const withClassPath: ConfigPlugin = (config) => {
19*082815dcSEvan Bacon  return withProjectBuildGradle(config, (config) => {
20*082815dcSEvan Bacon    if (config.modResults.language === 'groovy') {
21*082815dcSEvan Bacon      config.modResults.contents = setClassPath(config, config.modResults.contents);
22*082815dcSEvan Bacon    } else {
23*082815dcSEvan Bacon      addWarningAndroid(
24*082815dcSEvan Bacon        'android.googleServicesFile',
25*082815dcSEvan Bacon        `Cannot automatically configure project build.gradle if it's not groovy`
26*082815dcSEvan Bacon      );
27*082815dcSEvan Bacon    }
28*082815dcSEvan Bacon    return config;
29*082815dcSEvan Bacon  });
30*082815dcSEvan Bacon};
31*082815dcSEvan Bacon
32*082815dcSEvan Baconexport const withApplyPlugin: ConfigPlugin = (config) => {
33*082815dcSEvan Bacon  return withAppBuildGradle(config, (config) => {
34*082815dcSEvan Bacon    if (config.modResults.language === 'groovy') {
35*082815dcSEvan Bacon      config.modResults.contents = applyPlugin(config, config.modResults.contents);
36*082815dcSEvan Bacon    } else {
37*082815dcSEvan Bacon      addWarningAndroid(
38*082815dcSEvan Bacon        'android.googleServicesFile',
39*082815dcSEvan Bacon        `Cannot automatically configure app build.gradle if it's not groovy`
40*082815dcSEvan Bacon      );
41*082815dcSEvan Bacon    }
42*082815dcSEvan Bacon    return config;
43*082815dcSEvan Bacon  });
44*082815dcSEvan Bacon};
45*082815dcSEvan Bacon
46*082815dcSEvan Bacon/**
47*082815dcSEvan Bacon * Add `google-services.json` to project
48*082815dcSEvan Bacon */
49*082815dcSEvan Baconexport const withGoogleServicesFile: ConfigPlugin = (config) => {
50*082815dcSEvan Bacon  return withDangerousMod(config, [
51*082815dcSEvan Bacon    'android',
52*082815dcSEvan Bacon    async (config) => {
53*082815dcSEvan Bacon      await setGoogleServicesFile(config, config.modRequest.projectRoot);
54*082815dcSEvan Bacon      return config;
55*082815dcSEvan Bacon    },
56*082815dcSEvan Bacon  ]);
57*082815dcSEvan Bacon};
58*082815dcSEvan Bacon
59*082815dcSEvan Baconexport function getGoogleServicesFilePath(config: Pick<ExpoConfig, 'android'>) {
60*082815dcSEvan Bacon  return config.android?.googleServicesFile ?? null;
61*082815dcSEvan Bacon}
62*082815dcSEvan Bacon
63*082815dcSEvan Baconexport async function setGoogleServicesFile(
64*082815dcSEvan Bacon  config: Pick<ExpoConfig, 'android'>,
65*082815dcSEvan Bacon  projectRoot: string,
66*082815dcSEvan Bacon  targetPath: string = DEFAULT_TARGET_PATH
67*082815dcSEvan Bacon) {
68*082815dcSEvan Bacon  const partialSourcePath = getGoogleServicesFilePath(config);
69*082815dcSEvan Bacon  if (!partialSourcePath) {
70*082815dcSEvan Bacon    return false;
71*082815dcSEvan Bacon  }
72*082815dcSEvan Bacon
73*082815dcSEvan Bacon  const completeSourcePath = path.resolve(projectRoot, partialSourcePath);
74*082815dcSEvan Bacon  const destinationPath = path.resolve(projectRoot, targetPath);
75*082815dcSEvan Bacon
76*082815dcSEvan Bacon  try {
77*082815dcSEvan Bacon    await copyFilePathToPathAsync(completeSourcePath, destinationPath);
78*082815dcSEvan Bacon  } catch (e) {
79*082815dcSEvan Bacon    console.log(e);
80*082815dcSEvan Bacon    throw new Error(
81*082815dcSEvan Bacon      `Cannot copy google-services.json from ${completeSourcePath} to ${destinationPath}. Please make sure the source and destination paths exist.`
82*082815dcSEvan Bacon    );
83*082815dcSEvan Bacon  }
84*082815dcSEvan Bacon  return true;
85*082815dcSEvan Bacon}
86*082815dcSEvan Bacon
87*082815dcSEvan Bacon/**
88*082815dcSEvan Bacon * Adding the Google Services plugin
89*082815dcSEvan Bacon * NOTE(brentvatne): string replacement is a fragile approach! we need a
90*082815dcSEvan Bacon * better solution than this.
91*082815dcSEvan Bacon */
92*082815dcSEvan Baconexport function setClassPath(config: Pick<ExpoConfig, 'android'>, buildGradle: string) {
93*082815dcSEvan Bacon  const googleServicesFile = getGoogleServicesFilePath(config);
94*082815dcSEvan Bacon  if (!googleServicesFile) {
95*082815dcSEvan Bacon    return buildGradle;
96*082815dcSEvan Bacon  }
97*082815dcSEvan Bacon
98*082815dcSEvan Bacon  if (buildGradle.includes(googleServicesClassPath)) {
99*082815dcSEvan Bacon    return buildGradle;
100*082815dcSEvan Bacon  }
101*082815dcSEvan Bacon
102*082815dcSEvan Bacon  //
103*082815dcSEvan Bacon  return buildGradle.replace(
104*082815dcSEvan Bacon    /dependencies\s?{/,
105*082815dcSEvan Bacon    `dependencies {
106*082815dcSEvan Bacon        classpath '${googleServicesClassPath}:${googleServicesVersion}'`
107*082815dcSEvan Bacon  );
108*082815dcSEvan Bacon}
109*082815dcSEvan Bacon
110*082815dcSEvan Baconexport function applyPlugin(config: Pick<ExpoConfig, 'android'>, appBuildGradle: string) {
111*082815dcSEvan Bacon  const googleServicesFile = getGoogleServicesFilePath(config);
112*082815dcSEvan Bacon  if (!googleServicesFile) {
113*082815dcSEvan Bacon    return appBuildGradle;
114*082815dcSEvan Bacon  }
115*082815dcSEvan Bacon
116*082815dcSEvan Bacon  // Make sure the project does not have the plugin already
117*082815dcSEvan Bacon  const pattern = new RegExp(`apply\\s+plugin:\\s+['"]${googleServicesPlugin}['"]`);
118*082815dcSEvan Bacon  if (appBuildGradle.match(pattern)) {
119*082815dcSEvan Bacon    return appBuildGradle;
120*082815dcSEvan Bacon  }
121*082815dcSEvan Bacon
122*082815dcSEvan Bacon  // Add it to the end of the file
123*082815dcSEvan Bacon  return appBuildGradle + `\napply plugin: '${googleServicesPlugin}'`;
124*082815dcSEvan Bacon}
125