Show file numbers as 'n / N' in unified diff view

Change-Id: Ifd1399ef3744646fbcf220c31932a298563753b8
Signed-off-by: Edwin Kempin <ekempin@google.com>
diff --git a/app/src/main/java/com/google/reviewit/UnifiedDiffFragment.java b/app/src/main/java/com/google/reviewit/UnifiedDiffFragment.java
index b575409..33a4f80 100644
--- a/app/src/main/java/com/google/reviewit/UnifiedDiffFragment.java
+++ b/app/src/main/java/com/google/reviewit/UnifiedDiffFragment.java
@@ -93,9 +93,10 @@
       setVisible(v(R.id.navigationButtons));
       initPostReviewNavPanel(change);
       FileInfo file = files.get(path);
-      checkState(file != null, "File not found:" + path);
+      checkState(file != null, "File not found: " + path);
       init(path, change, files);
-      displayFile(change, path, file);
+      displayFile(change, path, file,
+          (new ArrayList<>(files.keySet())).indexOf(path) + 1, files.size());
     } else {
       setGone(v(R.id.navigationButtons));
       initPostReviewPanel(change);
@@ -196,7 +197,7 @@
         WidgetUtil.setText(v(R.id.navigationPrevFile),
             new File(prevPath).getName());
         setNavigationOnClickListener(R.id.navigationPrev, change, prevPath,
-            files);
+            i, files);
       } else {
         setInvisible(v(R.id.navigationPrev));
       }
@@ -205,7 +206,7 @@
         WidgetUtil.setText(v(R.id.navigationNextFile),
             new File(nextPath).getName());
         setNavigationOnClickListener(R.id.navigationNext, change, nextPath,
-            files);
+            i + 2, files);
       } else {
         setInvisible(v(R.id.navigationNext));
       }
@@ -214,29 +215,39 @@
 
   private void setNavigationOnClickListener(
       @IdRes int id, final Change change,
-      final String path, final Map<String, FileInfo> files) {
+      final String path, final int fileNumber,
+      final Map<String, FileInfo> files) {
     v(id).setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View v) {
         removeDiffView();
         createDiffView();
         init(path, change, files);
-        displayFile(change, path, files.get(path));
+        displayFile(change, path, files.get(path), fileNumber, files.size());
       }
     });
   }
 
