Bug 922694 - Part 2: expose distribution ID. r=margaret, a=lsblakk
authorRichard Newman <rnewman@mozilla.com>
Wed, 16 Oct 2013 18:56:26 -0700
changeset 160820 28bbf913f9a549437359039e57f57b0ee3c9dad1
parent 160819 9f3393ae0d5bf1014555a80061b9e63de9719efc
child 160821 c91d799e46ffe3c28ce43e1695072cda858a888f
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmargaret, lsblakk
bugs922694
milestone26.0a2
Bug 922694 - Part 2: expose distribution ID. r=margaret, a=lsblakk
mobile/android/base/Distribution.java
--- a/mobile/android/base/Distribution.java
+++ b/mobile/android/base/Distribution.java
@@ -4,39 +4,86 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.util.ThreadUtils;
 
 import org.json.JSONArray;
 import org.json.JSONException;
+import org.json.JSONObject;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.util.Log;
 
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
 import java.util.Scanner;
-import java.util.Enumeration;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
 public final class Distribution {
     private static final String LOGTAG = "GeckoDistribution";
 
     private static final int STATE_UNKNOWN = 0;
     private static final int STATE_NONE = 1;
     private static final int STATE_SET = 2;
 
+    public static class DistributionDescriptor {
+        public final boolean valid;
+        public final String id;
+        public final String version;    // Example uses a float, but that's a crazy idea.
+
+        // Default UI-visible description of the distribution.
+        public final String about;
+
+        // Each distribution file can include multiple localized versions of
+        // the 'about' string. These are represented as, e.g., "about.en-US"
+        // keys in the Global object.
+        // Here we map locale to description.
+        public final Map<String, String> localizedAbout;
+
+        @SuppressWarnings("unchecked")
+        public DistributionDescriptor(JSONObject obj) {
+            this.id = obj.optString("id");
+            this.version = obj.optString("version");
+            this.about = obj.optString("about");
+            Map<String, String> loc = new HashMap<String, String>();
+            try {
+                Iterator<String> keys = obj.keys();
+                while (keys.hasNext()) {
+                    String key = keys.next();
+                    if (key.startsWith("about.")) {
+                        String locale = key.substring(6);
+                        if (!obj.isNull(locale)) {
+                            loc.put(locale, obj.getString(key));
+                        }
+                    }
+                }
+            } catch (JSONException ex) {
+                Log.w(LOGTAG, "Unable to completely process distribution JSON.", ex);
+            }
+
+            this.localizedAbout = Collections.unmodifiableMap(loc);
+            this.valid = (null != this.id) &&
+                         (null != this.version) &&
+                         (null != this.about);
+        }
+    }
+
     /**
      * Initializes distribution if it hasn't already been initalized. Sends
      * messages to Gecko as appropriate.
      *
      * @param packagePath where to look for the distribution directory.
      */
     public static void init(final Context context, final String packagePath) {
         // Read/write preferences and files on the background thread.
@@ -212,53 +259,101 @@ public final class Distribution {
         }
         File system = getSystemDistributionDir();
         if (system.exists()) {
             return this.distributionDir = system;
         }
         return null;
     }
 
-    public JSONArray getBookmarks() {
+    /**
+     * Helper to grab a file in the distribution directory.
+     *
+     * Returns null if there is no distribution directory or the file
+     * doesn't exist. Ensures init first.
+     */
+    private File getDistributionFile(String name) {
+        Log.i(LOGTAG, "Getting file from distribution.");
         if (this.state == STATE_UNKNOWN) {
-            this.doInit();
+            if (!this.doInit()) {
+                return null;
+            }
         }
 
         File dist = ensureDistributionDir();
         if (dist == null) {
             return null;
         }
 
-        File bookmarks = new File(dist, "bookmarks.json");
-        if (!bookmarks.exists()) {
+        File descFile = new File(dist, name);
+        if (!descFile.exists()) {
+            Log.e(LOGTAG, "Distribution directory exists, but no file named " + name);
+            return null;
+        }
+
+        return descFile;
+    }
+
+    public DistributionDescriptor getDescriptor() {
+        File descFile = getDistributionFile("preferences.json");
+        if (descFile == null) {
+            // Logging and existence checks are handled in getDistributionFile.
             return null;
         }
 
-        // Shortcut to slurp a file without messing around with streams.
         try {
-            Scanner scanner = null;
-            try {
-                scanner = new Scanner(bookmarks, "UTF-8");
-                final String contents = scanner.useDelimiter("\\A").next();
-                return new JSONArray(contents);
-            } finally {
-                if (scanner != null) {
-                    scanner.close();
-                }
+            JSONObject all = new JSONObject(getFileContents(descFile));
+
+            if (!all.has("Global")) {
+                Log.e(LOGTAG, "Distribution preferences.json has no Global entry!");
+                return null;
             }
 
+            return new DistributionDescriptor(all.getJSONObject("Global"));
+
+        } catch (IOException e) {
+            Log.e(LOGTAG, "Error getting distribution descriptor file.", e);
+            return null;
+        } catch (JSONException e) {
+            Log.e(LOGTAG, "Error parsing preferences.json", e);
+            return null;
+        }
+    }
+
+    public JSONArray getBookmarks() {
+        File bookmarks = getDistributionFile("bookmarks.json");
+        if (bookmarks == null) {
+            // Logging and existence checks are handled in getDistributionFile.
+            return null;
+        }
+
+        try {
+            return new JSONArray(getFileContents(bookmarks));
         } catch (IOException e) {
             Log.e(LOGTAG, "Error getting bookmarks", e);
         } catch (JSONException e) {
             Log.e(LOGTAG, "Error parsing bookmarks.json", e);
         }
 
         return null;
     }
 
+    // Shortcut to slurp a file without messing around with streams.
+    private String getFileContents(File file) throws IOException {
+        Scanner scanner = null;
+        try {
+            scanner = new Scanner(file, "UTF-8");
+            return scanner.useDelimiter("\\A").next();
+        } finally {
+            if (scanner != null) {
+                scanner.close();
+            }
+        }
+    }
+
     private String getDataDir() {
         return context.getApplicationInfo().dataDir;
     }
 
     private File getSystemDistributionDir() {
         return new File("/system/" + context.getPackageName() + "/distribution");
     }
 }