Commit 0dc7f21c authored by rahadi's avatar rahadi

Added tree view on dependencies resolving

parent d03bb2de
...@@ -42,6 +42,7 @@ import id.ac.stis.capi.R; ...@@ -42,6 +42,7 @@ import id.ac.stis.capi.R;
import id.ac.stis.capi.collectiva.preferences.CapiInstancePreferences; import id.ac.stis.capi.collectiva.preferences.CapiInstancePreferences;
import id.ac.stis.capi.lessthink.binders.NodeViewFactory; import id.ac.stis.capi.lessthink.binders.NodeViewFactory;
import id.ac.stis.capi.lessthink.listeners.DependenciesResolverListener; import id.ac.stis.capi.lessthink.listeners.DependenciesResolverListener;
import id.ac.stis.capi.lessthink.models.ModeTreeNode;
import id.ac.stis.capi.lessthink.tasks.DependenciesResolverTask; import id.ac.stis.capi.lessthink.tasks.DependenciesResolverTask;
import id.ac.stis.capi.odk.adapters.CapiFormAdapter; import id.ac.stis.capi.odk.adapters.CapiFormAdapter;
import id.ac.stis.capi.odk.application.Collect; import id.ac.stis.capi.odk.application.Collect;
...@@ -72,7 +73,7 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList ...@@ -72,7 +73,7 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList
FormDownloaderListener, FormDownloaderListener,
AuthDialogUtility.AuthDialogUtilityResultListener, AuthDialogUtility.AuthDialogUtilityResultListener,
SearchView.OnQueryTextListener, SearchView.OnQueryTextListener,
DependenciesResolverListener { DependenciesResolverListener, CapiFormAdapter.OnFormDeletedListener {
private static final String FORM_DOWNLOAD_LIST_SORTING_ORDER = "formDownloadListSortingOrder"; private static final String FORM_DOWNLOAD_LIST_SORTING_ORDER = "formDownloadListSortingOrder";
...@@ -116,6 +117,8 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList ...@@ -116,6 +117,8 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList
private SessionManager sm; private SessionManager sm;
private String idPcl; private String idPcl;
private TreeNode rootTreeNode;
private int currentNodeIndex;
/** /**
* Determines if a local form on the device is superseded by a given version (of the same form * Determines if a local form on the device is superseded by a given version (of the same form
...@@ -179,6 +182,8 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList ...@@ -179,6 +182,8 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList
messageHolder = (LinearLayout) v.findViewById(R.id.holder_message); messageHolder = (LinearLayout) v.findViewById(R.id.holder_message);
messageError = (TextView) v.findViewById(R.id.message); messageError = (TextView) v.findViewById(R.id.message);
recycleView.setNestedScrollingEnabled(true);
sharedPreferences = getActivity().getSharedPreferences(CapiInstancePreferences.COLLECTIVA_PREFERENCES_KEY, Context.MODE_PRIVATE); sharedPreferences = getActivity().getSharedPreferences(CapiInstancePreferences.COLLECTIVA_PREFERENCES_KEY, Context.MODE_PRIVATE);
setHasOptionsMenu(true); setHasOptionsMenu(true);
alertMsg = getString(R.string.please_wait); alertMsg = getString(R.string.please_wait);
...@@ -187,22 +192,58 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList ...@@ -187,22 +192,58 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList
adapter = new CapiFormAdapter(getActivity(), this); adapter = new CapiFormAdapter(getActivity(), this);
recycleView.setAdapter(adapter); recycleView.setAdapter(adapter);
adapter.setOnFormDeletedListener(this);
sortingOptions = new String[]{getString(R.string.sort_by_name_asc), getString(R.string.sort_by_name_desc)}; sortingOptions = new String[]{getString(R.string.sort_by_name_asc), getString(R.string.sort_by_name_desc)};
restoreSelectedSortingOrder(); restoreSelectedSortingOrder();
setUpDownloadedFormsOffline(); setUpDownloadedFormsOffline();
initData(); initData();
if (formNamesAndURLs.isEmpty()) { // if (formNamesAndURLs.isEmpty()) {
// first time, so get the formlist // // first time, so get the formlist
onRefreshPage(); // onRefreshPage();
} else { // } else {
onDoneLoading(); // onDoneLoading();
// }
TreeNode root = TreeNode.root();
for (int i = 0; i < 5; i++) {
TreeNode child = new ModeTreeNode("Child " + i, "child_" + i, ModeTreeNode.DOWNLOADED);
child.setLevel(0);
child.setItemClickEnable(false);
child.setExpanded(true);
root.addChild(child);
for (int j = 0; j < 2; j++) {
TreeNode grandchild = new ModeTreeNode("Grand Child " + j, "grand_child_" + j, ModeTreeNode.QUEUED);
grandchild.setLevel(j+1);
grandchild.setItemClickEnable(false);
grandchild.setExpanded(true);
child.addChild(grandchild);
}
} }
AlertDialog alertDialog = new AlertDialog.Builder(getContext())
.setTitle("VO")
.setView(new TreeView(root, getContext(), new NodeViewFactory()).getView())
.setPositiveButton("YES", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.create();
alertDialog.show();
return v; return v;
} }
@Override
public void onResume() {
super.onResume();
onRefreshPage();
}
public void refreshAfterSync() { public void refreshAfterSync() {
setUpDownloadedFormsOffline(); setUpDownloadedFormsOffline();
} }
...@@ -369,11 +410,13 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList ...@@ -369,11 +410,13 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList
progressDialog.setCancelable(false); progressDialog.setCancelable(false);
progressDialog.setButton(getString(R.string.cancel), loadingButtonListener); progressDialog.setButton(getString(R.string.cancel), loadingButtonListener);
progressDialog.show(); progressDialog.show();
break;
case AUTH_DIALOG: case AUTH_DIALOG:
Collect.getInstance().getActivityLogger().logAction(this, "onCreateDialog.AUTH_DIALOG", "show"); Collect.getInstance().getActivityLogger().logAction(this, "onCreateDialog.AUTH_DIALOG", "show");
alertShowing = false; alertShowing = false;
new AuthDialogUtility().createDialog(getContext(), this); new AuthDialogUtility().createDialog(getContext(), this);
break;
case DEPENDENCY_PROGRESS_DIALOG: case DEPENDENCY_PROGRESS_DIALOG:
progressDialog = new ProgressDialog(getContext()); progressDialog = new ProgressDialog(getContext());
progressDialog.setTitle("Resolving Dependencies"); progressDialog.setTitle("Resolving Dependencies");
...@@ -381,6 +424,8 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList ...@@ -381,6 +424,8 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList
progressDialog.setIcon(android.R.drawable.ic_dialog_info); progressDialog.setIcon(android.R.drawable.ic_dialog_info);
progressDialog.setIndeterminate(true); progressDialog.setIndeterminate(true);
progressDialog.setCancelable(false); progressDialog.setCancelable(false);
progressDialog.show();
break;
} }
} }
...@@ -564,7 +609,6 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList ...@@ -564,7 +609,6 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList
return false; return false;
} }
/** /**
* Creates an alert dialog with the given title and message. If shouldExit is set to true, the * Creates an alert dialog with the given title and message. If shouldExit is set to true, the
* activity will exit when the user clicks "ok". * activity will exit when the user clicks "ok".
...@@ -608,7 +652,6 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList ...@@ -608,7 +652,6 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList
progressDialog.setMessage(alertMsg); progressDialog.setMessage(alertMsg);
} }
@Override @Override
public void formsDownloadingComplete(HashMap<FormDetails, String> result) { public void formsDownloadingComplete(HashMap<FormDetails, String> result) {
if (downloadFormsTask != null) { if (downloadFormsTask != null) {
...@@ -620,6 +663,7 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList ...@@ -620,6 +663,7 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList
progressDialog.dismiss(); progressDialog.dismiss();
} }
Set<FormDetails> keys = result.keySet(); Set<FormDetails> keys = result.keySet();
List<FormDetails> toDependenciesResolve = new ArrayList<>(); List<FormDetails> toDependenciesResolve = new ArrayList<>();
...@@ -714,6 +758,10 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList ...@@ -714,6 +758,10 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList
downloadFormsTask = new DownloadFormsTask(); downloadFormsTask = new DownloadFormsTask();
downloadFormsTask.setDownloaderListener(this); downloadFormsTask.setDownloaderListener(this);
downloadFormsTask.execute(filesToDownload); downloadFormsTask.execute(filesToDownload);
rootTreeNode = TreeNode.root();
rootTreeNode.setExpanded(true);
rootTreeNode.setItemClickEnable(false);
} }
private void downloadFormsFromList(List<FormDetails> filesToDownload) { private void downloadFormsFromList(List<FormDetails> filesToDownload) {
...@@ -740,40 +788,49 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList ...@@ -740,40 +788,49 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList
} }
// Shows form dependencies representation // Shows form dependencies representation
TreeNode root = TreeNode.root();
// StringBuilder b = new StringBuilder();
final List<FormDetails> toDownload = new ArrayList<>(); final List<FormDetails> toDownload = new ArrayList<>();
Set<FormDetails> keys = result.keySet(); Set<FormDetails> keys = result.keySet();
for (FormDetails k : keys) { for (FormDetails k : keys) {
List<FormDetails> dependencies = result.get(k); List<FormDetails> dependencies = result.get(k);
TreeNode currTreeNode = findExistingNode(rootTreeNode.getChildren(), k.formID);
if (currTreeNode != null) {
// set downloaded
if (currTreeNode instanceof ModeTreeNode) {
((ModeTreeNode) currTreeNode).setMode(ModeTreeNode.DOWNLOADED);
}
} else {
// add new dependencies
currTreeNode = new ModeTreeNode(k.formName, k.formID, ModeTreeNode.DOWNLOADED);
currTreeNode.setLevel(0);
rootTreeNode.addChild(currTreeNode);
}
currTreeNode.setExpanded(true);
currTreeNode.setItemClickEnable(false);
if (dependencies.size() > 0) { if (dependencies.size() > 0) {
toDownload.addAll(dependencies); toDownload.addAll(dependencies);
TreeNode mainForm = new TreeNode(k.formID);
mainForm.setLevel(0);
root.addChild(mainForm);
// b.append(k.getFormName());
// b.append("\n");
for (FormDetails d : dependencies) { for (FormDetails d : dependencies) {
TreeNode dependForm = new TreeNode(d.formID); TreeNode dependForm = new ModeTreeNode(d.formName, d.formID, ModeTreeNode.QUEUED);
dependForm.setLevel(1); dependForm.setExpanded(true);
mainForm.addChild(dependForm); dependForm.setItemClickEnable(false);
// String dep = "> " + d.getFormName(); dependForm.setLevel(currTreeNode.getLevel() + 1);
currTreeNode.addChild(dependForm);
// b.append(dep);
// b.append("\n");
} }
} }
} }
View treeView = new TreeView(root, getContext(), new NodeViewFactory()).getView(); View treeView = new TreeView(rootTreeNode, getContext(), new NodeViewFactory()).getView();
if (toDownload.size() > 0) { if (toDownload.size() > 0) {
// AndroidTreeView treeView = new AndroidTreeView(this, root); // AndroidTreeView treeView = new AndroidTreeView(this, root);
// treeView.setDefaultViewHolder(); // treeView.setDefaultViewHolder();
AlertDialog dialog = new AlertDialog.Builder(getContext()) AlertDialog dialog = new AlertDialog.Builder(getContext())
.setTitle("Dependencies Detail")
.setView(treeView) .setView(treeView)
// .setMessage(b) // .setMessage(b)
.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() { .setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
...@@ -789,11 +846,13 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList ...@@ -789,11 +846,13 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList
// TODO: 21/03/2018 DOWNLOAD and RESOLVING completed // TODO: 21/03/2018 DOWNLOAD and RESOLVING completed
AlertDialog dialog = new AlertDialog.Builder(getContext()) AlertDialog dialog = new AlertDialog.Builder(getContext())
.setView(treeView) .setView(treeView)
.setTitle("Download Summary")
// .setMessage(b) // .setMessage(b)
.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() { .setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialogInterface, int i) { public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss(); dialogInterface.dismiss();
onRefreshPage();
} }
}) })
.create(); .create();
...@@ -803,6 +862,30 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList ...@@ -803,6 +862,30 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList
} }
} }
private TreeNode findExistingNode(List<TreeNode> parent, String valueId) {
for (TreeNode child : parent) {
if (child instanceof ModeTreeNode) {
if (((ModeTreeNode) child).getValueId().equals(valueId)) {
return child;
} else {
TreeNode ret = findExistingNode(child.getChildren(), valueId);
if (ret != null) {
return ret;
}
}
} else if (child.getValue().equals(valueId)) {
return child;
} else {
TreeNode ret = findExistingNode(child.getChildren(), valueId);
if (ret != null) {
return ret;
}
}
}
return null;
}
@Override @Override
public void onProgressUpdate(String currentFile, int progress, int total) { public void onProgressUpdate(String currentFile, int progress, int total) {
alertMsg = getString(R.string.fetching_dependencies, currentFile, String.valueOf(progress), alertMsg = getString(R.string.fetching_dependencies, currentFile, String.valueOf(progress),
...@@ -820,4 +903,9 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList ...@@ -820,4 +903,9 @@ public class CapiFormFragment extends Fragment implements FormListDownloaderList
adapter.getFilter().filter(newText); adapter.getFilter().filter(newText);
return true; return true;
} }
@Override
public void onFormDelete(String formId) {
onRefreshPage();
}
} }
package id.ac.stis.capi.lessthink.binders;
import android.view.View;
import android.widget.TextView;
import id.ac.stis.capi.R;
import me.texy.treeview.TreeNode;
import me.texy.treeview.base.BaseNodeViewBinder;
/**
* Author : Rahadi Jalu
* Email : 14.8325@stis.ac.id
* Company: Politeknik Statistika STIS
*/
public class FirstLevelNodeViewBinder extends BaseNodeViewBinder {
private TextView mainText;
public FirstLevelNodeViewBinder(View itemView) {
super(itemView);
mainText = itemView.findViewById(R.id.text_main);
}
@Override
public int getLayoutId() {
return R.layout.item_node_first;
}
@Override
public void bindView(TreeNode treeNode) {
mainText.setText(treeNode.getValue().toString());
}
}
package id.ac.stis.capi.lessthink.binders; package id.ac.stis.capi.lessthink.binders;
import android.support.v7.widget.RecyclerView;
import android.view.View; import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import id.ac.stis.capi.R;
import id.ac.stis.capi.lessthink.models.ModeTreeNode;
import me.texy.treeview.TreeNode;
import me.texy.treeview.base.BaseNodeViewBinder; import me.texy.treeview.base.BaseNodeViewBinder;
import me.texy.treeview.base.BaseNodeViewFactory; import me.texy.treeview.base.BaseNodeViewFactory;
...@@ -13,13 +19,51 @@ import me.texy.treeview.base.BaseNodeViewFactory; ...@@ -13,13 +19,51 @@ import me.texy.treeview.base.BaseNodeViewFactory;
public class NodeViewFactory extends BaseNodeViewFactory { public class NodeViewFactory extends BaseNodeViewFactory {
@Override @Override
public BaseNodeViewBinder getNodeViewBinder(View view, int level) { public BaseNodeViewBinder getNodeViewBinder(View view, int level) {
switch (level) { return new NodeViewBinder(view, level);
case 0: }
return new FirstLevelNodeViewBinder(view);
case 1: public class NodeViewBinder extends BaseNodeViewBinder {
return new FirstLevelNodeViewBinder(view);
default: private int level;
return null; private TextView mainText, secondaryText;
private ImageView imageStatus;
public NodeViewBinder(View itemView, int level) {
super(itemView);
this.level = level;
this.mainText = itemView.findViewById(R.id.text_main);
this.secondaryText = itemView.findViewById(R.id.text_secondary);
this.imageStatus = itemView.findViewById(R.id.image_status);
}
@Override
public int getLayoutId() {
return R.layout.item_node_first;
}
@Override
public void bindView(TreeNode treeNode) {
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) itemView.getLayoutParams();
params.leftMargin = 64 * (level + 1);
params.rightMargin = 64 * (level + 1);
itemView.setLayoutParams(params);
mainText.setText(treeNode.getValue().toString());
if (treeNode instanceof ModeTreeNode) {
secondaryText.setText(((ModeTreeNode) treeNode).getValueId());
switch (((ModeTreeNode) treeNode).getMode()) {
case ModeTreeNode.DOWNLOADED:
imageStatus.setImageResource(R.drawable.ic_check_circle);
break;
case ModeTreeNode.QUEUED:
imageStatus.setImageResource(R.drawable.ic_download);
break;
}
} else {
secondaryText.setVisibility(View.GONE);
}
} }
} }
} }
package id.ac.stis.capi.lessthink.models;
import me.texy.treeview.TreeNode;
/**
* Author : Rahadi Jalu
* Email : 14.8325@stis.ac.id
* Company: Politeknik Statistika STIS
*/
public class ModeTreeNode extends TreeNode {
public static final int DOWNLOADED = 0;
public static final int QUEUED = 1;
private int mode;
private String valueId;
public ModeTreeNode(String value, String valueId, int mode) {
super(value);
this.valueId = valueId;
this.mode = mode;
}
public String getValueId() {
return valueId;
}
public int getMode() {
return mode;
}
public void setMode(int mode) {
this.mode = mode;
}
}
...@@ -56,9 +56,9 @@ public class DependenciesResolverTask extends AsyncTask<List<FormDetails>, Strin ...@@ -56,9 +56,9 @@ public class DependenciesResolverTask extends AsyncTask<List<FormDetails>, Strin
// Process the forms supplied by the execute() parameter // Process the forms supplied by the execute() parameter
for (FormDetails fd : toResolve) { for (FormDetails fd : toResolve) {
List<DependencyDetails> details = processOneForm(fd, total, count++); List<DependencyDetails> details = processOneForm(fd, total, count++);
if (details.size() > 0) { // if (details.size() > 0) {
result.put(fd, details); result.put(fd, details);
} // }
} }
return result; return result;
...@@ -171,9 +171,9 @@ public class DependenciesResolverTask extends AsyncTask<List<FormDetails>, Strin ...@@ -171,9 +171,9 @@ public class DependenciesResolverTask extends AsyncTask<List<FormDetails>, Strin
List<FormDetails> evaluated = evaluateOneFormDependencies(evaluateMe, List<FormDetails> evaluated = evaluateOneFormDependencies(evaluateMe,
k.formName, total, count++); k.formName, total, count++);
if (evaluated.size() > 0) { // if (evaluated.size() > 0) {
result.put(k, evaluated); result.put(k, evaluated);
} // }
} }
if (listener != null) { if (listener != null) {
......
...@@ -48,6 +48,11 @@ public class CapiFormAdapter extends RecyclerView.Adapter<CapiFormAdapter.ViewHo ...@@ -48,6 +48,11 @@ public class CapiFormAdapter extends RecyclerView.Adapter<CapiFormAdapter.ViewHo
private Fragment fragment; private Fragment fragment;
private ArrayList<HashMap<String, String>> dataSet; private ArrayList<HashMap<String, String>> dataSet;
private ArrayList<HashMap<String, String>> dataSetOri; private ArrayList<HashMap<String, String>> dataSetOri;
private OnFormDeletedListener onFormDeletedListener;
public void setOnFormDeletedListener(OnFormDeletedListener onFormDeletedListener) {
this.onFormDeletedListener = onFormDeletedListener;
}
public CapiFormAdapter(Activity activity, Fragment fragment) { public CapiFormAdapter(Activity activity, Fragment fragment) {
this.context = activity; this.context = activity;
...@@ -204,13 +209,17 @@ public class CapiFormAdapter extends RecyclerView.Adapter<CapiFormAdapter.ViewHo ...@@ -204,13 +209,17 @@ public class CapiFormAdapter extends RecyclerView.Adapter<CapiFormAdapter.ViewHo
deletedItems.put(HAS_BEEN_DOWNLOADED, "false"); deletedItems.put(HAS_BEEN_DOWNLOADED, "false");
dataSet.add(deletedItems); dataSet.add(deletedItems);
notifyDataSetChanged(); notifyDataSetChanged();
if (onFormDeletedListener != null) {
onFormDeletedListener.onFormDelete(formIdKey);
}
} }
private void showConfirmDelete(final int position) { private void showConfirmDelete(final int position) {
AlertDialog.Builder builder = new AlertDialog.Builder(context); AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("Delete Form"); builder.setTitle("Delete Form");
builder.setMessage("Are you sure to delete \"" + dataSet.get(position).get(FORMNAME) + "form?"); builder.setMessage("Are you sure to delete \"" + dataSet.get(position).get(FORMNAME) + "\" form?");
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() { builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
...@@ -290,4 +299,8 @@ public class CapiFormAdapter extends RecyclerView.Adapter<CapiFormAdapter.ViewHo ...@@ -290,4 +299,8 @@ public class CapiFormAdapter extends RecyclerView.Adapter<CapiFormAdapter.ViewHo
imgPattern = (ImageView) itemView.findViewById(R.id.image_pattern); imgPattern = (ImageView) itemView.findViewById(R.id.image_pattern);
} }
} }
public interface OnFormDeletedListener {
void onFormDelete(String formId);
}
} }
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
</vector>
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/background"> android:background="@color/background">
<android.support.v7.widget.RecyclerView <android.support.v4.widget.NestedScrollView
android:id="@+id/recycler_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"/> android:layout_height="match_parent"
android:fillViewport="true">
<!--<TextView--> <LinearLayout
<!--android:id="@android:id/empty"--> android:layout_width="match_parent"
<!--android:layout_width="match_parent"-->
<!--android:layout_height="match_parent"-->
<!--android:gravity="center"-->
<!--android:text="@string/no_items_display"-->
<!--android:textSize="21sp" />-->
<ProgressBar
android:id="@+id/holder_progress_bar"
android:layout_width="42dp"
android:layout_height="42dp"
android:layout_centerInParent="true"/>
<LinearLayout
android:id="@+id/holder_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/background"
android:orientation="vertical"
android:layout_centerInParent="true"
android:gravity="center"
android:visibility="gone">
<ImageView
android:id="@+id/icon_message"
android:layout_width="50dp"
android:layout_height="70dp"
android:tint="@android:color/darker_gray"