Refactor file box into an own view

Have the common info box functionality, like an action button in the
title and expanding by pages, in an abstract base class, so that this
functionality can be reused for other info boxes.

Change-Id: Ibcfec07cef5b8d2951bb3288a12923a4b0ef5fd8
Signed-off-by: Edwin Kempin <ekempin@google.com>
diff --git a/app/src/main/java/com/google/reviewit/DetailedChangeFragment.java b/app/src/main/java/com/google/reviewit/DetailedChangeFragment.java
index 2991696..f53b684 100644
--- a/app/src/main/java/com/google/reviewit/DetailedChangeFragment.java
+++ b/app/src/main/java/com/google/reviewit/DetailedChangeFragment.java
@@ -15,7 +15,6 @@
 package com.google.reviewit;
 
 import android.content.Intent;
-import android.graphics.Paint;
 import android.os.Bundle;
 import android.support.annotation.LayoutRes;
 import android.text.util.Linkify;
@@ -24,13 +23,9 @@
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.MotionEvent;
-import android.view.View;
-import android.widget.TableLayout;
-import android.widget.TableRow;
 import android.widget.TextView;
 
 import com.google.gerrit.extensions.client.ChangeStatus;
-import com.google.gerrit.extensions.common.FileInfo;
 import com.google.reviewit.app.SortActionHandler;
 import com.google.reviewit.app.Change;
 import com.google.reviewit.util.ChangeUtil;
@@ -39,14 +34,11 @@
 import com.google.reviewit.util.TaskObserver;
 import com.google.reviewit.util.WidgetUtil;
 import com.google.reviewit.widget.ApprovalsView;
+import com.google.reviewit.widget.FileBox;
 import com.google.reviewit.widget.ZoomHandler;
 
-import java.util.Map;
 import java.util.regex.Pattern;
 