-  private void displayFile(Change change, String path, FileInfo file) {
+  private void displayFile(Change change, String path, FileInfo file,
+                           int fileNumber, int totalFileCount) {
     Map<String, FileInfo> files = new HashMap<>();
     files.put(path, file);
-    displayFiles(change, files);
+    displayFiles(change, files, fileNumber, totalFileCount);
+  }
+
+  private void displayFiles(Change change, Map<String, FileInfo> files) {
+    displayFiles(change, files, 1, files.size());
   }
 
   private void displayFiles(final Change change,
-      Map<String, FileInfo> files) {
+                            final Map<String, FileInfo> files,
+                            final int startFileNumber,
+                            final int totalFileCount) {
     final Iterator<Map.Entry<String, FileInfo>> fileEntryIt =
         files.entrySet().iterator();
     diffView.setContent(new Iterator<ScrollWithHeadingsView.Content>() {
+      private int currentFileNumber = startFileNumber;
+
       @Override
       public boolean hasNext() {
         return fileEntryIt.hasNext();
@@ -248,10 +259,14 @@
         File f = new File(fileEntry.getKey());
         return new ScrollWithHeadingsView.Content(
             new ScrollWithHeadingsView.HeadingWithDetails(
-                getContext(), f.getName(), null,
-                FormatUtil.ensureSlash(f.getParent())),
-                createContent(change, fileEntry.getKey(),
-                    fileEntry.getValue()));
+                getContext(), f.getName(),
+                new ScrollWithHeadingsView.RightAlignedHeadingDetails(
+                    getContext().getString(R.string.file_count,
+                        currentFileNumber++, totalFileCount)),
+                new ScrollWithHeadingsView.CenteredHeadingDetails(
+                    FormatUtil.ensureSlash(f.getParent()))),
+            createContent(change, fileEntry.getKey(),
+                fileEntry.getValue()));
       }
 
       public View createContent(
diff --git a/app/src/main/java/com/google/reviewit/widget/ScrollWithHeadingsView.java b/app/src/main/java/com/google/reviewit/widget/ScrollWithHeadingsView.java
index 1972d3a..058a0f1 100644
--- a/app/src/main/java/com/google/reviewit/widget/ScrollWithHeadingsView.java
+++ b/app/src/main/java/com/google/reviewit/widget/ScrollWithHeadingsView.java
@@ -402,15 +402,22 @@
 
   public static class HeadingWithDetails extends TextHeading {
     private final RelativeLayout layout;
-    private final TextView detailsTop;
-    private final TextView detailsBottom;
+    private final TextView detailsTopView;
+    private final TextView detailsBottomView;
     private int height;
 
+    public HeadingWithDetails(
+        Context context, String headingText, String detailsTopText,
+        String detailsBottomText) {
+      this(context, headingText, new CenteredHeadingDetails(detailsTopText),
+          new CenteredHeadingDetails(detailsBottomText));
+    }
+
     // TODO make top/bottom heading smaller if there are no top/bottom
     // details
     public HeadingWithDetails(
-        Context context, String headingText, String detailsTopText,
-        String detailsBottomText) {
+        Context context, String headingText, HeadingDetails detailsTop,
+        HeadingDetails detailsBottom) {
       super(context, headingText);
 
       LayoutUtil.addOneTimeOnGlobalLayoutListener(headingView,
@@ -425,24 +432,38 @@
       layout = new RelativeLayout(context);
       layout.setLayoutParams(matchAndWrapLayout());
 
-      detailsTop = createDetailsView(detailsTopText);
-      layout.addView(detailsTop);
+      detailsTopView = createDetailsView(detailsTop);
+      if (detailsTopView != null) {
+        layout.addView(detailsTopView);
+      }
 
       layout.addView(headingView);
 
-      detailsBottom = createDetailsView(detailsBottomText);
-      layout.addView(gravityBottom(detailsBottom));
+      detailsBottomView = createDetailsView(detailsBottom);
+      if (detailsBottomView != null) {
+        layout.addView(gravityBottom(detailsBottomView));
+      }
     }
 
-    private TextView createDetailsView(String detailsText) {
+    private TextView createDetailsView(HeadingDetails details) {
+      if (details == null) {
+        return null;
+      }
+
       final MaxFontSizeTextView detailsView =
           new MaxFontSizeTextView(context);
-      detailsView.setLayoutParams(matchAndWrapLayout());
-      detailsView.setText(detailsText);
+      RelativeLayout.LayoutParams layoutParams =
+          new RelativeLayout.LayoutParams(
+              ViewGroup.LayoutParams.MATCH_PARENT,
+              ViewGroup.LayoutParams.WRAP_CONTENT);
+      layoutParams.setMargins(widgetUtil.dpToPx(5), 0,
+          widgetUtil.dpToPx(5), 0);
+      detailsView.setLayoutParams(layoutParams);
+      detailsView.setText(details.getText());
       detailsView.setTextColor(widgetUtil.color(R.color.headingFont));
       detailsView.setMinTextSize(widgetUtil.spToPx(5));
       detailsView.setMaxTextSize(widgetUtil.spToPx(15));
-      detailsView.setGravity(Gravity.CENTER_HORIZONTAL);
+      detailsView.setGravity(details.getGravity());
 
       LayoutUtil.addOnHeightListener(detailsView,
           new LayoutUtil.OnHeightListener() {
@@ -483,15 +504,27 @@
 
       float paddingSquish = heightToSquish / 2;
       if (paddingSquish > headingPaddingMax / 3) {
-        detailsTop.setAlpha(0f);
-        detailsBottom.setAlpha(0f);
+        if (detailsTopView != null) {
+          detailsTopView.setAlpha(0f);
+        }
+        if (detailsBottomView != null) {
+          detailsBottomView.setAlpha(0f);
+        }
       } else if (paddingSquish > 0) {
         float alpha = 1 - paddingSquish / (headingPaddingMax / 3);
-        detailsTop.setAlpha(alpha);
-        detailsBottom.setAlpha(alpha);
+        if (detailsTopView != null) {
+          detailsTopView.setAlpha(alpha);
+        }
+        if (detailsBottomView != null) {
+          detailsBottomView.setAlpha(alpha);
+        }
       } else {
-        detailsTop.setAlpha(1f);
-        detailsBottom.setAlpha(1f);
+        if (detailsTopView != null) {
+          detailsTopView.setAlpha(1f);
+        }
+        if (detailsBottomView != null) {
+          detailsBottomView.setAlpha(1f);
+        }
       }
 
       layout.getLayoutParams().height = height - heightToSquish;
@@ -507,4 +540,38 @@
       return ((ColorDrawable) layout.getBackground()).getColor();
     }
   }
+
+  public static abstract class HeadingDetails {
+    private final String text;
+
+    public HeadingDetails(String text) {
+      this.text = text;
+    }
+
+    public String getText() {
+      return text;
+    }
+
+    public abstract int getGravity();
+  }
+
+  public static class CenteredHeadingDetails extends HeadingDetails {
+    public CenteredHeadingDetails(String text) {
+      super(text);
+    }
+
+    public int getGravity() {
+      return Gravity.CENTER_HORIZONTAL;
+    }
+  }
+
+  public static class RightAlignedHeadingDetails extends HeadingDetails {
+    public RightAlignedHeadingDetails(String text) {
+      super(text);
+    }
+
+    public int getGravity() {
+      return Gravity.RIGHT;
+    }
+  }
 }
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b11a66c..7f6c378 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -241,6 +241,7 @@
 
   <!-- UnifiedDiffFragment -->
   <string name="title_activity_unified_diff">UnifiedDiffActivity</string>
+  <string name="file_count">%1$d / %2$d</string>
 
   <!-- UnfifiedDiffView -->
   <string name="skipped_common_lines">... skipped %1$d common lines ...</string>