diff --git a/mobile/android/base/Distribution.java b/mobile/android/base/Distribution.java index 999d07897088..a42d90332b0b 100644 --- a/mobile/android/base/Distribution.java +++ b/mobile/android/base/Distribution.java @@ -9,6 +9,7 @@ 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; @@ -20,8 +21,12 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.Scanner; +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.zip.ZipEntry; import java.util.zip.ZipFile; @@ -32,6 +37,48 @@ public final class Distribution { 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 localizedAbout; + + @SuppressWarnings("unchecked") + public DistributionDescriptor(JSONObject obj) { + this.id = obj.optString("id"); + this.version = obj.optString("version"); + this.about = obj.optString("about"); + Map loc = new HashMap(); + try { + Iterator 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. @@ -217,9 +264,18 @@ public final class Distribution { 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(); @@ -227,24 +283,50 @@ public final class Distribution { 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) { @@ -254,6 +336,19 @@ public final class Distribution { 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; }