View Javadoc

1   /*
2    * Copyright 2011 SOFTEC sa. All rights reserved.
3    *
4    * This source code is licensed under the Creative Commons
5    * Attribution-NonCommercial-NoDerivs 3.0 Luxembourg
6    * License.
7    *
8    * To view a copy of this license, visit
9    * http://creativecommons.org/licenses/by-nc-nd/3.0/lu/
10   * or send a letter to Creative Commons, 171 Second Street,
11   * Suite 300, San Francisco, California, 94105, USA.
12   */
13  
14  package org.codehaus.mojo.javascript.titanium;
15  
16  import org.apache.maven.plugin.MojoExecutionException;
17  import org.apache.maven.plugin.logging.Log;
18  import org.codehaus.mojo.javascript.TitaniumPackageMojo;
19  import org.codehaus.mojo.javascript.TitaniumSettings;
20  import org.codehaus.mojo.javascript.VirtualDevice;
21  
22  import java.io.*;
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.List;
26  import java.util.regex.Matcher;
27  import java.util.regex.Pattern;
28  
29  /**
30   * Titanium utility class.
31   * Contains helper methods related to Titanium.
32   */
33  public class TitaniumUtils {
34      /**
35       * The android home environment variable name.
36       */
37      public static final String ENV_ANDROID_HOME = "ANDROID_HOME";
38  
39      /**
40       * Retrieve the Android SDK home folder based on the ANDROID_HOME environment variable.
41       * @return The android SDK home folder.
42       * @throws MojoExecutionException When the android environment is not specified.
43       */
44      public static String getAndroidHome() throws MojoExecutionException {
45          final String androidHome = System.getenv(ENV_ANDROID_HOME);
46          if (androidHome == null || androidHome.isEmpty()) {
47              throw new MojoExecutionException("No Android SDK path could be found. You may configure it by setting the "
48              + ENV_ANDROID_HOME + " environment variable.");
49          }
50          final File androidHomeFile =  new File(androidHome);
51          if (!androidHomeFile.exists() || !androidHomeFile.isDirectory()) {
52              throw new MojoExecutionException("The android SDK path is not valid");
53          }
54          return androidHome;
55      }
56  
57      /**
58       * Retrieve the list of the available platforms for the specified Android SDK folder.
59       * @param androidSdkHome The Android SDK home folder.
60       * @return A list of android API version.
61       * @throws MojoExecutionException When the android SDK folder doesn't exist.
62       */
63      public static List<Integer> getAvailableAndroidPlatformVersions(File androidSdkHome) throws MojoExecutionException {
64          if (androidSdkHome == null) {
65              throw new MojoExecutionException("No android SDK home folder specified");
66          }
67          if (!androidSdkHome.exists()) {
68              throw new MojoExecutionException("The specified android SDK home folder doesn't exist: "
69              + androidSdkHome.getAbsolutePath());
70          }
71          if (!androidSdkHome.isDirectory()) {
72              throw new MojoExecutionException("The specified android sdk location is not a folder: "
73              + androidSdkHome.getAbsolutePath());
74          }
75          File sdk = new File(androidSdkHome, "platforms");
76          Pattern pattern = Pattern.compile("android-([0-9]+)");
77          List<Integer> result = new ArrayList<Integer>();
78          File[] sdkFiles = sdk.listFiles();
79          if (sdkFiles != null) {
80              for (File file : sdkFiles) {
81                  if (file.isDirectory() && file.getName().startsWith("android-")) {
82                      Matcher matcher = pattern.matcher(file.getName());
83                      if (matcher.find()) {
84                          result.add(new Integer(matcher.group(1)));
85                      }
86                  }
87              }
88          }
89          Collections.sort(result);
90          return result;
91      }
92  
93      /**
94       * Retrieve the latest android API version of the specified android SDK
95       * @param androidSdkHome The android SDK home folder.
96       * @return The latest android API version
97       * @throws MojoExecutionException When The androidSdkHome is not valid.
98       */
99      public static String getLatestAndroidPlatformVersion(File androidSdkHome) throws MojoExecutionException {
100         List<Integer> platforms = getAvailableAndroidPlatformVersions(androidSdkHome);
101         Integer lastVersion = platforms.get(platforms.size() - 1);
102         return lastVersion.toString();
103     }
104 
105     public static boolean isAndroidEmulatorRunning(File androidSdkHome) throws IOException {
106         if (androidSdkHome == null) {
107             return false;
108         }
109 
110         File adb = new File(androidSdkHome, "platform-tools" + File.separator + "adb");
111         if (!adb.exists()) {
112             return false;
113         }
114 
115         boolean isEmulatorRunning = false;
116 
117         ProcessBuilder pb = new ProcessBuilder(adb.getAbsolutePath(), "devices");
118         pb.redirectErrorStream(true);
119         Process p = pb.start();
120 
121         BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
122         String line = null;
123 
124         while ((line = reader.readLine()) != null) {
125             line = line.trim();
126             if (line.startsWith("List of devices")) {
127                 continue;
128             }
129             else if (line.startsWith("emulator-")) {
130                 String[] tokens = line.split("\\s");
131                 String name = tokens[0];
132                 String status = tokens[1];
133                 int port = Integer.parseInt(name.substring(name.indexOf("-") + 1));
134                 if (status.equals("device") && port == 5560) {
135                     isEmulatorRunning = true;
136                 }
137             }
138         }
139 
140         return isEmulatorRunning;
141 
142     }
143 
144     /**
145      * Create a new Android AVD.
146      *
147      * @param androidSdkHome The android SDK home folder.
148      * @param avdId The android API version.
149      * @param skin The skin of the emulator.
150      * @param log The logging system.
151      * @return true if the avd was successfully created, false if the avd already existed.
152      * @throws MojoExecutionException When an error occured during the AVD creation.
153      */
154     public static boolean createAvd(File androidSdkHome,
155                                     String avdId, String skin, Log log) throws MojoExecutionException {
156         String avdName = "titanium_" + avdId + "_"
157                 + skin;
158 
159         String homeDir = System.getProperty("user.home");
160         File avdDir = new File(homeDir, ".android" + File.separator + "avd"
161          + File.separator + avdName + ".avd");
162 
163         if (!avdDir.exists()) {
164             File androidCmd = new File(androidSdkHome, "tools" + File.separator + "android");
165             File sdCard = new File(homeDir, ".titanium" + File.separatorChar + avdName + ".sdcard");
166 
167             createSdCard(androidSdkHome, sdCard, "64M");
168             ProcessBuilder pb = new ProcessBuilder(androidCmd.getAbsolutePath(), "--verbose",
169                     "create", "avd",
170                     "-n", avdName, "-t", avdId,
171                     "-s", skin,
172                     "--force",
173                     "--sdcard", sdCard.getAbsolutePath());
174 
175             try {
176                 Process p = pb.start();
177                 BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
178                 //log.info("Start create AVD");
179                 //TitaniumPackageMojo.logProcess(p, log);
180                 log.info("Sending no");
181                 writer.write("no");
182                 writer.write("\n");
183                 writer.flush();
184                 TitaniumBuilder.logProcess(p, log);
185                 p.waitFor();
186             } catch (IOException ioe) {
187                 throw new MojoExecutionException("Error while creating avd", ioe);
188             } catch (InterruptedException ie) {
189                 throw new MojoExecutionException("Error while creating avd", ie);
190             }
191             return true;
192         }  else {
193             return false;
194         }
195     }
196 
197     /**
198      * Create a new SD Card.
199      *
200      * @param androidSdkHome The android SDK home folder.
201      * @param sdCard The SD card file name.
202      * @param size The size of the SD Card (i.e.: 64M)
203      * @throws MojoExecutionException When an error occured while creating the SD card.
204      */
205     private static void createSdCard(File androidSdkHome, File sdCard, String size)
206             throws MojoExecutionException {
207         File mkSdCardCmd = new File(androidSdkHome, "tools" + File.separatorChar + "mksdcard");
208 
209         ProcessBuilder pb = new ProcessBuilder(mkSdCardCmd.getAbsolutePath(),
210                 size, sdCard.getAbsolutePath());
211 
212         try {
213             pb.start().waitFor();
214         } catch (Throwable t) {
215             throw new MojoExecutionException("Error while creating AVD SDCard", t);
216         }
217     }
218 
219     private static boolean isWindows() {
220         return isOs("win");
221     }
222 
223     private static boolean isMac() {
224         return isOs("mac");
225     }
226 
227     private static boolean isUnix() {
228         return (isOs("nix") || isOs("nux"));
229     }
230 
231     private static boolean isOs(final String osName) {
232         final String os = System.getProperty("os.name").toLowerCase();
233         return (os.indexOf(osName) >= 0);
234     }
235 
236     public static String getOsClassifier() {
237         if (isWindows()) {
238             return "win32";
239         } else if (isMac()) {
240             return "osx";
241         } else if (isUnix()) {
242             return "linux";
243         } else {
244             return "";
245         }
246     }
247 
248     public static String getTitaniumSdkPath(String version) {
249         final String homeFolder = System.getProperty("user.home");
250         String sdkPath = null;
251         File sdkFile = null;
252 
253         if (isMac()) {
254             sdkPath = homeFolder + "/Library/Application Support/Titanium/mobilesdk/osx/" + version + "/";
255             sdkFile = new File(sdkPath);
256             if (!sdkFile.exists()) {
257                 sdkPath = "/Library/Application Support/Titanium/mobilesdk/osx/" + version + "/";
258                 sdkFile = new File(sdkPath);
259                 if (!sdkFile.exists()) {
260                     sdkPath = null;
261                 }
262             }
263         } else if (isWindows()) {
264             final String userProfileFolder = System.getenv("ALLUSERSPROFILE");
265             sdkPath = userProfileFolder + "\\Titanium\\mobilesdk\\win32\\" + version + "\\";
266             sdkFile = new File(sdkPath);
267             if (!sdkFile.exists()) {
268                 sdkPath = "C:\\Documents and Settings\\All Users\\Application Data\\Titanium\\mobilesdk\\win32\\" + version + "\\";
269                 sdkFile = new File(sdkPath);
270                 if (!sdkFile.exists()) {
271                     sdkPath = null;
272                 }
273             }
274         } else if (isUnix()) {
275             sdkPath = homeFolder + "/.titanium/mobilesdk/linux/" + version + "/";
276             sdkFile = new File(sdkPath);
277             if (!sdkFile.exists()) {
278                 sdkPath = null;
279             }
280         }
281 
282         return sdkPath;
283     }
284 
285     public static String getTitaniumArtifactSdkPath(String version, File targetDir) {
286         File sdkPath = new File(targetDir, "mobilesdk" + File.separator
287                 + TitaniumUtils.getOsClassifier() + File.separator
288                 + version);
289 
290         if (sdkPath.exists()) {
291             return sdkPath.getAbsolutePath();
292         } else {
293             return null;
294         }
295     }
296 
297     public static boolean isAndroidVersionValid(File androidSdkHome, String version)
298     throws MojoExecutionException {
299         List<Integer> versions = getAvailableAndroidPlatformVersions(androidSdkHome);
300         for (Integer v : versions) {
301             if (v.toString().equals(version)) {
302                 return true;
303             }
304         }
305         return false;
306     }
307 
308     public static void checkVirtualDevice(String platform, TitaniumSettings settings, VirtualDevice virtualDevice) throws MojoExecutionException {
309         if (platform.equals("android")) {
310             if (virtualDevice.getAndroidAPI() == null) {
311                 virtualDevice.setAndroidAPI(getLatestAndroidPlatformVersion(settings.getAndroidSdk()));
312             } else {
313                 if (!isAndroidVersionValid(settings.getAndroidSdk(), virtualDevice.getAndroidAPI())) {
314                     throw new MojoExecutionException("The specified android version is not present");
315                 }
316             }
317 
318             if (virtualDevice.getSkin() == null) {
319                 if (new Integer(virtualDevice.getAndroidAPI()).intValue() < 10) {
320                     virtualDevice.setSkin("HVGA");
321                 } else {
322                     virtualDevice.setSkin("WXGA");
323                 }
324             }
325         } else if (platform.equals("iphone")) {
326             if (virtualDevice.getFamily() == null) {
327                 virtualDevice.setFamily("iphone");
328             }
329         }  else if (platform.equals("ipad")) {
330             if (virtualDevice.getFamily() == null) {
331                 virtualDevice.setFamily("ipad");
332             }
333         }  else if (platform.equals("universal")) {
334             if (virtualDevice.getFamily() == null) {
335                 virtualDevice.setFamily("iphone");
336             }
337         }
338     }
339 
340     public static boolean isIphoneVersionValid(String version) {
341         List<String> platforms = listAvailableIosPlatformVersions();
342         for (String platform : platforms) {
343             if (platform.equals(version)) {
344                 return true;
345             }
346         }
347         return false;
348     }
349 
350     public static List<String> listAvailableIosPlatformVersions() {
351         List<String> results = new ArrayList<String>();
352         try {
353             ProcessBuilder pb = new ProcessBuilder("xcodebuild", "-showsdks");
354             pb.redirectErrorStream(true);
355             Process p = pb.start();
356             BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
357             String line = null;
358             Pattern pattern = Pattern.compile("-sdk iphoneos(([0-9]+.?)+)");
359             while ((line = reader.readLine()) != null) {
360                 Matcher m = pattern.matcher(line);
361                 while (m.find()) {
362                     results.add(m.group(1));
363                 }
364             }
365         } catch (Throwable t) {
366         }
367         return results;
368     }
369 
370     public static String getLatestIosPlatformVersion() {
371         List<String> platforms = listAvailableIosPlatformVersions();
372         if (!platforms.isEmpty()) {
373             return platforms.get(0);
374         }
375         return "4.3";
376     }
377 }