-import static com.google.reviewit.util.LayoutUtil.matchAndWrapTableLayout;
-import static com.google.reviewit.util.LayoutUtil.matchAndWrapTableRowLayout;
-import static com.google.reviewit.util.WidgetUtil.setGone;
 import static com.google.reviewit.util.WidgetUtil.setVisible;
 
 /**
@@ -56,7 +48,6 @@
     OnBackPressedAware, DispatchTouchEventAware {
   private static final String TAG = DetailedChangeFragment.class.getName();
 
-  private static final int PAGE_SIZE = 10;
   private static final Pattern PATTERN_CHANGE_ID =
       Pattern.compile("I[0-9a-f]{5,40}");
   private static final String PART_LINK = "(?:"
@@ -99,7 +90,7 @@
       displayChangeUrl(change);
       ((ApprovalsView) v(R.id.approvals)).displayApprovals(getApp(),
           change.info, this);
-      displayFiles(change, 1, false);
+      ((FileBox)v(R.id.fileBox)).display(this, change);
       // TODO show further change info, e.g. summary comments, hashtags,
       // related changes
     } catch (Throwable t) {
@@ -114,18 +105,7 @@
   }
 
   private void init() {
-    TextView commitMsg = (TextView) v(R.id.commitMessage);
-    commitMsg.setLinksClickable(true);
-
-    v(R.id.reviewButton).setOnClickListener(new View.OnClickListener() {
-      @Override
-      public void onClick(View v) {
-        display(UnifiedDiffFragment.class);
-      }
-    });
-
-    WidgetUtil.underline(tv(R.id.showMore));
-    WidgetUtil.underline(tv(R.id.showAll));
+    tv(R.id.commitMessage).setLinksClickable(true);
   }
 
   private void linkify() {
@@ -145,76 +125,6 @@
     return FormatUtil.ensureSlash(serverUrl);
   }
 
-  private void displayFiles(
-      final Change change, final int page, boolean showAll) {
-    TableLayout tl = (TableLayout) v(R.id.filesTable);
-    Map<String, FileInfo> files = change.currentRevision().files;
-    int count = 0;
-    for (Map.Entry<String, FileInfo> e : files.entrySet()) {
-      count++;
-      if (count <= (page - 1) * PAGE_SIZE) {
-        continue;
-      }
-      if (!showAll && count > page * PAGE_SIZE) {
-        break;
-      }
-      addFileRow(tl, e.getKey(), e.getValue());
-    }
-    if (!showAll && files.size() > page * PAGE_SIZE) {
-      WidgetUtil.setText(v(R.id.showAll),
-          getString(R.string.show_all, files.size() - page * PAGE_SIZE));
-      setVisible(v(R.id.fileButtons));
-      v(R.id.showAll).setOnClickListener(new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-          displayFiles(change, page + 1, true);
-        }
-      });
-      if (files.size() > (page + 1) * PAGE_SIZE) {
-        WidgetUtil.setText(v(R.id.showMore),
-            getString(R.string.show_more, PAGE_SIZE));
-        setVisible(v(R.id.showMoreArea, R.id.showMore));
-        v(R.id.showMore).setOnClickListener(new View.OnClickListener() {
-          @Override
-          public void onClick(View v) {
-            displayFiles(change, page + 1, false);
-          }
-        });
-      } else {
-        setGone(v(R.id.showMoreArea, R.id.showMore));
-      }
-    } else {
-      setGone(v(R.id.fileButtons));
-    }
-  }
-
-  private void addFileRow(TableLayout tl, final String path, FileInfo file) {
-    TableRow tr = new TableRow(getActivity());
-    tr.setLayoutParams(matchAndWrapTableRowLayout());
-
-    tr.addView(widgetUtil.tableRowRightMargin(widgetUtil.createTextView(
-        file.status != null ? Character.toString(file.status) : "M", 11), 4));
-
-    TextView pathText = widgetUtil.createTextView(path, 11);
-    pathText.setTextColor(widgetUtil.color(R.color.hyperlink));
-    pathText.setPaintFlags(
-        pathText.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
-    pathText.setOnClickListener(new View.OnClickListener() {
-      @Override
-      public void onClick(View v) {
-        display(UnifiedDiffFragment.create(path));
-      }
-    });
-    tr.addView(widgetUtil.tableRowRightMargin(pathText, 4));
-
-    tr.addView(widgetUtil.createTextView(
-        FormatUtil.formatBytes(file.size), 11));
-
-    // TODO show further file infos
-
-    tl.addView(tr, matchAndWrapTableLayout());
-  }
-
   @Override
   public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
     SortActionHandler actionHandler = getApp().getSortActionHandler();
diff --git a/app/src/main/java/com/google/reviewit/widget/FileBox.java b/app/src/main/java/com/google/reviewit/widget/FileBox.java
new file mode 100644
index 0000000..5f0c9ab
--- /dev/null
+++ b/app/src/main/java/com/google/reviewit/widget/FileBox.java
@@ -0,0 +1,104 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.reviewit.widget;
+
+import android.content.Context;
+import android.graphics.Paint;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.StringRes;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TableLayout;
+import android.widget.TableRow;
+import android.widget.TextView;
+
+import com.google.gerrit.extensions.common.FileInfo;
+import com.google.reviewit.BaseFragment;
+import com.google.reviewit.R;
+import com.google.reviewit.UnifiedDiffFragment;
+import com.google.reviewit.app.Change;
+import com.google.reviewit.util.FormatUtil;
+
+import java.util.ArrayList;
+import java.util.Map;
+
+import static com.google.reviewit.util.LayoutUtil.matchAndWrapTableLayout;
+import static com.google.reviewit.util.LayoutUtil.matchAndWrapTableRowLayout;
+
+public class FileBox extends InfoBox<Map.Entry<String, FileInfo>> {
+  public FileBox(Context context) {
+    super(context);
+  }
+
+  public FileBox(Context context, AttributeSet attrs) {
+    super(context, attrs);
+  }
+
+  public FileBox(Context context, AttributeSet attrs, int defStyle) {
+    super(context, attrs, defStyle);
+  }
+
+  @Override
+  protected @StringRes int getTitle() {
+    return R.string.files;
+  }
+
+  @Override
+  protected @DrawableRes int getIcon() {
+    return R.drawable.ic_feedback_black_18dp;
+  }
+
+  @Override
+  protected void onAction(BaseFragment fragment) {
+
+    fragment.display(UnifiedDiffFragment.class);
+  }
+
+  public void display(BaseFragment fragment, Change change) {
+    display(fragment,
+        new ArrayList<>(change.currentRevision().files.entrySet()));
+  }
+
+  @Override
+  protected void addRow(TableLayout tl, Map.Entry<String, FileInfo> entry) {
+    final String path = entry.getKey();
+    FileInfo file = entry.getValue();
+
+    TableRow tr = new TableRow(getContext());
+    tr.setLayoutParams(matchAndWrapTableRowLayout());
+
+    tr.addView(widgetUtil.tableRowRightMargin(widgetUtil.createTextView(
+        file.status != null ? Character.toString(file.status) : "M", 11), 4));
+
+    TextView pathText = widgetUtil.createTextView(path, 11);
+    pathText.setTextColor(widgetUtil.color(R.color.hyperlink));
+    pathText.setPaintFlags(
+        pathText.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
+    pathText.setOnClickListener(new View.OnClickListener() {
+      @Override
+      public void onClick(View v) {
+        fragment.display(UnifiedDiffFragment.create(path));
+      }
+    });
+    tr.addView(widgetUtil.tableRowRightMargin(pathText, 4));
+
+    tr.addView(widgetUtil.createTextView(
+        FormatUtil.formatBytes(file.size), 11));
+
+    // TODO show further file infos
+
+    tl.addView(tr, matchAndWrapTableLayout());
+  }
+}
diff --git a/app/src/main/java/com/google/reviewit/widget/InfoBox.java b/app/src/main/java/com/google/reviewit/widget/InfoBox.java
new file mode 100644
index 0000000..8d89387
--- /dev/null
+++ b/app/src/main/java/com/google/reviewit/widget/InfoBox.java
@@ -0,0 +1,142 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.reviewit.widget;
+
+import android.content.Context;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.StringRes;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TableLayout;
+import android.widget.TextView;
+
+import com.google.reviewit.BaseFragment;
+import com.google.reviewit.R;
+import com.google.reviewit.util.WidgetUtil;
+
+import java.util.List;
+
+import static com.google.reviewit.util.WidgetUtil.setGone;
+import static com.google.reviewit.util.WidgetUtil.setVisible;
+
+public abstract class InfoBox<T> extends RelativeLayout {
+  private static final int PAGE_SIZE = 10;
+
+  protected final WidgetUtil widgetUtil;
+  protected BaseFragment fragment;
+
+  public InfoBox(Context context) {
+    this(context, null, 0);
+  }
+
+  public InfoBox(Context context, AttributeSet attrs) {
+    this(context, attrs, 0);
+  }
+
+  public InfoBox(Context context, AttributeSet attrs, int defStyle) {
+    super(context, attrs, defStyle);
+
+    this.widgetUtil = new WidgetUtil(context);
+
+    inflate(context, R.layout.info_box, this);
+
+    init();
+  }
+
+  private void init() {
+    ((TextView)findViewById(R.id.info_title)).setText(getTitle());
+
+    if (getIcon() > 0) {
+      ImageView action = (ImageView)findViewById(R.id.info_action);
+      action.setImageDrawable(widgetUtil.getDrawable(getIcon()));
+      setVisible(action);
+      action.setOnClickListener(
+          new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+              onAction(fragment);
+            }
+          });
+    }
+
+    WidgetUtil.underline((TextView) findViewById(R.id.show_more));
+    WidgetUtil.underline((TextView) findViewById(R.id.show_all));
+  }
+
+  protected abstract @StringRes int getTitle();
+
+  protected @DrawableRes int getIcon() {
+    return -1;
+  }
+
+  protected void onAction(BaseFragment fragment) {
+  }
+
+  protected void display(BaseFragment fragment, List<T> entries) {
+    this.fragment = fragment;
+    display(entries, 1, false);
+  }
+
+  private void display(
+      final List<T> entries, final int page, boolean showAll) {
+    TableLayout tl = (TableLayout) findViewById(R.id.info_table);
+    int count = 0;
+    for (T e : entries) {
+      count++;
+      if (count <= (page - 1) * PAGE_SIZE) {
+        continue;
+      }
+      if (!showAll && count > page * PAGE_SIZE) {
+        break;
+      }
+      addRow(tl, e);
+    }
+    if (!showAll && entries.size() > page * PAGE_SIZE) {
+      WidgetUtil.setText(findViewById(R.id.show_all),
+          fragment.getString(R.string.show_all, entries.size() - page *
+              PAGE_SIZE));
+      setVisible(findViewById(R.id.info_buttons));
+      findViewById(R.id.show_all).setOnClickListener(
+          new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+              display(entries, page + 1, true);
+          }
+        });
+      if (entries.size() > (page + 1) * PAGE_SIZE) {
+        WidgetUtil.setText(findViewById(R.id.show_more),
+            fragment.getString(R.string.show_more, PAGE_SIZE));
+        setVisible(findViewById(R.id.show_more_area),
+            findViewById(R.id.show_more));
+        findViewById(R.id.show_more).setOnClickListener(
+            new View.OnClickListener() {
+              @Override
+              public void onClick(View v) {
+                display(entries, page + 1, false);
+              }
+          });
+      } else {
+        setGone(findViewById(R.id.show_more_area),
+            findViewById(R.id.show_more));
+      }
+    } else {
+      setGone(findViewById(R.id.info_buttons));
+    }
+  }
+
+  protected abstract void addRow(TableLayout tl, T entry);
+}
diff --git a/app/src/main/res/layout/content_detailed_change.xml b/app/src/main/res/layout/content_detailed_change.xml
index d0779c6..2c11c03 100644
--- a/app/src/main/res/layout/content_detailed_change.xml
+++ b/app/src/main/res/layout/content_detailed_change.xml
@@ -61,7 +61,11 @@
         android:layout_width="match_parent"
         android:layout_height="10dp"/>
 
-      <include layout="@layout/files"/>
+      <com.google.reviewit.widget.FileBox
+        android:id="@+id/fileBox"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+      </com.google.reviewit.widget.FileBox>
 
       <View
         android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/files.xml b/app/src/main/res/layout/info_box.xml
similarity index 88%
rename from app/src/main/res/layout/files.xml
rename to app/src/main/res/layout/info_box.xml
index 597b1c8..cac5fb1 100644
--- a/app/src/main/res/layout/files.xml
+++ b/app/src/main/res/layout/info_box.xml
@@ -21,7 +21,7 @@
               android:background="@drawable/info_table">
 
   <TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
-               android:id="@+id/filesTable"
+               android:id="@+id/info_table"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:stretchColumns="1"
@@ -36,41 +36,40 @@
         android:layout_span="2">
 
         <ImageView
-          android:id="@+id/reviewButton"
+          android:id="@+id/info_action"
           android:layout_width="24dp"
           android:layout_height="24dp"
           android:layout_marginTop="3dp"
           android:layout_marginRight="3dp"
           android:clickable="true"
-          android:src="@drawable/ic_feedback_black_18dp"/>
+          android:visibility="gone"/>
 
         <TextView
-          android:id="@+id/filesLabel"
+          android:id="@+id/info_title"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:layout_marginBottom="3dp"
-          android:textSize="20dp"
-          android:text="@string/files"/>
+          android:textSize="20dp"/>
       </LinearLayout>
     </TableRow>
   </TableLayout>
 
   <LinearLayout
-    android:id="@+id/fileButtons"
+    android:id="@+id/info_buttons"
     android:orientation="horizontal"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:visibility="gone">
 
     <View
-      android:id="@+id/showMoreArea"
+      android:id="@+id/show_more_area"
       android:layout_width="0dp"
       android:layout_height="0dp"
       android:layout_weight="1"
       />
 
     <Button
-      android:id="@+id/showMore"
+      android:id="@+id/show_more"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:textColor="@color/hyperlink"
@@ -87,7 +86,7 @@
       />
 
     <Button
-      android:id="@+id/showAll"
+      android:id="@+id/show_all"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:textColor="@color/hyperlink"