Proper I18N of the Details dialog, especially for RTL.

  Bug: 7141309

Change-Id: I27a2efd83b355cf2c7fa6cc6c9b386c1c0496c6b
diff --git a/res/layout/details.xml b/res/layout/details.xml
index dfda0ee..1fea0a0 100644
--- a/res/layout/details.xml
+++ b/res/layout/details.xml
@@ -19,5 +19,5 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:textAppearance="?android:attr/textAppearanceMedium"
-    android:gravity="left"
+    android:gravity="start"
 />
diff --git a/src/com/android/camera/data/LocalMediaData.java b/src/com/android/camera/data/LocalMediaData.java
index 48db0df..1f87b50 100644
--- a/src/com/android/camera/data/LocalMediaData.java
+++ b/src/com/android/camera/data/LocalMediaData.java
@@ -43,6 +43,7 @@
 import java.io.File;
 import java.text.DateFormat;
 import java.util.Date;
+import java.util.Locale;
 
 /**
  * A base class for all the local media files. The bitmap is loaded in
@@ -219,19 +220,21 @@
 
     @Override
     public MediaDetails getMediaDetails(Context context) {
-        DateFormat formater = DateFormat.getDateTimeInstance();
+        DateFormat dateFormatter = DateFormat.getDateTimeInstance();
         MediaDetails mediaDetails = new MediaDetails();
         mediaDetails.addDetail(MediaDetails.INDEX_TITLE, title);
         mediaDetails.addDetail(MediaDetails.INDEX_WIDTH, width);
         mediaDetails.addDetail(MediaDetails.INDEX_HEIGHT, height);
         mediaDetails.addDetail(MediaDetails.INDEX_PATH, path);
         mediaDetails.addDetail(MediaDetails.INDEX_DATETIME,
-                formater.format(new Date(dateModifiedInSeconds * 1000)));
+                dateFormatter.format(new Date(dateModifiedInSeconds * 1000)));
         if (sizeInBytes > 0) {
             mediaDetails.addDetail(MediaDetails.INDEX_SIZE, sizeInBytes);
         }
         if (latitude != 0 && longitude != 0) {
-            mediaDetails.addDetail(MediaDetails.INDEX_LOCATION, latitude + ", " + longitude);
+            String locationString = String.format(Locale.getDefault(), "%f, %f", latitude,
+                    longitude);
+            mediaDetails.addDetail(MediaDetails.INDEX_LOCATION, locationString);
         }
         return mediaDetails;
     }
diff --git a/src/com/android/camera/data/MediaDetails.java b/src/com/android/camera/data/MediaDetails.java
index 3049e77..a614b8d 100644
--- a/src/com/android/camera/data/MediaDetails.java
+++ b/src/com/android/camera/data/MediaDetails.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.util.Log;
+import android.util.SparseIntArray;
 
 import com.android.camera2.R;
 import com.android.gallery3d.exif.ExifInterface;
@@ -25,7 +26,6 @@
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map.Entry;
 import java.util.TreeMap;
@@ -35,7 +35,7 @@
     private static final String TAG = "MediaDetails";
 
     private TreeMap<Integer, Object> mDetails = new TreeMap<Integer, Object>();
-    private HashMap<Integer, Integer> mUnits = new HashMap<Integer, Integer>();
+    private SparseIntArray mUnits = new SparseIntArray();
 
     public static final int INDEX_TITLE = 1;
     public static final int INDEX_DESCRIPTION = 2;
@@ -101,7 +101,7 @@
     }
 
     public boolean hasUnit(int index) {
-        return mUnits.containsKey(index);
+        return mUnits.indexOfKey(index) >= 0;
     }
 
     public int getUnit(int index) {
diff --git a/src/com/android/camera/ui/DetailsDialog.java b/src/com/android/camera/ui/DetailsDialog.java
index cc0130d..bd4efea 100644
--- a/src/com/android/camera/ui/DetailsDialog.java
+++ b/src/com/android/camera/ui/DetailsDialog.java
@@ -33,7 +33,10 @@
 import com.android.camera.data.MediaDetails;
 import com.android.camera2.R;
 
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
 import java.util.ArrayList;
+import java.util.Locale;
 import java.util.Map.Entry;
 
 /**
@@ -72,7 +75,8 @@
         private final Context mContext;
         private final MediaDetails mMediaDetails;
         private final ArrayList<String> mItems;
-        private int mLocationIndex;
+        private final Locale mDefaultLocale = Locale.getDefault();
+        private final DecimalFormat mDecimalFormat = new DecimalFormat(".####");
         private int mWidthIndex = -1;
         private int mHeightIndex = -1;
 
@@ -80,7 +84,6 @@
             mContext = context;
             mMediaDetails = details;
             mItems = new ArrayList<String>(details.size());
-            mLocationIndex = -1;
             setDetails(context, details);
         }
 
@@ -90,7 +93,6 @@
             for (Entry<Integer, Object> detail : details) {
                 String value;
                 switch (detail.getKey()) {
-                    // TODO: Resolve address asynchronously.
                     case MediaDetails.INDEX_SIZE: {
                         value = Formatter.formatFileSize(
                                 context, (Long) detail.getValue());
@@ -118,20 +120,22 @@
                         value = (String) detail.getValue();
                         double time = Double.valueOf(value);
                         if (time < 1.0f) {
-                            value = String.format("1/%d", (int) (0.5f + 1 / time));
+                            value = String.format(mDefaultLocale, "%d/%d", 1,
+                                    (int) (0.5f + 1 / time));
                         } else {
                             int integer = (int) time;
                             time -= integer;
                             value = String.valueOf(integer) + "''";
                             if (time > 0.0001) {
-                                value += String.format(" 1/%d", (int) (0.5f + 1 / time));
+                                value += String.format(mDefaultLocale, " %d/%d", 1,
+                                        (int) (0.5f + 1 / time));
                             }
                         }
                         break;
                     }
                     case MediaDetails.INDEX_WIDTH:
                         mWidthIndex = mItems.size();
-                        value = detail.getValue().toString();
+                        value = toLocalNumber((Integer) detail.getValue());
                         if (value.equalsIgnoreCase("0")) {
                             value = context.getString(R.string.unknown);
                             resolutionIsValid = false;
@@ -139,7 +143,7 @@
                         break;
                     case MediaDetails.INDEX_HEIGHT: {
                         mHeightIndex = mItems.size();
-                        value = detail.getValue().toString();
+                        value = toLocalNumber((Integer) detail.getValue());
                         if (value.equalsIgnoreCase("0")) {
                             value = context.getString(R.string.unknown);
                             resolutionIsValid = false;
@@ -147,9 +151,21 @@
                         break;
                     }
                     case MediaDetails.INDEX_PATH:
-                        // Get the path and then fall through to the default
-                        // case
+                        // Prepend the new-line as a) paths are usually long, so
+                        // the formatting is better and b) an RTL UI will see it
+                        // as a separate section and interpret it for what it
+                        // is, rather than trying to make it RTL (which messes
+                        // up the path).
+                        value = "\n" + detail.getValue().toString();
                         path = detail.getValue().toString();
+                        break;
+                    case MediaDetails.INDEX_ISO:
+                        value = toLocalNumber(Integer.parseInt((String) detail.getValue()));
+                        break;
+                    case MediaDetails.INDEX_FOCAL_LENGTH:
+                        double focalLength = Double.parseDouble(detail.getValue().toString());
+                        value = toLocalNumber(focalLength);
+                        break;
                     default: {
                         Object valueObj = detail.getValue();
                         // This shouldn't happen, log its key to help us
@@ -171,9 +187,9 @@
                             context, key), value);
                 }
                 mItems.add(value);
-                if (!resolutionIsValid) {
-                    resolveResolution(path);
-                }
+            }
+            if (!resolutionIsValid) {
+                resolveResolution(path);
             }
         }
 
@@ -226,16 +242,26 @@
             if (width == 0 || height == 0)
                 return;
             // Update the resolution with the new width and height
-            String widthString = String.format("%s: %d",
+            String widthString = String.format(mDefaultLocale, "%s: %d",
                     getDetailsName(
                             mContext, MediaDetails.INDEX_WIDTH), width);
-            String heightString = String.format("%s: %d",
+            String heightString = String.format(mDefaultLocale, "%s: %d",
                     getDetailsName(
                             mContext, MediaDetails.INDEX_HEIGHT), height);
             mItems.set(mWidthIndex, String.valueOf(widthString));
             mItems.set(mHeightIndex, String.valueOf(heightString));
             notifyDataSetChanged();
         }
+
+        /** Converts the given integer to a localized String version. */
+        private String toLocalNumber(int n) {
+            return String.format(mDefaultLocale, "%d", n);
+        }
+
+        /** Converts the given double to a localized String version. */
+        private String toLocalNumber(double n) {
+            return mDecimalFormat.format(n);
+        }
     }
 
     public static String getDetailsName(Context context, int key) {