Refactor: Create a view for posting a review

This makes the code reusable.

Change-Id: I4f32d9c20d32e275b4e5a8f9684110d74ef4ecdd
Signed-off-by: Edwin Kempin <ekempin@google.com>
diff --git a/app/src/main/java/com/google/reviewit/PostReviewFragment.java b/app/src/main/java/com/google/reviewit/PostReviewFragment.java
index a084069..f855c53 100644
--- a/app/src/main/java/com/google/reviewit/PostReviewFragment.java
+++ b/app/src/main/java/com/google/reviewit/PostReviewFragment.java
@@ -15,27 +15,12 @@
 package com.google.reviewit;
 
 import android.os.Bundle;
-import android.support.annotation.DrawableRes;
 import android.support.annotation.LayoutRes;
-import android.view.View;
-import android.view.ViewGroup;
 import android.view.WindowManager;
-import android.widget.ImageView;
 
-import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.reviewit.app.Change;
-import com.google.reviewit.util.ObservableAsyncTask;
 import com.google.reviewit.util.TaskObserver;
-import com.google.reviewit.widget.ApprovalsView;
-import com.google.reviewit.widget.ExpandableCommitMessageView;
-import com.google.reviewit.widget.VoteView;
-import com.urswolfer.gerrit.client.rest.http.HttpStatusException;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import static com.google.reviewit.util.WidgetUtil.setGone;
-import static com.google.reviewit.util.WidgetUtil.setVisible;
+import com.google.reviewit.widget.PostReviewView;
 
 /**
  * Fragment to post a review
@@ -44,8 +29,6 @@
   private static final String VOTE =
       "com.google.reviewit.PostReviewFragment.VOTE";
 
-  private Map<String, Integer> selectedValues = new HashMap<>();
-
   public static PostReviewFragment create(int vote) {
     PostReviewFragment fragment = new PostReviewFragment();
     Bundle bundle = new Bundle();
@@ -70,151 +53,10 @@
         WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
 
     int vote = getArguments().getInt(VOTE);
-    update(vote);
     Change change = getApp().getCurrentChange();
 
     setTitle(getString(R.string.detailed_change_title, change.info._number));
-    init(change);
-    initLabels(change, vote);
-    ((ApprovalsView) v(R.id.approvals)).displayApprovals(getApp(),
-        change.info, this);
-  }
-
-  private void init(final Change change) {
-    ((ExpandableCommitMessageView)v(R.id.commitMessage)).init(change);
-
-    v(R.id.postReviewButton).setOnClickListener(new View.OnClickListener() {
-      @Override
-      public void onClick(final View v) {
-        v.setEnabled(false);
-        v.setBackgroundColor(widgetUtil.color(R.color.buttonDisabled));
-        final String msg = textOf(R.id.changeMessageInput).trim();
-        new ObservableAsyncTask<Change, Void, String>() {
-          @Override
-          protected String doInBackground(Change... changes) {
-            Change change = changes[0];
-            try {
-              change.postReview(msg, selectedValues);
-              change.reload();
-              return null;
-            } catch (RestApiException e) {
-              if (e instanceof HttpStatusException) {
-                HttpStatusException se = (HttpStatusException) e;
-                return getString(R.string.review_error, se.getStatusCode(),
-                    se.getStatusText());
-              } else {
-                return e.getMessage();
-              }
-            }
-          }
-
-          @Override
-          protected void postExecute(String errorMsg) {
-            if (errorMsg != null) {
-              v.setEnabled(true);
-              v.setBackgroundColor(widgetUtil.color(R.color.button));
-              widgetUtil.showError(errorMsg);
-            } else {
-              display(SortChangesFragment.class);
-            }
-          }
-        }.execute(change);
-      }
-    });
-
-    v(R.id.postReviewAndSubmitButton).setOnClickListener(
-        new View.OnClickListener() {
-      @Override
-      public void onClick(final View v) {
-        v.setEnabled(false);
-        v.setBackgroundColor(widgetUtil.color(R.color.buttonDisabled));
-        final String msg = textOf(R.id.changeMessageInput).trim();
-        new ObservableAsyncTask<Change, Void, String>() {
-          @Override
-          protected String doInBackground(Change... changes) {
-            Change change = changes[0];
-            try {
-              change.postReview(msg, selectedValues);
-            } catch (RestApiException e) {
-              if (e instanceof HttpStatusException) {
-                HttpStatusException se = (HttpStatusException) e;
-                return getString(R.string.review_error, se.getStatusCode(),
-                    se.getStatusText());
-              } else {
-                return e.getMessage();
-              }
-            }
-
-            try {
-              change.submit();
-              change.reload();
-              return null;
-            } catch (RestApiException e) {
-              if (e instanceof HttpStatusException) {
-                HttpStatusException se = (HttpStatusException) e;
-                return getString(R.string.submit_error, se.getStatusCode(),
-                    se.getStatusText());
-              } else {
-                return e.getMessage();
-              }
-            }
-          }
-
-          @Override
-          protected void postExecute(String errorMsg) {
-            if (errorMsg != null) {
-              v.setEnabled(true);
-              v.setBackgroundColor(widgetUtil.color(R.color.button));
-              widgetUtil.showError(errorMsg);
-            } else {
-              display(SortChangesFragment.class);
-            }
-          }
-        }.execute(change);
-      }
-    });
-
-  }
-
-  private void initLabels(Change change, int codeReviewVote) {
-    selectedValues.put("Code-Review", codeReviewVote);
-
-    Map<String, Integer> votes = new HashMap<>();
-    votes.put("Code-Review", codeReviewVote);
-
-    ViewGroup voteInput = vg(R.id.voteInput);
-    VoteView voteView = new VoteView(getContext());
-    voteView.init(change, votes);
-    voteInput.addView(voteView);
-
-    voteView.addOnSelectListener(new VoteView.OnSelectListener() {
-      @Override
-      public void onSelect(String label, int vote) {
-        selectedValues.put(label, vote);
-        if ("Code-Review".equals(label)) {
-          update(vote);
-        }
-      }
-    });
-  }
-
-  private void update(int codeReviewVote) {
-    setGone(v(R.id.postReviewAndSubmitButton));
-
-    @DrawableRes int iconRes;
-    if (codeReviewVote <= -2) {
-      iconRes = R.drawable.ic_sentiment_very_dissatisfied_white_48dp;
-    } else if (codeReviewVote == -1) {
-      iconRes = R.drawable.ic_sentiment_dissatisfied_white_48dp;
-    } else if (codeReviewVote == 0) {
-      iconRes = R.drawable.ic_sentiment_neutral_white_48dp;
-    } else if (codeReviewVote == 1) {
-      iconRes = R.drawable.ic_sentiment_satisfied_white_48dp;
-    } else {
-      iconRes = R.drawable.ic_sentiment_very_satisfied_white_48dp;
-      setVisible(v(R.id.postReviewAndSubmitButton));
-    }
-    ((ImageView) v(R.id.emoticon)).setImageDrawable(
-        widgetUtil.getDrawable(iconRes));
+    ((PostReviewView)v(R.id.postReview)).init(getApp(), this, vote,
+        SortChangesFragment.class);
   }
 }
diff --git a/app/src/main/java/com/google/reviewit/widget/PostReviewView.java b/app/src/main/java/com/google/reviewit/widget/PostReviewView.java
new file mode 100644
index 0000000..1d62d19
--- /dev/null
+++ b/app/src/main/java/com/google/reviewit/widget/PostReviewView.java
@@ -0,0 +1,212 @@
+// 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.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.reviewit.BaseFragment;
+import com.google.reviewit.R;
+import com.google.reviewit.app.Change;
+import com.google.reviewit.app.ReviewItApp;
+import com.google.reviewit.util.ObservableAsyncTask;
+import com.google.reviewit.util.WidgetUtil;
+import com.urswolfer.gerrit.client.rest.http.HttpStatusException;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.google.reviewit.util.WidgetUtil.setGone;
+import static com.google.reviewit.util.WidgetUtil.setVisible;
+
+public class PostReviewView extends LinearLayout {
+  private final WidgetUtil widgetUtil;
+  private final Map<String, Integer> selectedValues = new HashMap<>();
+
+  public PostReviewView(Context context) {
+    this(context, null, 0);
+  }
+
+  public PostReviewView(Context context, AttributeSet attrs) {
+    this(context, attrs, 0);
+  }
+
+  public PostReviewView(Context context, AttributeSet attrs, int defStyle) {
+    super(context, attrs, defStyle);
+    this.widgetUtil = new WidgetUtil(getContext());
+    inflate(context, R.layout.post_review, this);
+  }
+
+  public void init(ReviewItApp app, BaseFragment origin, int vote,
+                   Class<? extends BaseFragment> target) {
+    update(vote);
+    Change change = app.getCurrentChange();
+
+    init(origin, change, target);
+    initLabels(change, vote);
+    ((ApprovalsView) findViewById(R.id.approvals)).displayApprovals(app,
+        change.info, origin);
+  }
+
+  private void init(
+      final BaseFragment origin, final Change change,
+      final Class<? extends BaseFragment> target) {
+    ((ExpandableCommitMessageView)findViewById(R.id.commitMessage))
+        .init(change);
+
+    findViewById(R.id.postReviewButton).setOnClickListener(
+        new View.OnClickListener() {
+          @Override
+          public void onClick(final View v) {
+            v.setEnabled(false);
+            v.setBackgroundColor(widgetUtil.color(R.color.buttonDisabled));
+            final String msg = ((TextView) findViewById(R.id.changeMessageInput))
+                .getText().toString().trim();
+            new ObservableAsyncTask<Change, Void, String>() {
+              @Override
+              protected String doInBackground(Change... changes) {
+                Change change = changes[0];
+                try {
+                  change.postReview(msg, selectedValues);
+                  change.reload();
+                  return null;
+                } catch (RestApiException e) {
+                  if (e instanceof HttpStatusException) {
+                    HttpStatusException se = (HttpStatusException) e;
+                    return origin.getString(R.string.review_error,
+                        se.getStatusCode(), se.getStatusText());
+                  } else {
+                    return e.getMessage();
+                  }
+                }
+              }
+
+              @Override
+              protected void postExecute(String errorMsg) {
+                if (errorMsg != null) {
+                  v.setEnabled(true);
+                  v.setBackgroundColor(widgetUtil.color(R.color.button));
+                  widgetUtil.showError(errorMsg);
+                } else {
+                  origin.display(target);
+                }
+              }
+            }.execute(change);
+          }
+        });
+
+    findViewById(R.id.postReviewAndSubmitButton).setOnClickListener(
+        new View.OnClickListener() {
+          @Override
+          public void onClick(final View v) {
+            v.setEnabled(false);
+            v.setBackgroundColor(widgetUtil.color(R.color.buttonDisabled));
+            final String msg = ((TextView) findViewById(R.id.changeMessageInput))
+                .getText().toString().trim();
+            new ObservableAsyncTask<Change, Void, String>() {
+              @Override
+              protected String doInBackground(Change... changes) {
+                Change change = changes[0];
+                try {
+                  change.postReview(msg, selectedValues);
+                } catch (RestApiException e) {
+                  if (e instanceof HttpStatusException) {
+                    HttpStatusException se = (HttpStatusException) e;
+                    return origin.getString(R.string.review_error,
+                        se.getStatusCode(), se.getStatusText());
+                  } else {
+                    return e.getMessage();
+                  }
+                }
+
+                try {
+                  change.submit();
+                  change.reload();
+                  return null;
+                } catch (RestApiException e) {
+                  if (e instanceof HttpStatusException) {
+                    HttpStatusException se = (HttpStatusException) e;
+                    return origin.getString(R.string.submit_error,
+                        se.getStatusCode(), se.getStatusText());
+                  } else {
+                    return e.getMessage();
+                  }
+                }
+              }
+
+              @Override
+              protected void postExecute(String errorMsg) {
+                if (errorMsg != null) {
+                  v.setEnabled(true);
+                  v.setBackgroundColor(widgetUtil.color(R.color.button));
+                  widgetUtil.showError(errorMsg);
+                } else {
+                  origin.display(target);
+                }
+              }
+            }.execute(change);
+          }
+        });
+  }
+
+  private void initLabels(Change change, int codeReviewVote) {
+    selectedValues.put("Code-Review", codeReviewVote);
+
+    Map<String, Integer> votes = new HashMap<>();
+    votes.put("Code-Review", codeReviewVote);
+
+    ViewGroup voteInput = (ViewGroup)findViewById(R.id.voteInput);
+    VoteView voteView = new VoteView(getContext());
+    voteView.init(change, votes);
+    voteInput.addView(voteView);
+
+    voteView.addOnSelectListener(new VoteView.OnSelectListener() {
+      @Override
+      public void onSelect(String label, int vote) {
+        selectedValues.put(label, vote);
+        if ("Code-Review".equals(label)) {
+          update(vote);
+        }
+      }
+    });
+  }
+
+  private void update(int codeReviewVote) {
+    setGone(findViewById(R.id.postReviewAndSubmitButton));
+
+    @DrawableRes int iconRes;
+    if (codeReviewVote <= -2) {
+      iconRes = R.drawable.ic_sentiment_very_dissatisfied_white_48dp;
+    } else if (codeReviewVote == -1) {
+      iconRes = R.drawable.ic_sentiment_dissatisfied_white_48dp;
+    } else if (codeReviewVote == 0) {
+      iconRes = R.drawable.ic_sentiment_neutral_white_48dp;
+    } else if (codeReviewVote == 1) {
+      iconRes = R.drawable.ic_sentiment_satisfied_white_48dp;
+    } else {
+      iconRes = R.drawable.ic_sentiment_very_satisfied_white_48dp;
+      setVisible(findViewById(R.id.postReviewAndSubmitButton));
+    }
+    ((ImageView) findViewById(R.id.emoticon)).setImageDrawable(
+        widgetUtil.getDrawable(iconRes));
+  }
+}
diff --git a/app/src/main/res/layout/content_post_review.xml b/app/src/main/res/layout/content_post_review.xml
index 6c2f411..da845ae 100644
--- a/app/src/main/res/layout/content_post_review.xml
+++ b/app/src/main/res/layout/content_post_review.xml
@@ -14,126 +14,13 @@
     See the License for the specific language governing permissions and
     limitations under the License. -->
 
-<LinearLayout
-  xmlns:android="http://schemas.android.com/apk/res/android"
-  xmlns:app="http://schemas.android.com/apk/res-auto"
-  android:layout_width="match_parent"
-  android:layout_height="match_parent"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical"
-  android:paddingBottom="@dimen/activity_vertical_margin"
-  app:layout_behavior="@string/appbar_scrolling_view_behavior">
+  android:layout_width="match_parent"
+  android:layout_height="match_parent">
 
-  <LinearLayout
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical"
-    android:layout_marginBottom="5dp"
-    android:background="@drawable/navigation_button_bar">
-
-    <include layout="@layout/progress"/>
-
-    <com.google.reviewit.widget.ExpandableCommitMessageView
-      android:id="@+id/commitMessage"
-      android:layout_width="match_parent"
-      android:layout_height="wrap_content"
-      android:paddingTop="5dp"/>
-
-    <LinearLayout
-      android:layout_width="match_parent"
-      android:layout_height="wrap_content"
-      android:orientation="horizontal">
-
-      <ImageView
-        android:id="@+id/emoticon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginLeft="7dp"
-        android:layout_marginTop="3dp"
-        android:clickable="true"/>
-
-      <View
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:layout_weight="1"/>
-
-      <Button
-        android:id="@+id/postReviewButton"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:background="@color/button"
-        android:textColor="@color/buttonFont"
-        android:layout_marginTop="5dp"
-        android:layout_marginBottom="5dp"
-        android:paddingLeft="5dp"
-        android:paddingRight="5dp"
-        android:text="@string/publish"/>
-
-      <View
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:layout_weight="1"/>
-
-      <Button
-        android:id="@+id/postReviewAndSubmitButton"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:background="@color/button"
-        android:textColor="@color/buttonFont"
-        android:layout_marginTop="5dp"
-        android:layout_marginBottom="5dp"
-        android:paddingLeft="5dp"
-        android:paddingRight="5dp"
-        android:text="@string/publishAndSubmit"/>
-
-      <View
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:layout_weight="1"/>
-    </LinearLayout>
-  </LinearLayout>
-
-  <ScrollView
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:paddingLeft="5dp"
-    android:paddingRight="5dp">
-
-    <LinearLayout
-      android:orientation="vertical"
-      android:layout_width="match_parent"
-      android:layout_height="wrap_content">
-
-      <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/message"
-        android:textSize="15dp"/>
-
-      <EditText
-        android:id="@+id/changeMessageInput"
-        android:inputType="textMultiLine"
-        android:lines="4"
-        android:gravity="top|left"
-        android:layout_height="wrap_content"
-        android:layout_width="match_parent"
-        android:scrollbars="vertical"/>
-
-      <LinearLayout
-        android:id="@+id/voteInput"
-        android:orientation="vertical"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="10dp"
-        android:layout_marginBottom="10dp">
-      </LinearLayout>
-
-      <com.google.reviewit.widget.ApprovalsView
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:app="http://schemas.android.com/apk/res-auto"
-        android:id="@+id/approvals"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content">
-      </com.google.reviewit.widget.ApprovalsView>
-    </LinearLayout>
-  </ScrollView>
+  <com.google.reviewit.widget.PostReviewView
+    android:id="@+id/postReview"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"/>
 </LinearLayout>
diff --git a/app/src/main/res/layout/post_review.xml b/app/src/main/res/layout/post_review.xml
new file mode 100644
index 0000000..6c2f411
--- /dev/null
+++ b/app/src/main/res/layout/post_review.xml
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- 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. -->
+
+<LinearLayout
+  xmlns:android="http://schemas.android.com/apk/res/android"
+  xmlns:app="http://schemas.android.com/apk/res-auto"
+  android:layout_width="match_parent"
+  android:layout_height="match_parent"
+  android:orientation="vertical"
+  android:paddingBottom="@dimen/activity_vertical_margin"
+  app:layout_behavior="@string/appbar_scrolling_view_behavior">
+
+  <LinearLayout
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:layout_marginBottom="5dp"
+    android:background="@drawable/navigation_button_bar">
+
+    <include layout="@layout/progress"/>
+
+    <com.google.reviewit.widget.ExpandableCommitMessageView
+      android:id="@+id/commitMessage"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:paddingTop="5dp"/>
+
+    <LinearLayout
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:orientation="horizontal">
+
+      <ImageView
+        android:id="@+id/emoticon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="7dp"
+        android:layout_marginTop="3dp"
+        android:clickable="true"/>
+
+      <View
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_weight="1"/>
+
+      <Button
+        android:id="@+id/postReviewButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="@color/button"
+        android:textColor="@color/buttonFont"
+        android:layout_marginTop="5dp"
+        android:layout_marginBottom="5dp"
+        android:paddingLeft="5dp"
+        android:paddingRight="5dp"
+        android:text="@string/publish"/>
+
+      <View
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_weight="1"/>
+
+      <Button
+        android:id="@+id/postReviewAndSubmitButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="@color/button"
+        android:textColor="@color/buttonFont"
+        android:layout_marginTop="5dp"
+        android:layout_marginBottom="5dp"
+        android:paddingLeft="5dp"
+        android:paddingRight="5dp"
+        android:text="@string/publishAndSubmit"/>
+
+      <View
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_weight="1"/>
+    </LinearLayout>
+  </LinearLayout>
+
+  <ScrollView
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingLeft="5dp"
+    android:paddingRight="5dp">
+
+    <LinearLayout
+      android:orientation="vertical"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content">
+
+      <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/message"
+        android:textSize="15dp"/>
+
+      <EditText
+        android:id="@+id/changeMessageInput"
+        android:inputType="textMultiLine"
+        android:lines="4"
+        android:gravity="top|left"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:scrollbars="vertical"/>
+
+      <LinearLayout
+        android:id="@+id/voteInput"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="10dp"
+        android:layout_marginBottom="10dp">
+      </LinearLayout>
+
+      <com.google.reviewit.widget.ApprovalsView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        android:id="@+id/approvals"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+      </com.google.reviewit.widget.ApprovalsView>
+    </LinearLayout>
+  </ScrollView>
+</LinearLayout>