Add uploader to change screen if different from change owner

Unlike owner, this widget is not a link, because we have no
corresponding 'uploader:' search operator, and going to a search for
'owner:<uploader>' may be confusing because the change you just came
from is not in the result list.

Change-Id: I0752b045d99945d7be0c3f0536d958fa7c5bbd98
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ChangeInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ChangeInfo.java
index 695c126..ed3d4ec 100644
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ChangeInfo.java
+++ b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ChangeInfo.java
@@ -296,6 +296,7 @@
     public final native int _number() /*-{ return this._number; }-*/;
     public final native String name() /*-{ return this.name; }-*/;
     public final native boolean draft() /*-{ return this.draft || false; }-*/;
+    public final native AccountInfo uploader() /*-{ return this.uploader; }-*/;
     public final native boolean isEdit() /*-{ return this._number == 0; }-*/;
     public final native CommitInfo commit() /*-{ return this.commit; }-*/;
     public final native void setCommit(CommitInfo c) /*-{ this.commit = c; }-*/;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.java
index b9ea8e0..abd242e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.java
@@ -28,6 +28,7 @@
 import com.google.gerrit.client.changes.StarredChanges;
 import com.google.gerrit.client.changes.Util;
 import com.google.gerrit.client.diff.DiffApi;
+import com.google.gerrit.client.info.AccountInfo;
 import com.google.gerrit.client.info.AccountInfo.AvatarInfo;
 import com.google.gerrit.client.info.ActionInfo;
 import com.google.gerrit.client.info.ChangeInfo;
@@ -66,6 +67,7 @@
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.NativeEvent;
 import com.google.gwt.dom.client.SelectElement;
+import com.google.gwt.dom.client.Style.Display;
 import com.google.gwt.event.dom.client.ChangeEvent;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
@@ -86,6 +88,7 @@
 import com.google.gwt.user.client.ui.FlowPanel;
 import com.google.gwt.user.client.ui.HTMLPanel;
 import com.google.gwt.user.client.ui.Image;
+import com.google.gwt.user.client.ui.InlineLabel;
 import com.google.gwt.user.client.ui.ListBox;
 import com.google.gwt.user.client.ui.Panel;
 import com.google.gwt.user.client.ui.SimplePanel;
@@ -163,6 +166,9 @@
   @UiField Element hashtagTableRow;
   @UiField FlowPanel ownerPanel;
   @UiField InlineHyperlink ownerLink;
+  @UiField Element uploaderRow;
+  @UiField FlowPanel uploaderPanel;
+  @UiField InlineLabel uploaderName;
   @UiField Element statusText;
   @UiField Image projectSettings;
   @UiField AnchorElement projectSettingsLink;
@@ -1146,6 +1152,7 @@
     labels.set(info);
 
     renderOwner(info);
+    renderUploader(info, revision);
     renderActionTextDate(info);
     renderDiffBaseListBox(info);
     initReplyButton(info, revision);
@@ -1231,17 +1238,12 @@
 
   private void renderOwner(ChangeInfo info) {
     // TODO info card hover
-    String name = info.owner().name() != null
-        ? info.owner().name()
-        : Gerrit.info().user().anonymousCowardName();
-
+    String name = name(info.owner());
     if (info.owner().avatar(AvatarInfo.DEFAULT_SIZE) != null) {
       ownerPanel.insert(new AvatarImage(info.owner()), 0);
     }
     ownerLink.setText(name);
-    ownerLink.setTitle(info.owner().email() != null
-        ? info.owner().email()
-        : name);
+    ownerLink.setTitle(email(info.owner(), name));
     ownerLink.setTargetHistoryToken(PageLinks.toAccountQuery(
         info.owner().name() != null
         ? info.owner().name()
@@ -1250,6 +1252,32 @@
         : String.valueOf(info.owner()._accountId()), Change.Status.NEW));
   }
 
+  private void renderUploader(ChangeInfo info, String revision) {
+    AccountInfo uploader = info.revision(revision).uploader();
+    if (uploader._accountId() == info.owner()._accountId()) {
+      uploaderRow.getStyle().setDisplay(Display.NONE);
+      return;
+    }
+    uploaderRow.getStyle().setDisplay(Display.TABLE_ROW);
+
+    if (uploader.avatar(AvatarInfo.DEFAULT_SIZE) != null) {
+      uploaderPanel.insert(new AvatarImage(uploader), 0);
+    }
+    String name = name(uploader);
+    uploaderName.setText(name);
+    uploaderName.setTitle(email(uploader, name));
+  }
+
+  private static String name(AccountInfo info) {
+    return info.name() != null
+        ? info.name()
+        : Gerrit.info().user().anonymousCowardName();
+  }
+
+  private static String email(AccountInfo info, String name) {
+    return info.email() != null ? info.email() : name;
+  }
+
   private void renderSubmitType(String action) {
     try {
       SubmitType type = SubmitType.valueOf(action);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.ui.xml
index 06ae4ca..e40b431 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.ui.xml
@@ -315,7 +315,7 @@
       background-color: trimColor;
     }
 
-    .ownerPanel img {
+    .ownerPanel img, .uploaderPanel img {
       margin: 0 2px 0 0;
       width: 16px;
       height: 16px !important;
@@ -421,6 +421,14 @@
                 </g:FlowPanel>
               </td>
             </tr>
+            <tr ui:field='uploaderRow'>
+              <th><ui:msg>Uploader</ui:msg></th>
+              <td>
+                <g:FlowPanel ui:field='uploaderPanel' styleName='{style.uploaderPanel}'>
+                  <g:InlineLabel ui:field='uploaderName'/>
+                </g:FlowPanel>
+              </td>
+            </tr>
             <tr>
               <th><ui:msg>Reviewers</ui:msg></th>
               <td>