diff --git a/docs-android/app/src/main/java/com/sismics/docs/MainApplication.java b/docs-android/app/src/main/java/com/sismics/docs/MainApplication.java index 97867ffc..306f2a92 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/MainApplication.java +++ b/docs-android/app/src/main/java/com/sismics/docs/MainApplication.java @@ -20,7 +20,7 @@ public class MainApplication extends Application { JSONObject json = PreferenceUtil.getCachedJson(getApplicationContext(), PreferenceUtil.PREF_CACHED_USER_INFO_JSON); ApplicationContext.getInstance().setUserInfo(getApplicationContext(), json); - // TODO Error feedback (all REST request, even login) + // TODO Documents list page loading feedback // TODO Fullscreen preview // TODO Caching preferences // TODO Edit sharing diff --git a/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentActivity.java b/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentActivity.java index 78d01f25..3f11336b 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentActivity.java +++ b/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentActivity.java @@ -16,10 +16,10 @@ import android.view.View; import android.widget.ImageView; import android.widget.TextView; -import com.loopj.android.http.JsonHttpResponseHandler; import com.sismics.docs.R; import com.sismics.docs.adapter.FilePagerAdapter; import com.sismics.docs.event.DocumentFullscreenEvent; +import com.sismics.docs.listener.JsonHttpResponseHandler; import com.sismics.docs.model.application.ApplicationContext; import com.sismics.docs.resource.FileResource; import com.sismics.docs.util.PreferenceUtil; @@ -139,14 +139,27 @@ public class DocumentActivity extends ActionBarActivity { sharedImageView.setVisibility(shared ? View.VISIBLE : View.GONE); // 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() { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - fileViewPager = (ViewPager) findViewById(R.id.fileViewPager); - fileViewPager.setOffscreenPageLimit(1); - filePagerAdapter = new FilePagerAdapter(DocumentActivity.this, response.optJSONArray("files")); + JSONArray files = response.optJSONArray("files"); + filePagerAdapter = new FilePagerAdapter(DocumentActivity.this, files); 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); } }); } diff --git a/docs-android/app/src/main/java/com/sismics/docs/activity/LoginActivity.java b/docs-android/app/src/main/java/com/sismics/docs/activity/LoginActivity.java index 46814cbf..fa952854 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/activity/LoginActivity.java +++ b/docs-android/app/src/main/java/com/sismics/docs/activity/LoginActivity.java @@ -12,9 +12,9 @@ import android.widget.Button; import android.widget.EditText; import com.androidquery.AQuery; -import com.loopj.android.http.JsonHttpResponseHandler; import com.sismics.docs.R; import com.sismics.docs.listener.CallbackListener; +import com.sismics.docs.listener.JsonHttpResponseHandler; import com.sismics.docs.model.application.ApplicationContext; import com.sismics.docs.resource.UserResource; import com.sismics.docs.ui.form.Validator; @@ -109,11 +109,11 @@ public class LoginActivity extends ActionBarActivity { } @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); 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); } else { DialogUtil.showOkDialog(LoginActivity.this, R.string.network_error_title, R.string.network_error); @@ -167,9 +167,9 @@ public class LoginActivity extends ActionBarActivity { startActivity(intent); finish(); } - + @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); loginForm.setVisibility(View.VISIBLE); } diff --git a/docs-android/app/src/main/java/com/sismics/docs/activity/MainActivity.java b/docs-android/app/src/main/java/com/sismics/docs/activity/MainActivity.java index e2721ca6..c151e325 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/activity/MainActivity.java +++ b/docs-android/app/src/main/java/com/sismics/docs/activity/MainActivity.java @@ -18,10 +18,10 @@ import android.widget.AdapterView; import android.widget.ListView; import android.widget.TextView; -import com.loopj.android.http.JsonHttpResponseHandler; import com.sismics.docs.R; import com.sismics.docs.adapter.TagListAdapter; import com.sismics.docs.event.SearchEvent; +import com.sismics.docs.listener.JsonHttpResponseHandler; import com.sismics.docs.model.application.ApplicationContext; import com.sismics.docs.provider.RecentSuggestionsProvider; import com.sismics.docs.resource.TagResource; @@ -79,12 +79,20 @@ public class MainActivity extends ActionBarActivity { // Get tag list to fill the drawer final ListView tagListView = (ListView) findViewById(R.id.tagListView); 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); TagResource.stats(this, new JsonHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { 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); } }); diff --git a/docs-android/app/src/main/java/com/sismics/docs/fragment/DocListFragment.java b/docs-android/app/src/main/java/com/sismics/docs/fragment/DocListFragment.java index 3c391081..0f7693e6 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/fragment/DocListFragment.java +++ b/docs-android/app/src/main/java/com/sismics/docs/fragment/DocListFragment.java @@ -8,15 +8,18 @@ import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; 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.activity.DocumentActivity; import com.sismics.docs.adapter.DocListAdapter; import com.sismics.docs.event.SearchEvent; +import com.sismics.docs.listener.JsonHttpResponseHandler; import com.sismics.docs.listener.RecyclerItemClickListener; import com.sismics.docs.resource.DocumentResource; import com.sismics.docs.ui.view.DividerItemDecoration; +import com.sismics.docs.ui.view.EmptyRecyclerView; import org.apache.http.Header; import org.json.JSONObject; @@ -27,6 +30,11 @@ import de.greenrobot.event.EventBus; * @author bgamard. */ public class DocListFragment extends Fragment { + /** + * Recycler view. + */ + private EmptyRecyclerView recyclerView; + /** * Documents adapter. */ @@ -46,7 +54,7 @@ public class DocListFragment extends Fragment { View view = inflater.inflate(R.layout.doc_list_fragment, container, false); // Configure the RecyclerView - RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.docList); + recyclerView = (EmptyRecyclerView) view.findViewById(R.id.docList); adapter = new DocListAdapter(); recyclerView.setAdapter(adapter); recyclerView.setHasFixedSize(true); @@ -87,14 +95,14 @@ public class DocListFragment extends Fragment { } } if (!loading && totalItemCount - visibleItemCount <= firstVisibleItem + 3) { - loadDocuments(false); + loadDocuments(getView(), false); loading = true; } } }); // Grab the documents - loadDocuments(true); + loadDocuments(view, true); EventBus.getDefault().register(this); return view; @@ -113,30 +121,43 @@ public class DocListFragment extends Fragment { */ public void onEvent(SearchEvent event) { query = event.getQuery(); - loadDocuments(true); + loadDocuments(getView(), true); } /** * Refresh the document list. * + * @param view View * @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) { loading = true; previousTotal = 0; 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() { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { adapter.addDocuments(response.optJSONArray("documents")); - if (getView() != null) { - getView().findViewById(R.id.progressBar).setVisibility(View.GONE); + documentsEmptyView.setText(R.string.no_documents); + 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(); } } }); diff --git a/docs-android/app/src/main/java/com/sismics/docs/listener/JsonHttpResponseHandler.java b/docs-android/app/src/main/java/com/sismics/docs/listener/JsonHttpResponseHandler.java new file mode 100644 index 00000000..207812f2 --- /dev/null +++ b/docs-android/app/src/main/java/com/sismics/docs/listener/JsonHttpResponseHandler.java @@ -0,0 +1,241 @@ +/* + Android Asynchronous Http Client + Copyright (c) 2011 James Smith + 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}.

 

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.

 

+ * 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; + } +} diff --git a/docs-android/app/src/main/java/com/sismics/docs/model/application/ApplicationContext.java b/docs-android/app/src/main/java/com/sismics/docs/model/application/ApplicationContext.java index 3afeaab5..d9bea906 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/model/application/ApplicationContext.java +++ b/docs-android/app/src/main/java/com/sismics/docs/model/application/ApplicationContext.java @@ -3,8 +3,8 @@ package com.sismics.docs.model.application; import android.app.Activity; import android.content.Context; -import com.loopj.android.http.JsonHttpResponseHandler; import com.sismics.docs.listener.CallbackListener; +import com.sismics.docs.listener.JsonHttpResponseHandler; import com.sismics.docs.resource.UserResource; import com.sismics.docs.util.PreferenceUtil; diff --git a/docs-android/app/src/main/java/com/sismics/docs/resource/DocumentResource.java b/docs-android/app/src/main/java/com/sismics/docs/resource/DocumentResource.java index c9121882..526010f8 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/resource/DocumentResource.java +++ b/docs-android/app/src/main/java/com/sismics/docs/resource/DocumentResource.java @@ -2,8 +2,8 @@ package com.sismics.docs.resource; import android.content.Context; -import com.loopj.android.http.JsonHttpResponseHandler; import com.loopj.android.http.RequestParams; +import com.sismics.docs.listener.JsonHttpResponseHandler; /** * Access to /document API. diff --git a/docs-android/app/src/main/java/com/sismics/docs/resource/FileResource.java b/docs-android/app/src/main/java/com/sismics/docs/resource/FileResource.java index 516fd3e1..c30cf3fd 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/resource/FileResource.java +++ b/docs-android/app/src/main/java/com/sismics/docs/resource/FileResource.java @@ -2,7 +2,8 @@ package com.sismics.docs.resource; import android.content.Context; -import com.loopj.android.http.JsonHttpResponseHandler; +import com.sismics.docs.listener.JsonHttpResponseHandler; + /** * Access to /file API. diff --git a/docs-android/app/src/main/java/com/sismics/docs/resource/TagResource.java b/docs-android/app/src/main/java/com/sismics/docs/resource/TagResource.java index 9e70ef24..db03bb16 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/resource/TagResource.java +++ b/docs-android/app/src/main/java/com/sismics/docs/resource/TagResource.java @@ -2,7 +2,8 @@ package com.sismics.docs.resource; import android.content.Context; -import com.loopj.android.http.JsonHttpResponseHandler; +import com.sismics.docs.listener.JsonHttpResponseHandler; + /** * Access to /tag API. diff --git a/docs-android/app/src/main/java/com/sismics/docs/resource/UserResource.java b/docs-android/app/src/main/java/com/sismics/docs/resource/UserResource.java index b2ebdf99..36cb11db 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/resource/UserResource.java +++ b/docs-android/app/src/main/java/com/sismics/docs/resource/UserResource.java @@ -2,8 +2,8 @@ package com.sismics.docs.resource; import android.content.Context; -import com.loopj.android.http.JsonHttpResponseHandler; import com.loopj.android.http.RequestParams; +import com.sismics.docs.listener.JsonHttpResponseHandler; /** * Access to /user API. diff --git a/docs-android/app/src/main/java/com/sismics/docs/ui/view/EmptyRecyclerView.java b/docs-android/app/src/main/java/com/sismics/docs/ui/view/EmptyRecyclerView.java new file mode 100644 index 00000000..92f4b7c3 --- /dev/null +++ b/docs-android/app/src/main/java/com/sismics/docs/ui/view/EmptyRecyclerView.java @@ -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(); + } +} \ No newline at end of file diff --git a/docs-android/app/src/main/res/layout/doc_list_fragment.xml b/docs-android/app/src/main/res/layout/doc_list_fragment.xml index 46316db1..316b2f15 100644 --- a/docs-android/app/src/main/res/layout/doc_list_fragment.xml +++ b/docs-android/app/src/main/res/layout/doc_list_fragment.xml @@ -4,20 +4,30 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - + + + \ No newline at end of file diff --git a/docs-android/app/src/main/res/layout/document_activity.xml b/docs-android/app/src/main/res/layout/document_activity.xml index c66f2ad1..bae34ac1 100644 --- a/docs-android/app/src/main/res/layout/document_activity.xml +++ b/docs-android/app/src/main/res/layout/document_activity.xml @@ -94,6 +94,16 @@ android:layout_centerInParent="true" android:indeterminate="true"/> + + \ No newline at end of file diff --git a/docs-android/app/src/main/res/values/strings.xml b/docs-android/app/src/main/res/values/strings.xml index c002cc1c..3c9ac83e 100644 --- a/docs-android/app/src/main/res/values/strings.xml +++ b/docs-android/app/src/main/res/values/strings.xml @@ -1,6 +1,14 @@ + + Invalid email + Too short (min. %d) + Too long (max. %d) + Required + Only letters and numbers + + Sismics Docs Open navigation drawer Close navigation drawer @@ -28,12 +36,10 @@ Shared documents All tags No tags - - - Invalid email - Too short (min. %d) - Too long (max. %d) - Required - Only letters and numbers + Error loading tags + No documents + Error loading documents + No files + Error loading files