Bug 922694 - Part 2: expose distribution ID. r=margaret
authorRichard Newman <rnewman@mozilla.com>
Wed, 16 Oct 2013 18:56:26 -0700
changeset 164868 e0bcb897d3bc1d882e04b518ea16663bd5c6cc29
parent 164867 24fa5d6e4d62200773fad723a8afbd628cbdf9c6
child 164869 6fe8a2c4841ed5d32593ab69e55529fbbda068c8
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmargaret
bugs922694
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 922694 - Part 2: expose distribution ID. r=margaret
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");
     }
 }