mirror of
https://github.com/sismics/docs.git
synced 2024-11-22 14:07:55 +01:00
Android: empty lists and loading error feedback
This commit is contained in:
parent
a84748f075
commit
1d08508e51
@ -20,7 +20,7 @@ public class MainApplication extends Application {
|
|||||||
JSONObject json = PreferenceUtil.getCachedJson(getApplicationContext(), PreferenceUtil.PREF_CACHED_USER_INFO_JSON);
|
JSONObject json = PreferenceUtil.getCachedJson(getApplicationContext(), PreferenceUtil.PREF_CACHED_USER_INFO_JSON);
|
||||||
ApplicationContext.getInstance().setUserInfo(getApplicationContext(), json);
|
ApplicationContext.getInstance().setUserInfo(getApplicationContext(), json);
|
||||||
|
|
||||||
// TODO Error feedback (all REST request, even login)
|
// TODO Documents list page loading feedback
|
||||||
// TODO Fullscreen preview
|
// TODO Fullscreen preview
|
||||||
// TODO Caching preferences
|
// TODO Caching preferences
|
||||||
// TODO Edit sharing
|
// TODO Edit sharing
|
||||||
|
@ -16,10 +16,10 @@ import android.view.View;
|
|||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.loopj.android.http.JsonHttpResponseHandler;
|
|
||||||
import com.sismics.docs.R;
|
import com.sismics.docs.R;
|
||||||
import com.sismics.docs.adapter.FilePagerAdapter;
|
import com.sismics.docs.adapter.FilePagerAdapter;
|
||||||
import com.sismics.docs.event.DocumentFullscreenEvent;
|
import com.sismics.docs.event.DocumentFullscreenEvent;
|
||||||
|
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||||
import com.sismics.docs.model.application.ApplicationContext;
|
import com.sismics.docs.model.application.ApplicationContext;
|
||||||
import com.sismics.docs.resource.FileResource;
|
import com.sismics.docs.resource.FileResource;
|
||||||
import com.sismics.docs.util.PreferenceUtil;
|
import com.sismics.docs.util.PreferenceUtil;
|
||||||
@ -139,14 +139,27 @@ public class DocumentActivity extends ActionBarActivity {
|
|||||||
sharedImageView.setVisibility(shared ? View.VISIBLE : View.GONE);
|
sharedImageView.setVisibility(shared ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
// Grab the attached files
|
// Grab the attached files
|
||||||
|
final View progressBar = findViewById(R.id.progressBar);
|
||||||
|
final TextView filesEmptyView = (TextView) findViewById(R.id.filesEmptyView);
|
||||||
|
fileViewPager = (ViewPager) findViewById(R.id.fileViewPager);
|
||||||
|
fileViewPager.setOffscreenPageLimit(1);
|
||||||
|
|
||||||
FileResource.list(this, id, new JsonHttpResponseHandler() {
|
FileResource.list(this, id, new JsonHttpResponseHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||||
fileViewPager = (ViewPager) findViewById(R.id.fileViewPager);
|
JSONArray files = response.optJSONArray("files");
|
||||||
fileViewPager.setOffscreenPageLimit(1);
|
filePagerAdapter = new FilePagerAdapter(DocumentActivity.this, files);
|
||||||
filePagerAdapter = new FilePagerAdapter(DocumentActivity.this, response.optJSONArray("files"));
|
|
||||||
fileViewPager.setAdapter(filePagerAdapter);
|
fileViewPager.setAdapter(filePagerAdapter);
|
||||||
findViewById(R.id.progressBar).setVisibility(View.GONE);
|
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
if (files.length() == 0) filesEmptyView.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||||
|
filesEmptyView.setText(R.string.error_loading_files);
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
filesEmptyView.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,9 @@ import android.widget.Button;
|
|||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
|
||||||
import com.androidquery.AQuery;
|
import com.androidquery.AQuery;
|
||||||
import com.loopj.android.http.JsonHttpResponseHandler;
|
|
||||||
import com.sismics.docs.R;
|
import com.sismics.docs.R;
|
||||||
import com.sismics.docs.listener.CallbackListener;
|
import com.sismics.docs.listener.CallbackListener;
|
||||||
|
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||||
import com.sismics.docs.model.application.ApplicationContext;
|
import com.sismics.docs.model.application.ApplicationContext;
|
||||||
import com.sismics.docs.resource.UserResource;
|
import com.sismics.docs.resource.UserResource;
|
||||||
import com.sismics.docs.ui.form.Validator;
|
import com.sismics.docs.ui.form.Validator;
|
||||||
@ -109,11 +109,11 @@ public class LoginActivity extends ActionBarActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
|
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||||
loginForm.setVisibility(View.VISIBLE);
|
loginForm.setVisibility(View.VISIBLE);
|
||||||
progressBar.setVisibility(View.GONE);
|
progressBar.setVisibility(View.GONE);
|
||||||
|
|
||||||
if (responseString != null && responseString.contains("\"ForbiddenError\"")) {
|
if (responseBytes != null && new String(responseBytes).contains("\"ForbiddenError\"")) {
|
||||||
DialogUtil.showOkDialog(LoginActivity.this, R.string.login_fail_title, R.string.login_fail);
|
DialogUtil.showOkDialog(LoginActivity.this, R.string.login_fail_title, R.string.login_fail);
|
||||||
} else {
|
} else {
|
||||||
DialogUtil.showOkDialog(LoginActivity.this, R.string.network_error_title, R.string.network_error);
|
DialogUtil.showOkDialog(LoginActivity.this, R.string.network_error_title, R.string.network_error);
|
||||||
@ -167,9 +167,9 @@ public class LoginActivity extends ActionBarActivity {
|
|||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
|
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||||
DialogUtil.showOkDialog(LoginActivity.this, R.string.network_error_title, R.string.network_error);
|
DialogUtil.showOkDialog(LoginActivity.this, R.string.network_error_title, R.string.network_error);
|
||||||
loginForm.setVisibility(View.VISIBLE);
|
loginForm.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,10 @@ import android.widget.AdapterView;
|
|||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.loopj.android.http.JsonHttpResponseHandler;
|
|
||||||
import com.sismics.docs.R;
|
import com.sismics.docs.R;
|
||||||
import com.sismics.docs.adapter.TagListAdapter;
|
import com.sismics.docs.adapter.TagListAdapter;
|
||||||
import com.sismics.docs.event.SearchEvent;
|
import com.sismics.docs.event.SearchEvent;
|
||||||
|
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||||
import com.sismics.docs.model.application.ApplicationContext;
|
import com.sismics.docs.model.application.ApplicationContext;
|
||||||
import com.sismics.docs.provider.RecentSuggestionsProvider;
|
import com.sismics.docs.provider.RecentSuggestionsProvider;
|
||||||
import com.sismics.docs.resource.TagResource;
|
import com.sismics.docs.resource.TagResource;
|
||||||
@ -79,12 +79,20 @@ public class MainActivity extends ActionBarActivity {
|
|||||||
// Get tag list to fill the drawer
|
// Get tag list to fill the drawer
|
||||||
final ListView tagListView = (ListView) findViewById(R.id.tagListView);
|
final ListView tagListView = (ListView) findViewById(R.id.tagListView);
|
||||||
final View tagProgressView = findViewById(R.id.tagProgressView);
|
final View tagProgressView = findViewById(R.id.tagProgressView);
|
||||||
final View tagEmptyView = findViewById(R.id.tagEmptyView);
|
final TextView tagEmptyView = (TextView) findViewById(R.id.tagEmptyView);
|
||||||
tagListView.setEmptyView(tagProgressView);
|
tagListView.setEmptyView(tagProgressView);
|
||||||
TagResource.stats(this, new JsonHttpResponseHandler() {
|
TagResource.stats(this, new JsonHttpResponseHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||||
tagListView.setAdapter(new TagListAdapter(response.optJSONArray("stats")));
|
tagListView.setAdapter(new TagListAdapter(response.optJSONArray("stats")));
|
||||||
|
tagProgressView.setVisibility(View.GONE);
|
||||||
|
tagListView.setEmptyView(tagEmptyView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||||
|
tagEmptyView.setText(R.string.error_loading_tags);
|
||||||
|
tagProgressView.setVisibility(View.GONE);
|
||||||
tagListView.setEmptyView(tagEmptyView);
|
tagListView.setEmptyView(tagEmptyView);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -8,15 +8,18 @@ import android.support.v7.widget.RecyclerView;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.loopj.android.http.JsonHttpResponseHandler;
|
|
||||||
import com.sismics.docs.R;
|
import com.sismics.docs.R;
|
||||||
import com.sismics.docs.activity.DocumentActivity;
|
import com.sismics.docs.activity.DocumentActivity;
|
||||||
import com.sismics.docs.adapter.DocListAdapter;
|
import com.sismics.docs.adapter.DocListAdapter;
|
||||||
import com.sismics.docs.event.SearchEvent;
|
import com.sismics.docs.event.SearchEvent;
|
||||||
|
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||||
import com.sismics.docs.listener.RecyclerItemClickListener;
|
import com.sismics.docs.listener.RecyclerItemClickListener;
|
||||||
import com.sismics.docs.resource.DocumentResource;
|
import com.sismics.docs.resource.DocumentResource;
|
||||||
import com.sismics.docs.ui.view.DividerItemDecoration;
|
import com.sismics.docs.ui.view.DividerItemDecoration;
|
||||||
|
import com.sismics.docs.ui.view.EmptyRecyclerView;
|
||||||
|
|
||||||
import org.apache.http.Header;
|
import org.apache.http.Header;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@ -27,6 +30,11 @@ import de.greenrobot.event.EventBus;
|
|||||||
* @author bgamard.
|
* @author bgamard.
|
||||||
*/
|
*/
|
||||||
public class DocListFragment extends Fragment {
|
public class DocListFragment extends Fragment {
|
||||||
|
/**
|
||||||
|
* Recycler view.
|
||||||
|
*/
|
||||||
|
private EmptyRecyclerView recyclerView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Documents adapter.
|
* Documents adapter.
|
||||||
*/
|
*/
|
||||||
@ -46,7 +54,7 @@ public class DocListFragment extends Fragment {
|
|||||||
View view = inflater.inflate(R.layout.doc_list_fragment, container, false);
|
View view = inflater.inflate(R.layout.doc_list_fragment, container, false);
|
||||||
|
|
||||||
// Configure the RecyclerView
|
// Configure the RecyclerView
|
||||||
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.docList);
|
recyclerView = (EmptyRecyclerView) view.findViewById(R.id.docList);
|
||||||
adapter = new DocListAdapter();
|
adapter = new DocListAdapter();
|
||||||
recyclerView.setAdapter(adapter);
|
recyclerView.setAdapter(adapter);
|
||||||
recyclerView.setHasFixedSize(true);
|
recyclerView.setHasFixedSize(true);
|
||||||
@ -87,14 +95,14 @@ public class DocListFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!loading && totalItemCount - visibleItemCount <= firstVisibleItem + 3) {
|
if (!loading && totalItemCount - visibleItemCount <= firstVisibleItem + 3) {
|
||||||
loadDocuments(false);
|
loadDocuments(getView(), false);
|
||||||
loading = true;
|
loading = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Grab the documents
|
// Grab the documents
|
||||||
loadDocuments(true);
|
loadDocuments(view, true);
|
||||||
|
|
||||||
EventBus.getDefault().register(this);
|
EventBus.getDefault().register(this);
|
||||||
return view;
|
return view;
|
||||||
@ -113,30 +121,43 @@ public class DocListFragment extends Fragment {
|
|||||||
*/
|
*/
|
||||||
public void onEvent(SearchEvent event) {
|
public void onEvent(SearchEvent event) {
|
||||||
query = event.getQuery();
|
query = event.getQuery();
|
||||||
loadDocuments(true);
|
loadDocuments(getView(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refresh the document list.
|
* Refresh the document list.
|
||||||
*
|
*
|
||||||
|
* @param view View
|
||||||
* @param reset If true, reload the documents
|
* @param reset If true, reload the documents
|
||||||
*/
|
*/
|
||||||
private void loadDocuments(final boolean reset) {
|
private void loadDocuments(final View view, final boolean reset) {
|
||||||
|
if (view == null) return;
|
||||||
|
final View progressBar = view.findViewById(R.id.progressBar);
|
||||||
|
final TextView documentsEmptyView = (TextView) view.findViewById(R.id.documentsEmptyView);
|
||||||
|
|
||||||
if (reset) {
|
if (reset) {
|
||||||
loading = true;
|
loading = true;
|
||||||
previousTotal = 0;
|
previousTotal = 0;
|
||||||
adapter.clearDocuments();
|
adapter.clearDocuments();
|
||||||
if (getView() != null) {
|
|
||||||
getView().findViewById(R.id.progressBar).setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
recyclerView.setEmptyView(progressBar);
|
||||||
|
|
||||||
DocumentResource.list(getActivity(), reset ? 0 : adapter.getItemCount(), query, new JsonHttpResponseHandler() {
|
DocumentResource.list(getActivity(), reset ? 0 : adapter.getItemCount(), query, new JsonHttpResponseHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||||
adapter.addDocuments(response.optJSONArray("documents"));
|
adapter.addDocuments(response.optJSONArray("documents"));
|
||||||
if (getView() != null) {
|
documentsEmptyView.setText(R.string.no_documents);
|
||||||
getView().findViewById(R.id.progressBar).setVisibility(View.GONE);
|
recyclerView.setEmptyView(documentsEmptyView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||||
|
documentsEmptyView.setText(R.string.error_loading_documents);
|
||||||
|
recyclerView.setEmptyView(documentsEmptyView);
|
||||||
|
|
||||||
|
if (!reset) {
|
||||||
|
// We are loading a new page, so the empty view won't be visible, pop a toast
|
||||||
|
Toast.makeText(getActivity(), R.string.error_loading_documents, Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,241 @@
|
|||||||
|
/*
|
||||||
|
Android Asynchronous Http Client
|
||||||
|
Copyright (c) 2011 James Smith <james@loopj.com>
|
||||||
|
http://loopj.com
|
||||||
|
|
||||||
|
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.sismics.docs.listener;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.loopj.android.http.TextHttpResponseHandler;
|
||||||
|
|
||||||
|
import org.apache.http.Header;
|
||||||
|
import org.apache.http.HttpStatus;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.json.JSONTokener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to intercept and handle the responses from requests made using {@link com.loopj.android.http.AsyncHttpClient}, with
|
||||||
|
* automatic parsing into a {@link JSONObject} or {@link JSONArray}. <p> </p> This class is
|
||||||
|
* designed to be passed to get, post, put and delete requests with the {@link #onSuccess(int,
|
||||||
|
* org.apache.http.Header[], org.json.JSONArray)} or {@link #onSuccess(int,
|
||||||
|
* org.apache.http.Header[], org.json.JSONObject)} methods anonymously overridden. <p> </p>
|
||||||
|
* Additionally, you can override the other event methods from the parent class.
|
||||||
|
*/
|
||||||
|
public class JsonHttpResponseHandler extends TextHttpResponseHandler {
|
||||||
|
|
||||||
|
private static final String LOG_TAG = "JsonHttpResponseHandler";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new JsonHttpResponseHandler, with JSON String encoding UTF-8
|
||||||
|
*/
|
||||||
|
public JsonHttpResponseHandler() {
|
||||||
|
super(DEFAULT_CHARSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new JsonHttpRespnseHandler with given JSON String encoding
|
||||||
|
*
|
||||||
|
* @param encoding String encoding to be used when parsing JSON
|
||||||
|
*/
|
||||||
|
public JsonHttpResponseHandler(String encoding) {
|
||||||
|
super(encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns when request succeeds
|
||||||
|
*
|
||||||
|
* @param statusCode http response status line
|
||||||
|
* @param headers response headers if any
|
||||||
|
* @param response parsed response if any
|
||||||
|
*/
|
||||||
|
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||||
|
Log.w(LOG_TAG, "onSuccess(int, Header[], JSONObject) was not overriden, but callback was received");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns when request succeeds
|
||||||
|
*
|
||||||
|
* @param statusCode http response status line
|
||||||
|
* @param headers response headers if any
|
||||||
|
* @param response parsed response if any
|
||||||
|
*/
|
||||||
|
public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
|
||||||
|
Log.w(LOG_TAG, "onSuccess(int, Header[], JSONArray) was not overriden, but callback was received");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns when request failed
|
||||||
|
*
|
||||||
|
* @param statusCode http response status line
|
||||||
|
* @param headers response headers if any
|
||||||
|
* @param throwable throwable describing the way request failed
|
||||||
|
* @param errorResponse parsed response if any
|
||||||
|
*/
|
||||||
|
public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
|
||||||
|
Log.w(LOG_TAG, "onFailure(int, Header[], Throwable, JSONObject) was not overriden, but callback was received", throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns when request failed
|
||||||
|
*
|
||||||
|
* @param statusCode http response status line
|
||||||
|
* @param headers response headers if any
|
||||||
|
* @param throwable throwable describing the way request failed
|
||||||
|
* @param errorResponse parsed response if any
|
||||||
|
*/
|
||||||
|
public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONArray errorResponse) {
|
||||||
|
Log.w(LOG_TAG, "onFailure(int, Header[], Throwable, JSONArray) was not overriden, but callback was received", throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
|
||||||
|
Log.w(LOG_TAG, "onFailure(int, Header[], String, Throwable) was not overriden, but callback was received", throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(int statusCode, Header[] headers, String responseString) {
|
||||||
|
Log.w(LOG_TAG, "onSuccess(int, Header[], String) was not overriden, but callback was received");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void onSuccess(final int statusCode, final Header[] headers, final byte[] responseBytes) {
|
||||||
|
if (statusCode != HttpStatus.SC_NO_CONTENT) {
|
||||||
|
Runnable parser = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
final Object jsonResponse = parseResponse(responseBytes);
|
||||||
|
postRunnable(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (jsonResponse instanceof JSONObject) {
|
||||||
|
onSuccess(statusCode, headers, (JSONObject) jsonResponse);
|
||||||
|
} else if (jsonResponse instanceof JSONArray) {
|
||||||
|
onSuccess(statusCode, headers, (JSONArray) jsonResponse);
|
||||||
|
} else if (jsonResponse instanceof String) {
|
||||||
|
onFailure(statusCode, headers, (String) jsonResponse, new JSONException("Response cannot be parsed as JSON data"));
|
||||||
|
} else {
|
||||||
|
onFailure(statusCode, headers, new JSONException("Unexpected response type " + jsonResponse.getClass().getName()), (JSONObject) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (final JSONException ex) {
|
||||||
|
postRunnable(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
onFailure(statusCode, headers, ex, (JSONObject) null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (!getUseSynchronousMode()) {
|
||||||
|
new Thread(parser).start();
|
||||||
|
} else {
|
||||||
|
// In synchronous mode everything should be run on one thread
|
||||||
|
parser.run();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onSuccess(statusCode, headers, new JSONObject());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void onFailure(final int statusCode, final Header[] headers, final byte[] responseBytes, final Throwable throwable) {
|
||||||
|
if (responseBytes != null) {
|
||||||
|
Runnable parser = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
final Object jsonResponse = parseResponse(responseBytes);
|
||||||
|
postRunnable(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (jsonResponse instanceof JSONObject) {
|
||||||
|
onFailure(statusCode, headers, throwable, (JSONObject) jsonResponse);
|
||||||
|
} else if (jsonResponse instanceof JSONArray) {
|
||||||
|
onFailure(statusCode, headers, throwable, (JSONArray) jsonResponse);
|
||||||
|
} else if (jsonResponse instanceof String) {
|
||||||
|
onFailure(statusCode, headers, (String) jsonResponse, throwable);
|
||||||
|
} else {
|
||||||
|
onFailure(statusCode, headers, new JSONException("Unexpected response type " + jsonResponse.getClass().getName()), (JSONObject) null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (final JSONException ex) {
|
||||||
|
postRunnable(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
onFailure(statusCode, headers, ex, (JSONObject) null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (!getUseSynchronousMode()) {
|
||||||
|
new Thread(parser).start();
|
||||||
|
} else {
|
||||||
|
// In synchronous mode everything should be run on one thread
|
||||||
|
parser.run();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.v(LOG_TAG, "response body is null, calling onFailure(Throwable, JSONObject)");
|
||||||
|
onFailure(statusCode, headers, throwable, (JSONObject) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// In all cases, call the default failure listener
|
||||||
|
onAllFailure(statusCode, headers, responseBytes, throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||||
|
// All failures go there
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns Object of type {@link JSONObject}, {@link JSONArray}, String, Boolean, Integer, Long,
|
||||||
|
* Double or {@link JSONObject#NULL}, see {@link org.json.JSONTokener#nextValue()}
|
||||||
|
*
|
||||||
|
* @param responseBody response bytes to be assembled in String and parsed as JSON
|
||||||
|
* @return Object parsedResponse
|
||||||
|
* @throws org.json.JSONException exception if thrown while parsing JSON
|
||||||
|
*/
|
||||||
|
protected Object parseResponse(byte[] responseBody) throws JSONException {
|
||||||
|
if (null == responseBody)
|
||||||
|
return null;
|
||||||
|
Object result = null;
|
||||||
|
//trim the string to prevent start with blank, and test if the string is valid JSON, because the parser don't do this :(. If JSON is not valid this will return null
|
||||||
|
String jsonString = getResponseString(responseBody, getCharset());
|
||||||
|
if (jsonString != null) {
|
||||||
|
jsonString = jsonString.trim();
|
||||||
|
if (jsonString.startsWith(UTF8_BOM)) {
|
||||||
|
jsonString = jsonString.substring(1);
|
||||||
|
}
|
||||||
|
if (jsonString.startsWith("{") || jsonString.startsWith("[")) {
|
||||||
|
result = new JSONTokener(jsonString).nextValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result == null) {
|
||||||
|
result = jsonString;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -3,8 +3,8 @@ package com.sismics.docs.model.application;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.loopj.android.http.JsonHttpResponseHandler;
|
|
||||||
import com.sismics.docs.listener.CallbackListener;
|
import com.sismics.docs.listener.CallbackListener;
|
||||||
|
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||||
import com.sismics.docs.resource.UserResource;
|
import com.sismics.docs.resource.UserResource;
|
||||||
import com.sismics.docs.util.PreferenceUtil;
|
import com.sismics.docs.util.PreferenceUtil;
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@ package com.sismics.docs.resource;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.loopj.android.http.JsonHttpResponseHandler;
|
|
||||||
import com.loopj.android.http.RequestParams;
|
import com.loopj.android.http.RequestParams;
|
||||||
|
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Access to /document API.
|
* Access to /document API.
|
||||||
|
@ -2,7 +2,8 @@ package com.sismics.docs.resource;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.loopj.android.http.JsonHttpResponseHandler;
|
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Access to /file API.
|
* Access to /file API.
|
||||||
|
@ -2,7 +2,8 @@ package com.sismics.docs.resource;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.loopj.android.http.JsonHttpResponseHandler;
|
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Access to /tag API.
|
* Access to /tag API.
|
||||||
|
@ -2,8 +2,8 @@ package com.sismics.docs.resource;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.loopj.android.http.JsonHttpResponseHandler;
|
|
||||||
import com.loopj.android.http.RequestParams;
|
import com.loopj.android.http.RequestParams;
|
||||||
|
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Access to /user API.
|
* Access to /user API.
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
package com.sismics.docs.ui.view;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RecyclerView with empty view support.
|
||||||
|
* Thanks to https://gist.github.com/adelnizamutdinov/31c8f054d1af4588dc5c
|
||||||
|
*
|
||||||
|
* @author Nizamutdinov Adel
|
||||||
|
*/
|
||||||
|
public class EmptyRecyclerView extends RecyclerView {
|
||||||
|
private View emptyView;
|
||||||
|
|
||||||
|
public EmptyRecyclerView(Context context) { super(context); }
|
||||||
|
|
||||||
|
public EmptyRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); }
|
||||||
|
|
||||||
|
public EmptyRecyclerView(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkIfEmpty() {
|
||||||
|
if (emptyView != null) {
|
||||||
|
emptyView.setVisibility(getAdapter().getItemCount() > 0 ? GONE : VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final AdapterDataObserver observer = new AdapterDataObserver() {
|
||||||
|
@Override public void onChanged() {
|
||||||
|
super.onChanged();
|
||||||
|
checkIfEmpty();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override public void setAdapter(Adapter adapter) {
|
||||||
|
final Adapter oldAdapter = getAdapter();
|
||||||
|
if (oldAdapter != null) {
|
||||||
|
oldAdapter.unregisterAdapterDataObserver(observer);
|
||||||
|
}
|
||||||
|
super.setAdapter(adapter);
|
||||||
|
if (adapter != null) {
|
||||||
|
adapter.registerAdapterDataObserver(observer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmptyView(View emptyView) {
|
||||||
|
// Hide the current empty view
|
||||||
|
if (this.emptyView != null) {
|
||||||
|
this.emptyView.setVisibility(GONE);
|
||||||
|
}
|
||||||
|
this.emptyView = emptyView;
|
||||||
|
checkIfEmpty();
|
||||||
|
}
|
||||||
|
}
|
@ -4,20 +4,30 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<android.support.v7.widget.RecyclerView
|
<com.sismics.docs.ui.view.EmptyRecyclerView
|
||||||
android:id="@+id/docList"
|
android:id="@+id/docList"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:scrollbars="vertical">
|
android:scrollbars="vertical">
|
||||||
|
</com.sismics.docs.ui.view.EmptyRecyclerView>
|
||||||
</android.support.v7.widget.RecyclerView>
|
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/progressBar"
|
android:id="@+id/progressBar"
|
||||||
style="?android:attr/progressBarStyleLarge"
|
style="?android:attr/progressBarStyleLarge"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
android:layout_centerInParent="true"
|
android:layout_centerInParent="true"
|
||||||
android:indeterminate="true" />
|
android:indeterminate="true" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/documentsEmptyView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:text="@string/no_documents"
|
||||||
|
android:fontFamily="sans-serif-light"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:layout_centerInParent="true"/>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
@ -94,6 +94,16 @@
|
|||||||
android:layout_centerInParent="true"
|
android:layout_centerInParent="true"
|
||||||
android:indeterminate="true"/>
|
android:indeterminate="true"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/filesEmptyView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:text="@string/no_files"
|
||||||
|
android:fontFamily="sans-serif-light"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:layout_centerInParent="true"/>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@ -1,6 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
|
<!-- Validation -->
|
||||||
|
<string name="validate_error_email">Invalid email</string>
|
||||||
|
<string name="validate_error_length_min">Too short (min. %d)</string>
|
||||||
|
<string name="validate_error_length_max">Too long (max. %d)</string>
|
||||||
|
<string name="validate_error_required">Required</string>
|
||||||
|
<string name="validate_error_alphanumeric">Only letters and numbers</string>
|
||||||
|
|
||||||
|
<!-- App -->
|
||||||
<string name="app_name">Sismics Docs</string>
|
<string name="app_name">Sismics Docs</string>
|
||||||
<string name="drawer_open">Open navigation drawer</string>
|
<string name="drawer_open">Open navigation drawer</string>
|
||||||
<string name="drawer_close">Close navigation drawer</string>
|
<string name="drawer_close">Close navigation drawer</string>
|
||||||
@ -28,12 +36,10 @@
|
|||||||
<string name="shared_documents">Shared documents</string>
|
<string name="shared_documents">Shared documents</string>
|
||||||
<string name="all_tags">All tags</string>
|
<string name="all_tags">All tags</string>
|
||||||
<string name="no_tags">No tags</string>
|
<string name="no_tags">No tags</string>
|
||||||
|
<string name="error_loading_tags">Error loading tags</string>
|
||||||
<!-- Validation -->
|
<string name="no_documents">No documents</string>
|
||||||
<string name="validate_error_email">Invalid email</string>
|
<string name="error_loading_documents">Error loading documents</string>
|
||||||
<string name="validate_error_length_min">Too short (min. %d)</string>
|
<string name="no_files">No files</string>
|
||||||
<string name="validate_error_length_max">Too long (max. %d)</string>
|
<string name="error_loading_files">Error loading files</string>
|
||||||
<string name="validate_error_required">Required</string>
|
|
||||||
<string name="validate_error_alphanumeric">Only letters and numbers</string>
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user