Implement change list for reviewing changes This is a first step to provide a change list from which the user can select changes for review. This change only implements a basic version of the change list that is not fully implemented yet: - Selecting a change does not trigger any action yet. - It only shows the first page of changes, it is not possible yet to load further pages. - The entry for a change does not yet display all information that we would like to show here (e.g. current votes are missing). Change-Id: I36bf7b3737240b8434475590c13c47e0afa01a3a Signed-off-by: Edwin Kempin <ekempin@google.com>
diff --git a/app/src/main/java/com/google/reviewit/AbandonFragment.java b/app/src/main/java/com/google/reviewit/AbandonFragment.java index 4233411..dcc2137 100644 --- a/app/src/main/java/com/google/reviewit/AbandonFragment.java +++ b/app/src/main/java/com/google/reviewit/AbandonFragment.java
@@ -50,7 +50,7 @@ public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - Change change = getApp().getActionHandler().getCurrentChange(); + Change change = getApp().getSortActionHandler().getCurrentChange(); setTitle(getString(R.string.abandon_change_title, change.info._number)); init(change); }
diff --git a/app/src/main/java/com/google/reviewit/AddReviewerFragment.java b/app/src/main/java/com/google/reviewit/AddReviewerFragment.java index c53071c..7e25d7f 100644 --- a/app/src/main/java/com/google/reviewit/AddReviewerFragment.java +++ b/app/src/main/java/com/google/reviewit/AddReviewerFragment.java
@@ -77,7 +77,7 @@ public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - Change change = getApp().getActionHandler().getCurrentChange(); + Change change = getApp().getSortActionHandler().getCurrentChange(); initInputField(change); try { displayReviewers(change);
diff --git a/app/src/main/java/com/google/reviewit/BaseFragment.java b/app/src/main/java/com/google/reviewit/BaseFragment.java index 9babd93..e64d33a 100644 --- a/app/src/main/java/com/google/reviewit/BaseFragment.java +++ b/app/src/main/java/com/google/reviewit/BaseFragment.java
@@ -31,7 +31,7 @@ import android.view.inputmethod.InputMethodManager; import android.widget.TextView; -import com.google.reviewit.app.ActionHandler; +import com.google.reviewit.app.SortActionHandler; import com.google.reviewit.app.ReviewItApp; import com.google.reviewit.util.TaskObserver; import com.google.reviewit.util.WidgetUtil; @@ -121,8 +121,8 @@ return ((ReviewItApp) getActivity().getApplication()); } - protected ActionHandler getActionHandler() { - return getApp().getActionHandler(); + protected SortActionHandler getSortActionHandler() { + return getApp().getSortActionHandler(); } public void display(
diff --git a/app/src/main/java/com/google/reviewit/DetailedChangeFragment.java b/app/src/main/java/com/google/reviewit/DetailedChangeFragment.java index f99d812..2991696 100644 --- a/app/src/main/java/com/google/reviewit/DetailedChangeFragment.java +++ b/app/src/main/java/com/google/reviewit/DetailedChangeFragment.java
@@ -31,7 +31,7 @@ import com.google.gerrit.extensions.client.ChangeStatus; import com.google.gerrit.extensions.common.FileInfo; -import com.google.reviewit.app.ActionHandler; +import com.google.reviewit.app.SortActionHandler; import com.google.reviewit.app.Change; import com.google.reviewit.util.ChangeUtil; import com.google.reviewit.widget.ChangeBox; @@ -85,7 +85,7 @@ public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - Change change = getApp().getActionHandler().getCurrentChange(); + Change change = getApp().getSortActionHandler().getCurrentChange(); setTitle(getString(R.string.detailed_change_title, change.info._number)); setHasOptionsMenu(true); init(); @@ -217,7 +217,7 @@ @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - ActionHandler actionHandler = getApp().getActionHandler(); + SortActionHandler actionHandler = getApp().getSortActionHandler(); inflater.inflate(R.menu.menu_detailed_change, menu); for (int i = 0; i < menu.size(); i++) { MenuItem item = menu.getItem(i); @@ -237,7 +237,7 @@ @Override public boolean onOptionsItemSelected(MenuItem item) { - ActionHandler actionHandler = getApp().getActionHandler(); + SortActionHandler actionHandler = getApp().getSortActionHandler(); switch (item.getItemId()) { case R.id.action_add_reviewer: display(AddReviewerFragment.create(getClass()));
diff --git a/app/src/main/java/com/google/reviewit/PostReviewFragment.java b/app/src/main/java/com/google/reviewit/PostReviewFragment.java index b887e48..bb431db 100644 --- a/app/src/main/java/com/google/reviewit/PostReviewFragment.java +++ b/app/src/main/java/com/google/reviewit/PostReviewFragment.java
@@ -71,7 +71,7 @@ int vote = getArguments().getInt(VOTE); update(vote); - Change change = getApp().getActionHandler().getCurrentChange(); + Change change = getApp().getSortActionHandler().getCurrentChange(); setTitle(getString(R.string.detailed_change_title, change.info._number)); init(change);
diff --git a/app/src/main/java/com/google/reviewit/SortSettingFragment.java b/app/src/main/java/com/google/reviewit/QuerySettingsFragment.java similarity index 97% rename from app/src/main/java/com/google/reviewit/SortSettingFragment.java rename to app/src/main/java/com/google/reviewit/QuerySettingsFragment.java index 5efb057..5ab3f1a 100644 --- a/app/src/main/java/com/google/reviewit/SortSettingFragment.java +++ b/app/src/main/java/com/google/reviewit/QuerySettingsFragment.java
@@ -25,12 +25,12 @@ import com.google.reviewit.app.ServerConfig; import com.google.reviewit.util.WidgetUtil; -public class SortSettingFragment extends BaseFragment { +public class QuerySettingsFragment extends BaseFragment { private static final String LABEL_REGEXP = "^[a-zA-Z][a-zA-Z0-9]*$"; @Override protected @LayoutRes int getLayout() { - return R.layout.content_sort_settings; + return R.layout.content_query_settings; } @Override
diff --git a/app/src/main/java/com/google/reviewit/RestoreFragment.java b/app/src/main/java/com/google/reviewit/RestoreFragment.java index 5ec9622..45d398a 100644 --- a/app/src/main/java/com/google/reviewit/RestoreFragment.java +++ b/app/src/main/java/com/google/reviewit/RestoreFragment.java
@@ -50,7 +50,7 @@ public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - Change change = getApp().getActionHandler().getCurrentChange(); + Change change = getApp().getSortActionHandler().getCurrentChange(); setTitle(getString(R.string.restore_change_title, change.info._number)); init(change); }
diff --git a/app/src/main/java/com/google/reviewit/ReviewChangesFragment.java b/app/src/main/java/com/google/reviewit/ReviewChangesFragment.java index b38f651..5c68c1e 100644 --- a/app/src/main/java/com/google/reviewit/ReviewChangesFragment.java +++ b/app/src/main/java/com/google/reviewit/ReviewChangesFragment.java
@@ -14,12 +14,184 @@ package com.google.reviewit; +import android.os.Bundle; import android.support.annotation.LayoutRes; +import android.util.Log; +import android.util.TypedValue; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.google.gerrit.extensions.restapi.RestApiException; +import com.google.reviewit.app.Change; +import com.google.reviewit.app.QueryHandler; +import com.google.reviewit.util.ObservableAsynTask; +import com.google.reviewit.util.TaskObserver; +import com.google.reviewit.widget.ChangeEntry; + +import java.util.Collections; +import java.util.List; + +import static com.google.reviewit.util.LayoutUtil.matchAndFixedLayout; +import static com.google.reviewit.util.WidgetUtil.setGone; +import static com.google.reviewit.util.WidgetUtil.setInvisible; +import static com.google.reviewit.util.WidgetUtil.setVisible; public class ReviewChangesFragment extends BaseFragment { + private static final String TAG = ReviewChangesFragment.class.getName(); @Override protected @LayoutRes int getLayout() { return R.layout.content_review_changes; } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + setHasOptionsMenu(true); + + TaskObserver.enableProgressBar(getWindow()); + init(); + display(); + } + + private void init() { + v(R.id.reloadButton).setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + reloadQuery(); + } + }); + + // TODO detect when scrolled to the end and automatically load next page + } + + private void display() { + if (!isOnline()) { + setInvisible(v(R.id.progress)); + setGone(v(R.id.initialProgress)); + setVisible(v(R.id.statusText, R.id.reloadButton)); + TextView statusText = tv(R.id.statusText); + statusText.setText(getString(R.string.no_network)); + statusText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15); + return; + } + + new ObservableAsynTask<Void, Void, ChangeListData>() { + private View progress; + private View initialProgress; + private View reloadButton; + private TextView statusText; + private ViewGroup changeList; + + @Override + protected void preExecute() { + super.preExecute(); + progress = v(R.id.progress); + initialProgress = v(R.id.initialProgress); + reloadButton = v(R.id.reloadButton); + statusText = tv(R.id.statusText); + changeList = vg(R.id.changeList); + } + + @Override + protected ChangeListData doInBackground(Void... v) { + try { + QueryHandler queryHandler = getApp().getQueryHandler(); + if (queryHandler.hasNext()) { + return new ChangeListData(queryHandler.next()); + } else { + return new ChangeListData(Collections.<Change>emptyList()); + } + } catch (RestApiException e) { + // e.g. server not reachable + Log.e(TAG, "Request failed", e); + if (e.getCause() != null) { + return new ChangeListData(getString(R.string.error_with_cause, + e.getMessage(), e.getCause().getMessage())); + } else { + return new ChangeListData(e.getMessage()); + } + } + } + + protected void postExecute(ChangeListData changeListData) { + super.postExecute(changeListData); + + if (getActivity() == null) { + // user navigated away while we were waiting for the request + return; + } + + getActivity().invalidateOptionsMenu(); + setInvisible(progress); + setGone(initialProgress, reloadButton); + + if (changeListData.error != null) { + statusText.setText(changeListData.error); + return; + } + + if (!changeListData.changeList.isEmpty()) { + setGone(statusText); + for (Change change : changeListData.changeList) { + ChangeEntry changeEntry = new ChangeEntry(getContext()); + changeEntry.init(getApp(), change); + changeList.addView(changeEntry); + addSeparator(changeList); + } + } else { + statusText.setText(getString(R.string.no_changes_match)); + } + } + }.execute(); + } + + private void addSeparator(ViewGroup viewGroup) { + View separator = new View(getContext()); + separator.setLayoutParams( + matchAndFixedLayout(widgetUtil.dpToPx(1))); + separator.setBackgroundColor(widgetUtil.color(R.color.separator)); + viewGroup.addView(separator); + } + + private void reloadQuery() { + getApp().getQueryHandler().reset(); + display(getClass(), false); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.menu_review_changes, menu); + super.onCreateOptionsMenu(menu, inflater); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_reload_query: + reloadQuery(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + private static class ChangeListData { + final List<Change> changeList; + final String error; + + ChangeListData(List<Change> changeList) { + this.changeList = changeList; + this.error = null; + } + + ChangeListData(String error) { + this.changeList = null; + this.error = error; + } + } }
diff --git a/app/src/main/java/com/google/reviewit/SettingsFragment.java b/app/src/main/java/com/google/reviewit/SettingsFragment.java index c81210e..67f0c0f 100644 --- a/app/src/main/java/com/google/reviewit/SettingsFragment.java +++ b/app/src/main/java/com/google/reviewit/SettingsFragment.java
@@ -41,10 +41,10 @@ } }); - v(R.id.sortConfig).setOnClickListener(new View.OnClickListener() { + v(R.id.queryConfig).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - display(SortSettingFragment.class); + display(QuerySettingsFragment.class); } }); }
diff --git a/app/src/main/java/com/google/reviewit/SortChangesFragment.java b/app/src/main/java/com/google/reviewit/SortChangesFragment.java index df116a0..2969891 100644 --- a/app/src/main/java/com/google/reviewit/SortChangesFragment.java +++ b/app/src/main/java/com/google/reviewit/SortChangesFragment.java
@@ -37,7 +37,7 @@ import com.google.gerrit.extensions.client.ChangeStatus; import com.google.gerrit.extensions.restapi.RestApiException; -import com.google.reviewit.app.ActionHandler; +import com.google.reviewit.app.SortActionHandler; import com.google.reviewit.app.Change; import com.google.reviewit.app.ConfigManager; import com.google.reviewit.app.QueryConfig; @@ -74,8 +74,8 @@ setHasOptionsMenu(true); - if (getActionHandler().hasCurrentChange()) { - getActionHandler().pushBack(); + if (getSortActionHandler().hasCurrentChange()) { + getSortActionHandler().pushBack(); } TaskObserver.enableProgressBar(getWindow()); @@ -92,7 +92,7 @@ ConfigManager cfgManager = getApp().getConfigManager(); QueryConfig config = cfgManager.getQueryConfig(); if (!config.isComplete()) { - display(SortSettingFragment.class); + display(QuerySettingsFragment.class); return; } @@ -105,7 +105,7 @@ return; } - if (getActionHandler().isQueryNeeded()) { + if (getSortActionHandler().isQueryNeeded()) { if (v(R.id.changeBox) == null) { setVisible(v(R.id.loadingBox)); setInvisible(v(R.id.progress)); @@ -115,7 +115,7 @@ setVisible(v(R.id.progress)); } } else { - Change change = getActionHandler().preview(); + Change change = getSortActionHandler().preview(); if (change != null) { ChangeUtil.colorBackground(root, change); } @@ -144,7 +144,7 @@ @Override protected ChangeData doInBackground(Void... v) { try { - ActionHandler actionHandler = getActionHandler(); + SortActionHandler actionHandler = getSortActionHandler(); if (actionHandler.hasNext()) { Change change = actionHandler.next(); int queueSize = actionHandler.getQueueSize(); @@ -235,16 +235,16 @@ v(R.id.skipButton).setOnClickListener(new View.OnClickListener() { public void onClick(View v) { disableButtons(); - getActionHandler().skip(); - animate(changeBox, ActionHandler.Action.SKIP); + getSortActionHandler().skip(); + animate(changeBox, SortActionHandler.Action.SKIP); } }); View.OnClickListener onStarClickListener = new View.OnClickListener() { public void onClick(View v) { disableButtons(); - getActionHandler().star(); - animate(changeBox, ActionHandler.Action.STAR); + getSortActionHandler().star(); + animate(changeBox, SortActionHandler.Action.STAR); } }; v(R.id.starButton).setOnClickListener(onStarClickListener); @@ -253,8 +253,8 @@ View.OnClickListener onIgnoreClickListener = new View.OnClickListener() { public void onClick(View v) { disableButtons(); - getActionHandler().ignore(); - animate(changeBox, ActionHandler.Action.IGNORE); + getSortActionHandler().ignore(); + animate(changeBox, SortActionHandler.Action.IGNORE); } }; v(R.id.ignoreButton).setOnClickListener(onIgnoreClickListener); @@ -304,7 +304,7 @@ final ViewGroup resultBox = vg(R.id.resultBox); changeBox.findViewById(R.id.changeBoxUpperPart).setOnTouchListener( new View.OnTouchListener() { - private ActionHandler.Action action = ActionHandler.Action.NONE; + private SortActionHandler.Action action = SortActionHandler.Action.NONE; private int x; private int y; @@ -330,9 +330,9 @@ if (eventX > (screenCenter + (screenCenter / 2))) { ((GradientDrawable) changeBox.getBackground()) .setColor(widgetUtil.color(R.color.commitMessageStar)); - action = ActionHandler.Action.STAR; + action = SortActionHandler.Action.STAR; } else { - action = ActionHandler.Action.NONE; + action = SortActionHandler.Action.NONE; ((GradientDrawable) changeBox.getBackground()) .setColor(widgetUtil.color(R.color.commitMessage)); } @@ -342,9 +342,9 @@ if (eventX < (screenCenter / 2)) { ((GradientDrawable) changeBox.getBackground()) .setColor(widgetUtil.color(R.color.commitMessageIgnore)); - action = ActionHandler.Action.IGNORE; + action = SortActionHandler.Action.IGNORE; } else { - action = ActionHandler.Action.NONE; + action = SortActionHandler.Action.NONE; ((GradientDrawable) changeBox.getBackground()) .setColor(widgetUtil.color(R.color.commitMessage)); } @@ -355,17 +355,17 @@ .setColor(widgetUtil.color(R.color.commitMessage)); switch (action) { case STAR: - getActionHandler().star(); + getSortActionHandler().star(); resultBox.removeView(changeBox); display(); break; case IGNORE: - getActionHandler().ignore(); + getSortActionHandler().ignore(); resultBox.removeView(changeBox); display(); break; case SKIP: - getActionHandler().skip(); + getSortActionHandler().skip(); resultBox.removeView(changeBox); display(); break; @@ -388,7 +388,7 @@ } private void animate( - final View changeBox, final ActionHandler.Action action) { + final View changeBox, final SortActionHandler.Action action) { final ViewGroup resultBox = vg(R.id.resultBox); Point screenSize = getScreenSize(); final int screenCenter = screenSize.x / 2; @@ -517,7 +517,7 @@ @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - ActionHandler actionHandler = getActionHandler(); + SortActionHandler actionHandler = getSortActionHandler(); inflater.inflate(R.menu.menu_sort_changes, menu); for (int i = 0; i < menu.size(); i++) { MenuItem item = menu.getItem(i); @@ -574,7 +574,7 @@ } private void undo() { - ActionHandler actionHandler = getActionHandler(); + SortActionHandler actionHandler = getSortActionHandler(); if (!actionHandler.undoPossible()) { return; } @@ -615,17 +615,17 @@ display(SortChangesFragment.class); } } - }.execute(getActionHandler().getCurrentChange()); + }.execute(getSortActionHandler().getCurrentChange()); } private void reloadQuery() { - getActionHandler().reset(); + getSortActionHandler().reset(); display(getClass(), false); } @Override public boolean onBackPressed() { - if (getActionHandler().undoPossible()) { + if (getSortActionHandler().undoPossible()) { undo(); return true; }
diff --git a/app/src/main/java/com/google/reviewit/UnifiedDiffFragment.java b/app/src/main/java/com/google/reviewit/UnifiedDiffFragment.java index 4db29a2..4a31052 100644 --- a/app/src/main/java/com/google/reviewit/UnifiedDiffFragment.java +++ b/app/src/main/java/com/google/reviewit/UnifiedDiffFragment.java
@@ -84,7 +84,7 @@ ? getArguments().getString(PATH) : null; - Change change = getApp().getActionHandler().getCurrentChange(); + Change change = getApp().getSortActionHandler().getCurrentChange(); checkState(change != null, "Change not set"); Map<String, FileInfo> files = change.currentRevision().files; root = (ScrollWithHeadingsView) v(R.id.unifiedDiffRoot);
diff --git a/app/src/main/java/com/google/reviewit/app/QueryConfig.java b/app/src/main/java/com/google/reviewit/app/QueryConfig.java index ea87e08..4759c9d 100644 --- a/app/src/main/java/com/google/reviewit/app/QueryConfig.java +++ b/app/src/main/java/com/google/reviewit/app/QueryConfig.java
@@ -16,6 +16,9 @@ import com.google.common.base.Strings; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + public class QueryConfig { public final String serverId; public final String query; @@ -27,6 +30,15 @@ this.label = label; } + public String encodedQuery() { + try { + // TODO use StandardCharsets.UTF_8.name() with API level 19 + return URLEncoder.encode(query, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException(e); + } + } + public boolean isComplete() { return !Strings.isNullOrEmpty(serverId) && !Strings.isNullOrEmpty(query)
diff --git a/app/src/main/java/com/google/reviewit/app/QueryHandler.java b/app/src/main/java/com/google/reviewit/app/QueryHandler.java new file mode 100644 index 0000000..bdaa6f3 --- /dev/null +++ b/app/src/main/java/com/google/reviewit/app/QueryHandler.java
@@ -0,0 +1,110 @@ +// 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.app; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; +import com.google.gerrit.extensions.client.ListChangesOption; +import com.google.gerrit.extensions.common.ChangeInfo; +import com.google.gerrit.extensions.restapi.RestApiException; + +import java.util.LinkedList; +import java.util.List; + +public class QueryHandler { + /** + * Number of changes that should be fetched at once from the server. + */ + private static final int LIMIT_QUERY = 25; + + private final Gerrit gerrit; + + private final LinkedList<Change> result = new LinkedList<>(); + + private QueryConfig config; + + /** + * Number of changes which should be skipped when querying the next changes + * from the server. + */ + private int start = 0; + + private int page = 0; + + /** + * Whether there are more changes at the server. + */ + private boolean more = true; + + QueryHandler(ConfigManager cfgManager, Gerrit gerrit) { + this.gerrit = gerrit; + this.config = cfgManager.getQueryConfig(); + + cfgManager.addUpdateListener(new ConfigManager.OnUpdate() { + @Override + public void onUpdate(QueryConfig cfg) { + config = cfg; + reset(); + } + }); + } + + public List<Change> next() throws RestApiException { + query(); + return result.subList(page * LIMIT_QUERY, (page + 1) * LIMIT_QUERY); + } + + public boolean hasNext() { + return more; + } + + private void query() throws RestApiException { + result.addAll(Collections2.transform(Collections2.filter( + gerrit.api() + .changes() + .query(config.encodedQuery()) + .withOption(ListChangesOption.ALL_FILES) + .withOption(ListChangesOption.CURRENT_COMMIT) + .withOption(ListChangesOption.CURRENT_REVISION) + .withOption(ListChangesOption.DETAILED_ACCOUNTS) + .withOption(ListChangesOption.DETAILED_LABELS) + .withLimit(LIMIT_QUERY) + .withStart(start) + .get(), new Predicate<ChangeInfo>() { + @Override + public boolean apply(ChangeInfo changeInfo) { + // filter out changes with no revisions + return changeInfo.revisions != null; + } + }), new Function<ChangeInfo, Change>() { + @Override + public Change apply(ChangeInfo changeInfo) { + return new Change(gerrit.api(), changeInfo); + } + })); + start += LIMIT_QUERY; + more = !result.isEmpty() && (result.getLast().info._moreChanges != null + ? result.getLast().info._moreChanges + : false); + } + + public void reset() { + result.clear(); + start = 0; + more = true; + page = 0; + } +}
diff --git a/app/src/main/java/com/google/reviewit/app/ReviewItApp.java b/app/src/main/java/com/google/reviewit/app/ReviewItApp.java index 10fb519..b2efb88 100644 --- a/app/src/main/java/com/google/reviewit/app/ReviewItApp.java +++ b/app/src/main/java/com/google/reviewit/app/ReviewItApp.java
@@ -32,7 +32,8 @@ public class ReviewItApp extends Application { private static final String TAG = ReviewItApp.class.getName(); - private ActionHandler actionHandler; + private QueryHandler queryHandler; + private SortActionHandler sortActionHandler; private AvatarCache avatarCache; private ConfigManager cfgManager; private ExecutorService executor; @@ -94,11 +95,18 @@ super.onCreate(); } - public ActionHandler getActionHandler() { - if (actionHandler == null) { - actionHandler = new ActionHandler(getConfigManager(), getGerrit()); + public QueryHandler getQueryHandler() { + if (queryHandler == null) { + queryHandler = new QueryHandler(getConfigManager(), getGerrit()); } - return actionHandler; + return queryHandler; + } + + public SortActionHandler getSortActionHandler() { + if (sortActionHandler == null) { + sortActionHandler = new SortActionHandler(getConfigManager(), getGerrit()); + } + return sortActionHandler; } public AvatarCache getAvatarCache() {
diff --git a/app/src/main/java/com/google/reviewit/app/ActionHandler.java b/app/src/main/java/com/google/reviewit/app/SortActionHandler.java similarity index 96% rename from app/src/main/java/com/google/reviewit/app/ActionHandler.java rename to app/src/main/java/com/google/reviewit/app/SortActionHandler.java index c182ea9..2a95b1e 100644 --- a/app/src/main/java/com/google/reviewit/app/ActionHandler.java +++ b/app/src/main/java/com/google/reviewit/app/SortActionHandler.java
@@ -23,7 +23,7 @@ import java.util.LinkedList; -public class ActionHandler { +public class SortActionHandler { public enum Action { /** * Do nothing. @@ -90,7 +90,7 @@ */ private boolean more = true; - ActionHandler(ConfigManager cfgManager, Gerrit gerrit) { + SortActionHandler(ConfigManager cfgManager, Gerrit gerrit) { this.gerrit = gerrit; this.config = cfgManager.getQueryConfig(); @@ -147,7 +147,7 @@ toProcess.addAll(Collections2.transform(Collections2.filter( gerrit.api() .changes() - .query(encodeQuery(config.query)) + .query(config.encodedQuery()) .withOption(ListChangesOption.ALL_FILES) .withOption(ListChangesOption.CURRENT_COMMIT) .withOption(ListChangesOption.CURRENT_REVISION) @@ -179,10 +179,6 @@ return toProcess.size() < THRESHOLD_QUEUE && more; } - private static String encodeQuery(String query) { - return query.replaceAll(" ", "+"); - } - /** * Stars the current change. */
diff --git a/app/src/main/java/com/google/reviewit/util/LayoutUtil.java b/app/src/main/java/com/google/reviewit/util/LayoutUtil.java index 907ea6c..ff19c94 100644 --- a/app/src/main/java/com/google/reviewit/util/LayoutUtil.java +++ b/app/src/main/java/com/google/reviewit/util/LayoutUtil.java
@@ -34,6 +34,11 @@ ViewGroup.LayoutParams.WRAP_CONTENT); } + public static ViewGroup.LayoutParams matchAndFixedLayout(int height) { + return new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, height); + } + public static TableLayout.LayoutParams matchAndWrapTableLayout() { return new TableLayout.LayoutParams( TableLayout.LayoutParams.MATCH_PARENT,
diff --git a/app/src/main/java/com/google/reviewit/widget/ChangeEntry.java b/app/src/main/java/com/google/reviewit/widget/ChangeEntry.java new file mode 100644 index 0000000..cfd5e37 --- /dev/null +++ b/app/src/main/java/com/google/reviewit/widget/ChangeEntry.java
@@ -0,0 +1,50 @@ +// 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.util.AttributeSet; +import android.widget.LinearLayout; + +import com.google.gerrit.extensions.common.ChangeInfo; +import com.google.reviewit.R; +import com.google.reviewit.app.Change; +import com.google.reviewit.app.ReviewItApp; +import com.google.reviewit.util.WidgetUtil; + +public class ChangeEntry extends LinearLayout { + public ChangeEntry(Context context) { + this(context, null, 0); + } + + public ChangeEntry(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ChangeEntry(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + inflate(context, R.layout.change_entry, this); + } + + public void init(ReviewItApp app, Change change) { + ((ProjectBranchTopicAgeView)findViewById(R.id.projectBranchTopicAge)) + .init(change); + ((UserView)findViewById(R.id.owner)).init(app, change.info.owner); + + ChangeInfo info = change.info; + WidgetUtil.setText(findViewById(R.id.subject), info.subject); + } +}
diff --git a/app/src/main/res/layout/change_entry.xml b/app/src/main/res/layout/change_entry.xml new file mode 100644 index 0000000..411470d --- /dev/null +++ b/app/src/main/res/layout/change_entry.xml
@@ -0,0 +1,51 @@ +<?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:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingLeft="3dp" + android:paddingTop="3dp" + android:paddingRight="3dp" + android:paddingBottom="3dp"> + + <LinearLayout + android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1"> + + <com.google.reviewit.widget.ProjectBranchTopicAgeView + android:id="@+id/projectBranchTopicAge" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> + + <TextView + android:id="@+id/subject" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + <com.google.reviewit.widget.UserView + android:id="@+id/owner" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:textSize="10sp" + app:avatarSize="15dp"/> + </LinearLayout> + +</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/content_sort_settings.xml b/app/src/main/res/layout/content_query_settings.xml similarity index 100% rename from app/src/main/res/layout/content_sort_settings.xml rename to app/src/main/res/layout/content_query_settings.xml
diff --git a/app/src/main/res/layout/content_review_changes.xml b/app/src/main/res/layout/content_review_changes.xml index 456ce76..3851a97 100644 --- a/app/src/main/res/layout/content_review_changes.xml +++ b/app/src/main/res/layout/content_review_changes.xml
@@ -15,14 +15,47 @@ limitations under the License. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent"> + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> - <TextView + <include layout="@layout/progress"/> + + <ScrollView android:layout_width="match_parent" - android:layout_height="match_parent" - android:gravity="center" - android:textSize="40sp" - android:text="TODO"/> -</LinearLayout> \ No newline at end of file + android:layout_height="match_parent"> + + <LinearLayout + android:id="@+id/changeList" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:id="@+id/statusText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_horizontal" + android:layout_marginTop="15dp" + android:textSize="24sp" + android:text="@string/loading"/> + + <ProgressBar + android:id="@+id/initialProgress" + android:layout_marginTop="20dp" + android:layout_width="match_parent" + android:layout_height="150dp" + android:indeterminate="true"/> + + <ImageView + android:id="@+id/reloadButton" + android:layout_width="match_parent" + android:layout_height="150dp" + android:layout_marginTop="20dp" + android:gravity="center_horizontal" + android:clickable="true" + android:src="@drawable/ic_refresh_black_48dp" + android:visibility="gone"/> + </LinearLayout> + </ScrollView> +</LinearLayout>
diff --git a/app/src/main/res/layout/content_settings.xml b/app/src/main/res/layout/content_settings.xml index 41bd70a..78eecf8 100644 --- a/app/src/main/res/layout/content_settings.xml +++ b/app/src/main/res/layout/content_settings.xml
@@ -49,10 +49,10 @@ android:layout_marginBottom="10dp"/> <TextView - android:id="@+id/sortConfig" + android:id="@+id/queryConfig" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="@string/sortConfig" + android:text="@string/queryConfig" android:gravity="center_horizontal" android:textSize="20sp"/> </LinearLayout>
diff --git a/app/src/main/res/menu/menu_review_changes.xml b/app/src/main/res/menu/menu_review_changes.xml new file mode 100644 index 0000000..af11e52 --- /dev/null +++ b/app/src/main/res/menu/menu_review_changes.xml
@@ -0,0 +1,22 @@ +<!-- 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. --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + <item + android:id="@+id/action_reload_query" + android:orderInCategory="100" + android:title="@string/action_reload_query" + app:showAsAction="never"/> +</menu> \ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 68e7525..c4de3bc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml
@@ -40,6 +40,9 @@ <string name="action_restore">Restore...</string> <string name="action_undo">Undo</string> + <!-- ReviewChangesFragment --> + <string name="no_changes_match">No changes match the query!</string> + <!-- ErrorFragment --> <string name="title_activity_error">Error</string> @@ -131,7 +134,7 @@ <!-- SettingsFragment --> <string name="serverConfig">Server Configuration</string> - <string name="sortConfig">Sort Configuration</string> + <string name="queryConfig">Query Configuration</string> <string name="add_server">Add Server</string> <string name="connection_failed">Connection failed: %1$s</string>