diff --git a/README.md b/README.md
index 6daf5871..dd5ff95c 100644
--- a/README.md
+++ b/README.md
@@ -60,7 +60,6 @@ or download the sources from GitHub.
From the `docs-parent` directory:
- mvn -Pinit validate -N
mvn clean -DskipTests install
#### Run a stand-alone version
diff --git a/docs-android/app/app.iml b/docs-android/app/app.iml
index 704ef345..0df93fa8 100644
--- a/docs-android/app/app.iml
+++ b/docs-android/app/app.iml
@@ -12,10 +12,7 @@
-
-
- generateDebugAndroidTestSources
generateDebugSources
@@ -28,7 +25,7 @@
-
+
@@ -50,6 +47,13 @@
+
+
+
+
+
+
+
@@ -64,65 +68,58 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs-android/app/build.gradle b/docs-android/app/build.gradle
index 3c2c46fb..3c6ad05c 100644
--- a/docs-android/app/build.gradle
+++ b/docs-android/app/build.gradle
@@ -3,7 +3,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.0.0-alpha3'
+ classpath 'com.android.tools.build:gradle:2.0.0-alpha7'
}
}
apply plugin: 'com.android.application'
@@ -13,12 +13,12 @@ repositories {
}
android {
- compileSdkVersion 22
+ compileSdkVersion 23
buildToolsVersion '23.0.2'
defaultConfig {
minSdkVersion 14
- targetSdkVersion 22
+ targetSdkVersion 23
versionCode 1
versionName "1.0"
}
@@ -50,10 +50,14 @@ android {
dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
- compile 'com.android.support:appcompat-v7:22.+'
- compile 'com.android.support:recyclerview-v7:22.+'
- compile 'com.loopj.android:android-async-http:1.4.6'
+ compile 'com.android.support:appcompat-v7:23.1.1'
+ compile 'com.android.support:recyclerview-v7:23.1.1'
+ compile 'com.loopj.android:android-async-http:1.4.9'
compile 'it.sephiroth.android.library.imagezoom:imagezoom:1.0.5'
compile 'de.greenrobot:eventbus:2.4.1'
compile 'com.shamanland:fab:0.0.6'
+ compile 'com.squareup.picasso:picasso:2.5.2'
+ compile 'com.squareup.okhttp3:okhttp:3.0.1'
+ compile "com.squareup.okhttp3:okhttp-urlconnection:3.0.1"
+ compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.0.2'
}
diff --git a/docs-android/app/libs/android-query.0.26.8.jar b/docs-android/app/libs/android-query.0.26.8.jar
deleted file mode 100644
index 7fc26f17..00000000
Binary files a/docs-android/app/libs/android-query.0.26.8.jar and /dev/null differ
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 6d636c78..0f01b31c 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
@@ -2,7 +2,6 @@ package com.sismics.docs;
import android.app.Application;
-import com.androidquery.callback.BitmapAjaxCallback;
import com.sismics.docs.model.application.ApplicationContext;
import com.sismics.docs.util.PreferenceUtil;
@@ -24,10 +23,4 @@ public class MainApplication extends Application {
super.onCreate();
}
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- BitmapAjaxCallback.clearCache();
- }
}
diff --git a/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentEditActivity.java b/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentEditActivity.java
index cea2d4f4..6214b2fe 100644
--- a/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentEditActivity.java
+++ b/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentEditActivity.java
@@ -17,7 +17,7 @@ import com.sismics.docs.adapter.LanguageAdapter;
import com.sismics.docs.adapter.TagAutoCompleteAdapter;
import com.sismics.docs.event.DocumentAddEvent;
import com.sismics.docs.event.DocumentEditEvent;
-import com.sismics.docs.listener.JsonHttpResponseHandler;
+import com.sismics.docs.listener.HttpCallback;
import com.sismics.docs.resource.DocumentResource;
import com.sismics.docs.ui.form.Validator;
import com.sismics.docs.ui.form.validator.Required;
@@ -25,7 +25,6 @@ import com.sismics.docs.ui.view.DatePickerView;
import com.sismics.docs.ui.view.TagsCompleteTextView;
import com.sismics.docs.util.PreferenceUtil;
-import org.apache.http.Header;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -174,9 +173,9 @@ public class DocumentEditActivity extends AppCompatActivity {
});
// Server callback
- JsonHttpResponseHandler callback = new JsonHttpResponseHandler() {
+ HttpCallback callback = new HttpCallback() {
@Override
- public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
+ public void onSuccess(JSONObject response) {
// Build a fake document JSON to update the UI
final JSONObject outputDoc = new JSONObject();
try {
@@ -211,7 +210,7 @@ public class DocumentEditActivity extends AppCompatActivity {
}
@Override
- public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
+ public void onFailure(JSONObject json, Exception e) {
Toast.makeText(DocumentEditActivity.this, R.string.error_editing_document, Toast.LENGTH_LONG).show();
}
diff --git a/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java b/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java
index d6bc8f12..8c90f2c5 100644
--- a/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java
+++ b/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java
@@ -44,7 +44,9 @@ import com.sismics.docs.event.DocumentEditEvent;
import com.sismics.docs.event.DocumentFullscreenEvent;
import com.sismics.docs.event.FileAddEvent;
import com.sismics.docs.event.FileDeleteEvent;
+import com.sismics.docs.fragment.DocExportPdfFragment;
import com.sismics.docs.fragment.DocShareFragment;
+import com.sismics.docs.listener.HttpCallback;
import com.sismics.docs.listener.JsonHttpResponseHandler;
import com.sismics.docs.model.application.ApplicationContext;
import com.sismics.docs.resource.CommentResource;
@@ -54,7 +56,6 @@ import com.sismics.docs.service.FileUploadService;
import com.sismics.docs.util.PreferenceUtil;
import com.sismics.docs.util.TagUtil;
-import org.apache.http.Header;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -63,6 +64,7 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
+import cz.msebera.android.httpclient.Header;
import de.greenrobot.event.EventBus;
/**
@@ -244,6 +246,16 @@ public class DocumentViewActivity extends AppCompatActivity {
}
});
+ // Action export PDF
+ button = (Button) findViewById(R.id.actionExportPdf);
+ button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ DialogFragment dialog = DocExportPdfFragment.newInstance(DocumentViewActivity.this.document.optString("id"));
+ dialog.show(getSupportFragmentManager(), "DocExportPdfFragment");
+ }
+ });
+
// Action share
button = (Button) findViewById(R.id.actionSharing);
button.setOnClickListener(new View.OnClickListener() {
@@ -476,14 +488,14 @@ public class DocumentViewActivity extends AppCompatActivity {
// Actual delete server call
final String documentId = document.optString("id");
- DocumentResource.delete(DocumentViewActivity.this, documentId, new JsonHttpResponseHandler() {
+ DocumentResource.delete(DocumentViewActivity.this, documentId, new HttpCallback() {
@Override
- public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
+ public void onSuccess(JSONObject response) {
EventBus.getDefault().post(new DocumentDeleteEvent(documentId));
}
@Override
- public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
+ public void onFailure(JSONObject json, Exception e) {
Toast.makeText(DocumentViewActivity.this, R.string.document_delete_failure, Toast.LENGTH_LONG).show();
}
@@ -635,9 +647,9 @@ public class DocumentViewActivity extends AppCompatActivity {
// Silently get the document to know if it is writable by the current user
// If this call fails or is slow and the document is read-only,
// write actions will be allowed and will fail
- DocumentResource.get(this, document.optString("id"), new JsonHttpResponseHandler() {
+ DocumentResource.get(this, document.optString("id"), new HttpCallback() {
@Override
- public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
+ public void onSuccess(JSONObject response) {
document = response;
boolean writable = document.optBoolean("writable");
@@ -720,7 +732,7 @@ public class DocumentViewActivity extends AppCompatActivity {
@Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
JSONArray comments = response.optJSONArray("comments");
- commentListAdapter = new CommentListAdapter(comments);
+ commentListAdapter = new CommentListAdapter(DocumentViewActivity.this, comments);
listView.setAdapter(commentListAdapter);
listView.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
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 dc10323b..9495e863 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
@@ -10,8 +10,8 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
+import android.widget.TextView;
-import com.androidquery.AQuery;
import com.sismics.docs.R;
import com.sismics.docs.listener.CallbackListener;
import com.sismics.docs.listener.JsonHttpResponseHandler;
@@ -22,9 +22,10 @@ import com.sismics.docs.ui.form.validator.Required;
import com.sismics.docs.util.DialogUtil;
import com.sismics.docs.util.PreferenceUtil;
-import org.apache.http.Header;
import org.json.JSONObject;
+import cz.msebera.android.httpclient.Header;
+
/**
* Login activity.
*
@@ -42,19 +43,17 @@ public class LoginActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login_activity);
-
- AQuery aq = new AQuery(this);
- aq.id(R.id.loginExplain)
- .text(Html.fromHtml(getString(R.string.login_explain)))
- .getTextView()
- .setMovementMethod(LinkMovementMethod.getInstance());
-
- final EditText txtServer = aq.id(R.id.txtServer).getEditText();
- final EditText txtUsername = aq.id(R.id.txtUsername).getEditText();
- final EditText txtPassword = aq.id(R.id.txtPassword).getEditText();
- final Button btnConnect = aq.id(R.id.btnConnect).getButton();
- loginForm = aq.id(R.id.loginForm).getView();
- progressBar = aq.id(R.id.progressBar).getView();
+
+ TextView loginExplainTextView = (TextView) findViewById(R.id.loginExplain);
+ loginExplainTextView.setText(Html.fromHtml(getString(R.string.login_explain)));
+ loginExplainTextView.setMovementMethod(LinkMovementMethod.getInstance());
+
+ final EditText txtServer = (EditText) findViewById(R.id.txtServer);
+ final EditText txtUsername = (EditText) findViewById(R.id.txtUsername);
+ final EditText txtPassword = (EditText) findViewById(R.id.txtPassword);
+ final Button btnConnect = (Button) findViewById(R.id.btnConnect);
+ loginForm = findViewById(R.id.loginForm);
+ progressBar = findViewById(R.id.progressBar);
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
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 70ec4975..80748991 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,7 +18,6 @@ import android.widget.ListView;
import android.widget.SearchView;
import android.widget.TextView;
-import com.androidquery.util.AQUtility;
import com.sismics.docs.R;
import com.sismics.docs.adapter.TagListAdapter;
import com.sismics.docs.event.AdvancedSearchEvent;
@@ -31,9 +30,9 @@ import com.sismics.docs.resource.TagResource;
import com.sismics.docs.resource.UserResource;
import com.sismics.docs.util.PreferenceUtil;
-import org.apache.http.Header;
import org.json.JSONObject;
+import cz.msebera.android.httpclient.Header;
import de.greenrobot.event.EventBus;
/**
@@ -274,10 +273,6 @@ public class MainActivity extends AppCompatActivity {
@Override
protected void onDestroy() {
EventBus.getDefault().unregister(this);
- if(isTaskRoot()) {
- int cacheSizeMb = PreferenceUtil.getIntegerPreference(this, PreferenceUtil.PREF_CACHE_SIZE, 10);
- AQUtility.cleanCacheAsync(this, cacheSizeMb * 1000000, cacheSizeMb * 1000000);
- }
super.onDestroy();
}
}
\ No newline at end of file
diff --git a/docs-android/app/src/main/java/com/sismics/docs/adapter/CommentListAdapter.java b/docs-android/app/src/main/java/com/sismics/docs/adapter/CommentListAdapter.java
index 3a11585c..6983d5e3 100644
--- a/docs-android/app/src/main/java/com/sismics/docs/adapter/CommentListAdapter.java
+++ b/docs-android/app/src/main/java/com/sismics/docs/adapter/CommentListAdapter.java
@@ -1,7 +1,6 @@
package com.sismics.docs.adapter;
import android.content.Context;
-import android.graphics.Bitmap;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.View;
@@ -10,9 +9,8 @@ import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
-import com.androidquery.AQuery;
-import com.androidquery.callback.BitmapAjaxCallback;
import com.sismics.docs.R;
+import com.sismics.docs.util.OkHttpUtil;
import org.json.JSONArray;
import org.json.JSONObject;
@@ -27,22 +25,23 @@ import java.util.List;
* @author bgamard.
*/
public class CommentListAdapter extends BaseAdapter {
- /**
- * AQuery.
- */
- private AQuery aq;
-
/**
* Tags.
*/
private List commentList = new ArrayList<>();
+ /**
+ * Context.
+ */
+ private Context context;
+
/**
* Comment list adapter.
*
* @param commentsArray Comments
*/
- public CommentListAdapter(JSONArray commentsArray) {
+ public CommentListAdapter(Context context, JSONArray commentsArray) {
+ this.context = context;
for (int i = 0; i < commentsArray.length(); i++) {
commentList.add(commentsArray.optJSONObject(i));
}
@@ -70,12 +69,6 @@ public class CommentListAdapter extends BaseAdapter {
view = vi.inflate(R.layout.comment_list_item, parent, false);
}
- if (aq == null) {
- aq = new AQuery(view);
- } else {
- aq.recycle(view);
- }
-
// Fill the view
JSONObject comment = getItem(position);
TextView creatorTextView = (TextView) view.findViewById(R.id.creatorTextView);
@@ -88,14 +81,9 @@ public class CommentListAdapter extends BaseAdapter {
// Gravatar image
String gravatarUrl = "http://www.gravatar.com/avatar/" + comment.optString("creator_gravatar") + "?s=128d=identicon";
- if (aq.shouldDelay(position, view, parent, gravatarUrl)) {
- aq.id(gravatarImageView).image((Bitmap) null);
- } else {
- aq.id(gravatarImageView).image(new BitmapAjaxCallback()
- .url(gravatarUrl)
- .animation(AQuery.FADE_IN_NETWORK)
- );
- }
+ OkHttpUtil.picasso(context)
+ .load(gravatarUrl)
+ .into(gravatarImageView);
return view;
}
diff --git a/docs-android/app/src/main/java/com/sismics/docs/adapter/FilePagerAdapter.java b/docs-android/app/src/main/java/com/sismics/docs/adapter/FilePagerAdapter.java
index e952ee35..8b04cede 100644
--- a/docs-android/app/src/main/java/com/sismics/docs/adapter/FilePagerAdapter.java
+++ b/docs-android/app/src/main/java/com/sismics/docs/adapter/FilePagerAdapter.java
@@ -7,10 +7,11 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
-import com.androidquery.AQuery;
-import com.androidquery.callback.BitmapAjaxCallback;
import com.sismics.docs.R;
+import com.sismics.docs.util.OkHttpUtil;
import com.sismics.docs.util.PreferenceUtil;
+import com.squareup.picasso.Callback;
+import com.squareup.picasso.MemoryPolicy;
import org.json.JSONArray;
import org.json.JSONObject;
@@ -30,11 +31,6 @@ public class FilePagerAdapter extends PagerAdapter {
*/
private List files;
- /**
- * AQuery.
- */
- private AQuery aq;
-
/**
* Context.
*/
@@ -58,7 +54,6 @@ public class FilePagerAdapter extends PagerAdapter {
}
this.context = context;
this.authToken = PreferenceUtil.getAuthToken(context);
- aq = new AQuery(context);
}
@Override
@@ -66,15 +61,20 @@ public class FilePagerAdapter extends PagerAdapter {
View view = LayoutInflater.from(container.getContext()).inflate(R.layout.file_viewpager_item, container, false);
ImageViewTouch fileImageView = (ImageViewTouch) view.findViewById(R.id.fileImageView);
- ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.fileProgressBar);
+ final ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.fileProgressBar);
JSONObject file = files.get(position);
String fileUrl = PreferenceUtil.getServerUrl(context) + "/api/file/" + file.optString("id") + "/data?size=web";
- aq.id(fileImageView)
- .image(new BitmapAjaxCallback()
- .url(fileUrl)
- .progress(progressBar)
- .animation(AQuery.FADE_IN_NETWORK)
- .cookie("auth_token", authToken));
+
+ // Load image
+ OkHttpUtil.picasso(context)
+ .load(fileUrl)
+ .memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE) // Don't memory cache the images
+ .into(fileImageView, new Callback.EmptyCallback() {
+ @Override
+ public void onSuccess() {
+ progressBar.setVisibility(View.GONE);
+ }
+ });
fileImageView.setDisplayType(ImageViewTouchBase.DisplayType.FIT_TO_SCREEN);
@@ -109,7 +109,7 @@ public class FilePagerAdapter extends PagerAdapter {
* @return Object
*/
public JSONObject getObjectAt(int position) {
- if (files == null) {
+ if (files == null || position < 0 || position >= files.size()) {
return null;
}
diff --git a/docs-android/app/src/main/java/com/sismics/docs/fragment/DocExportPdfFragment.java b/docs-android/app/src/main/java/com/sismics/docs/fragment/DocExportPdfFragment.java
new file mode 100644
index 00000000..0aab910e
--- /dev/null
+++ b/docs-android/app/src/main/java/com/sismics/docs/fragment/DocExportPdfFragment.java
@@ -0,0 +1,56 @@
+package com.sismics.docs.fragment;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.sismics.docs.R;
+
+/**
+ * Export PDF dialog fragment.
+ *
+ * @author bgamard.
+ */
+public class DocExportPdfFragment extends DialogFragment {
+ /**
+ * Export PDF dialog fragment.
+ *
+ * @param id Document ID
+ */
+ public static DocExportPdfFragment newInstance(String id) {
+ DocExportPdfFragment fragment = new DocExportPdfFragment();
+ Bundle args = new Bundle();
+ args.putString("id", id);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ // Setup the view
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+ View view = inflater.inflate(R.layout.document_export_pdf_dialog, null);
+
+ // Build the dialog
+ builder.setView(view)
+ .setPositiveButton(R.string.download, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ getDialog().cancel();
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ getDialog().cancel();
+ }
+ });
+ return builder.create();
+ }
+}
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 3122128b..db810b88 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
@@ -21,13 +21,12 @@ import com.sismics.docs.event.DocumentAddEvent;
import com.sismics.docs.event.DocumentDeleteEvent;
import com.sismics.docs.event.DocumentEditEvent;
import com.sismics.docs.event.SearchEvent;
-import com.sismics.docs.listener.JsonHttpResponseHandler;
+import com.sismics.docs.listener.HttpCallback;
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;
import de.greenrobot.event.EventBus;
@@ -218,16 +217,16 @@ public class DocListFragment extends Fragment {
recyclerView.setEmptyView(progressBar);
- DocumentResource.list(getActivity(), reset ? 0 : adapter.getItemCount(), query, new JsonHttpResponseHandler() {
+ DocumentResource.list(getActivity(), reset ? 0 : adapter.getItemCount(), query, new HttpCallback() {
@Override
- public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
+ public void onSuccess(JSONObject response) {
adapter.addDocuments(response.optJSONArray("documents"));
documentsEmptyView.setText(R.string.no_documents);
recyclerView.setEmptyView(documentsEmptyView);
}
@Override
- public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
+ public void onFailure(JSONObject response, Exception e) {
documentsEmptyView.setText(R.string.error_loading_documents);
recyclerView.setEmptyView(documentsEmptyView);
diff --git a/docs-android/app/src/main/java/com/sismics/docs/fragment/DocShareFragment.java b/docs-android/app/src/main/java/com/sismics/docs/fragment/DocShareFragment.java
index 93cb93d2..aa85a063 100644
--- a/docs-android/app/src/main/java/com/sismics/docs/fragment/DocShareFragment.java
+++ b/docs-android/app/src/main/java/com/sismics/docs/fragment/DocShareFragment.java
@@ -21,15 +21,16 @@ import com.sismics.docs.R;
import com.sismics.docs.adapter.ShareListAdapter;
import com.sismics.docs.event.ShareDeleteEvent;
import com.sismics.docs.event.ShareSendEvent;
+import com.sismics.docs.listener.HttpCallback;
import com.sismics.docs.listener.JsonHttpResponseHandler;
import com.sismics.docs.resource.DocumentResource;
import com.sismics.docs.resource.ShareResource;
import com.sismics.docs.util.PreferenceUtil;
-import org.apache.http.Header;
import org.json.JSONArray;
import org.json.JSONObject;
+import cz.msebera.android.httpclient.Header;
import de.greenrobot.event.EventBus;
/**
@@ -44,7 +45,8 @@ public class DocShareFragment extends DialogFragment {
private JSONObject document;
/**
- * Document sharing dialog fragment
+ * Document sharing dialog fragment.
+ *
* @param id Document ID
*/
public static DocShareFragment newInstance(String id) {
@@ -121,9 +123,9 @@ public class DocShareFragment extends DialogFragment {
final ProgressBar shareProgressBar = (ProgressBar) view.findViewById(R.id.shareProgressBar);
shareListView.setEmptyView(shareProgressBar);
- DocumentResource.get(getActivity(), getArguments().getString("id"), new JsonHttpResponseHandler() {
+ DocumentResource.get(getActivity(), getArguments().getString("id"), new HttpCallback() {
@Override
- public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
+ public void onSuccess(JSONObject response) {
document = response;
JSONArray acls = response.optJSONArray("acls");
shareProgressBar.setVisibility(View.GONE);
@@ -132,7 +134,7 @@ public class DocShareFragment extends DialogFragment {
}
@Override
- public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
+ public void onFailure(JSONObject json, Exception e) {
getDialog().cancel();
Toast.makeText(getActivity(), R.string.error_loading_shares, Toast.LENGTH_SHORT).show();
}
diff --git a/docs-android/app/src/main/java/com/sismics/docs/fragment/SettingsFragment.java b/docs-android/app/src/main/java/com/sismics/docs/fragment/SettingsFragment.java
index f3c67fa1..2daecefe 100644
--- a/docs-android/app/src/main/java/com/sismics/docs/fragment/SettingsFragment.java
+++ b/docs-android/app/src/main/java/com/sismics/docs/fragment/SettingsFragment.java
@@ -9,10 +9,10 @@ import android.preference.PreferenceManager;
import android.provider.SearchRecentSuggestions;
import android.widget.Toast;
-import com.androidquery.util.AQUtility;
import com.sismics.docs.R;
import com.sismics.docs.provider.RecentSuggestionsProvider;
import com.sismics.docs.util.ApplicationUtil;
+import com.sismics.docs.util.OkHttpUtil;
import com.sismics.docs.util.PreferenceUtil;
/**
@@ -52,7 +52,7 @@ public class SettingsFragment extends PreferenceFragment implements SharedPrefer
clearCachePref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
- AQUtility.cleanCacheAsync(getActivity());
+ OkHttpUtil.clearCache(getActivity());
Toast.makeText(getActivity(), R.string.pref_clear_cache_success, Toast.LENGTH_LONG).show();
return true;
}
diff --git a/docs-android/app/src/main/java/com/sismics/docs/listener/HttpCallback.java b/docs-android/app/src/main/java/com/sismics/docs/listener/HttpCallback.java
new file mode 100644
index 00000000..17802174
--- /dev/null
+++ b/docs-android/app/src/main/java/com/sismics/docs/listener/HttpCallback.java
@@ -0,0 +1,78 @@
+package com.sismics.docs.listener;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import org.json.JSONObject;
+
+import java.io.IOException;
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+/**
+ * An HTTP callback.
+ *
+ * @author bgamard.
+ */
+public class HttpCallback {
+ public void onSuccess(JSONObject json) {
+ // Implement me
+ }
+
+ public void onFailure(JSONObject json, Exception e) {
+ // Implement me
+ }
+
+ public void onFinish() {
+ // Implement me
+ }
+
+ /**
+ * Build an OkHttp Callback from a HttpCallback.
+ *
+ * @param httpCallback HttpCallback
+ * @return OkHttp Callback
+ */
+ public static Callback buildOkHttpCallback(final HttpCallback httpCallback) {
+ return new Callback() {
+ @Override
+ public void onResponse(final Call call, final Response response) throws IOException {
+ final String body = response.body().string();
+
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ if (response.isSuccessful()) {
+ try {
+ httpCallback.onSuccess(new JSONObject(body));
+ } catch (Exception e) {
+ httpCallback.onFailure(null, e);
+ }
+ } else {
+ try {
+ httpCallback.onFailure(new JSONObject(body), null);
+ } catch (Exception e) {
+ httpCallback.onFailure(null, e);
+ }
+ }
+
+ httpCallback.onFinish();
+ }
+ });
+ }
+
+ @Override
+ public void onFailure(final Call call, final IOException e) {
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ httpCallback.onFailure(null, e);
+ httpCallback.onFinish();
+ }
+ });
+ }
+ };
+ }
+}
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
index 207812f2..ee9bb33c 100644
--- 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
@@ -22,19 +22,20 @@ 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;
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HttpStatus;
+
/**
* 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.
+ * cz.msebera.android.httpclient.Header[], org.json.JSONArray)} or {@link #onSuccess(int,
+ * cz.msebera.android.httpclient.Header[], org.json.JSONObject)} methods anonymously overridden.
* Additionally, you can override the other event methods from the parent class.
*/
public class JsonHttpResponseHandler extends TextHttpResponseHandler {
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 d9bea906..3fe032a8 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
@@ -8,9 +8,10 @@ import com.sismics.docs.listener.JsonHttpResponseHandler;
import com.sismics.docs.resource.UserResource;
import com.sismics.docs.util.PreferenceUtil;
-import org.apache.http.Header;
import org.json.JSONObject;
+import cz.msebera.android.httpclient.Header;
+
/**
* Global context of the application.
*
diff --git a/docs-android/app/src/main/java/com/sismics/docs/resource/BaseResource.java b/docs-android/app/src/main/java/com/sismics/docs/resource/BaseResource.java
index d79d8ef7..c0f421cb 100644
--- a/docs-android/app/src/main/java/com/sismics/docs/resource/BaseResource.java
+++ b/docs-android/app/src/main/java/com/sismics/docs/resource/BaseResource.java
@@ -3,14 +3,11 @@ package com.sismics.docs.resource;
import android.content.Context;
import android.os.Build;
-import com.androidquery.callback.AbstractAjaxCallback;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.PersistentCookieStore;
import com.sismics.docs.util.ApplicationUtil;
import com.sismics.docs.util.PreferenceUtil;
-import org.apache.http.conn.ssl.SSLSocketFactory;
-
import java.io.IOException;
import java.net.Socket;
import java.security.KeyManagementException;
@@ -26,13 +23,14 @@ import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
+import cz.msebera.android.httpclient.conn.ssl.SSLSocketFactory;
+
/**
* Base class for API access.
*
* @author bgamard
*/
public class BaseResource {
-
/**
* User-Agent to use.
*/
@@ -44,20 +42,21 @@ public class BaseResource {
protected static String ACCEPT_LANGUAGE = null;
/**
- * HTTP client.
+ * Async HTTP client.
*/
protected static AsyncHttpClient client = new AsyncHttpClient();
-
+
static {
// 20sec default timeout
client.setTimeout(60000);
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
+
+ // Async HTTP Client uses another HTTP libary
MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(MySSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
client.setSSLSocketFactory(sf);
- AbstractAjaxCallback.setSSF(sf);
} catch (Exception e) {
// NOP
}
@@ -82,14 +81,14 @@ public class BaseResource {
client.addHeader("Accept-Language", ACCEPT_LANGUAGE);
}
}
-
+
/**
- * Socket factory to allow self-signed certificates.
- *
+ * Socket factory to allow self-signed certificates for Async HTTP Client.
+ *
* @author bgamard
*/
- public static class MySSLSocketFactory extends SSLSocketFactory {
- SSLContext sslContext = SSLContext.getInstance("TLS");
+ public static class MySSLSocketFactory extends cz.msebera.android.httpclient.conn.ssl.SSLSocketFactory {
+ SSLContext sslContext = SSLContext.getInstance(SSLSocketFactory.TLS);
public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
super(truststore);
@@ -106,7 +105,7 @@ public class BaseResource {
}
};
- sslContext.init(null, new TrustManager[] { tm }, null);
+ sslContext.init(null, new TrustManager[]{tm}, null);
}
@Override
@@ -119,7 +118,7 @@ public class BaseResource {
return sslContext.getSocketFactory().createSocket();
}
}
-
+
/**
* Returns cleaned API URL.
*
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 dc7a89f2..474590bf 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,11 +2,15 @@ package com.sismics.docs.resource;
import android.content.Context;
-import com.loopj.android.http.RequestParams;
-import com.sismics.docs.listener.JsonHttpResponseHandler;
+import com.sismics.docs.listener.HttpCallback;
+import com.sismics.docs.util.OkHttpUtil;
import java.util.Set;
+import okhttp3.FormBody;
+import okhttp3.HttpUrl;
+import okhttp3.Request;
+
/**
* Access to /document API.
*
@@ -19,18 +23,23 @@ public class DocumentResource extends BaseResource {
* @param context Context
* @param offset Offset
* @param query Search query
- * @param responseHandler Callback
+ * @param callback Callback
*/
- public static void list(Context context, int offset, String query, JsonHttpResponseHandler responseHandler) {
- init(context);
-
- RequestParams params = new RequestParams();
- params.put("limit", 20);
- params.put("offset", offset);
- params.put("sort_column", 3);
- params.put("asc", false);
- params.put("search", query);
- client.get(getApiUrl(context) + "/document/list", params, responseHandler);
+ public static void list(Context context, int offset, String query, HttpCallback callback) {
+ Request request = new Request.Builder()
+ .url(HttpUrl.parse(getApiUrl(context) + "/document/list")
+ .newBuilder()
+ .addQueryParameter("limit", "20")
+ .addQueryParameter("offset", Integer.toString(offset))
+ .addQueryParameter("sort_column", "3")
+ .addQueryParameter("asc", "false")
+ .addQueryParameter("search", query)
+ .build())
+ .get()
+ .build();
+ OkHttpUtil.buildClient(context)
+ .newCall(request)
+ .enqueue(HttpCallback.buildOkHttpCallback(callback));
}
/**
@@ -38,12 +47,16 @@ public class DocumentResource extends BaseResource {
*
* @param context Context
* @param id ID
- * @param responseHandler Callback
+ * @param callback Callback
*/
- public static void get(Context context, String id, JsonHttpResponseHandler responseHandler) {
- init(context);
-
- client.get(getApiUrl(context) + "/document/" + id, responseHandler);
+ public static void get(Context context, String id, HttpCallback callback) {
+ Request request = new Request.Builder()
+ .url(HttpUrl.parse(getApiUrl(context) + "/document/" + id))
+ .get()
+ .build();
+ OkHttpUtil.buildClient(context)
+ .newCall(request)
+ .enqueue(HttpCallback.buildOkHttpCallback(callback));
}
/**
@@ -51,12 +64,16 @@ public class DocumentResource extends BaseResource {
*
* @param context Context
* @param id ID
- * @param responseHandler Callback
+ * @param callback Callback
*/
- public static void delete(Context context, String id, JsonHttpResponseHandler responseHandler) {
- init(context);
-
- client.delete(getApiUrl(context) + "/document/" + id, responseHandler);
+ public static void delete(Context context, String id, HttpCallback callback) {
+ Request request = new Request.Builder()
+ .url(HttpUrl.parse(getApiUrl(context) + "/document/" + id))
+ .delete()
+ .build();
+ OkHttpUtil.buildClient(context)
+ .newCall(request)
+ .enqueue(HttpCallback.buildOkHttpCallback(callback));
}
/**
@@ -68,19 +85,27 @@ public class DocumentResource extends BaseResource {
* @param tagIdList Tags ID list
* @param language Language
* @param createDate Create date
- * @param responseHandler Callback
+ * @param callback Callback
*/
public static void add(Context context, String title, String description,
- Set tagIdList, String language, long createDate, JsonHttpResponseHandler responseHandler) {
- init(context);
+ Set tagIdList, String language, long createDate, HttpCallback callback) {
+ FormBody.Builder formBuilder = new FormBody.Builder()
+ .add("title", title)
+ .add("description", description)
+ .add("language", language)
+ .add("create_date", Long.toString(createDate));
+ String[] tagIdArray = tagIdList.toArray(new String[tagIdList.size()]);
+ for (int i = 0; i < tagIdArray.length; i++) {
+ formBuilder.add("tags", tagIdArray[i]);
+ }
- RequestParams params = new RequestParams();
- params.put("title", title);
- params.put("description", description);
- params.put("tags", tagIdList);
- params.put("language", language);
- params.put("create_date", createDate);
- client.put(getApiUrl(context) + "/document", params, responseHandler);
+ Request request = new Request.Builder()
+ .url(HttpUrl.parse(getApiUrl(context) + "/document"))
+ .put(formBuilder.build())
+ .build();
+ OkHttpUtil.buildClient(context)
+ .newCall(request)
+ .enqueue(HttpCallback.buildOkHttpCallback(callback));
}
/**
@@ -93,19 +118,27 @@ public class DocumentResource extends BaseResource {
* @param tagIdList Tags ID list
* @param language Language
* @param createDate Create date
- * @param responseHandler Callback
+ * @param callback Callback
*/
public static void edit(Context context, String id, String title, String description,
- Set tagIdList, String language, long createDate, JsonHttpResponseHandler responseHandler) {
- init(context);
+ Set tagIdList, String language, long createDate, HttpCallback callback) {
+ FormBody.Builder formBuilder = new FormBody.Builder()
+ .add("title", title)
+ .add("description", description)
+ .add("language", language)
+ .add("create_date", Long.toString(createDate));
+ String[] tagIdArray = tagIdList.toArray(new String[tagIdList.size()]);
+ for (int i = 0; i < tagIdArray.length; i++) {
+ formBuilder.add("tags", tagIdArray[i]);
+ }
- RequestParams params = new RequestParams();
- params.put("title", title);
- params.put("description", description);
- params.put("tags", tagIdList);
- params.put("language", language);
- params.put("create_date", createDate);
- client.post(getApiUrl(context) + "/document/" + id, params, responseHandler);
+ Request request = new Request.Builder()
+ .url(HttpUrl.parse(getApiUrl(context) + "/document/" + id))
+ .post(formBuilder.build())
+ .build();
+ OkHttpUtil.buildClient(context)
+ .newCall(request)
+ .enqueue(HttpCallback.buildOkHttpCallback(callback));
}
/**
diff --git a/docs-android/app/src/main/java/com/sismics/docs/resource/cookie/PersistentCookieStore.java b/docs-android/app/src/main/java/com/sismics/docs/resource/cookie/PersistentCookieStore.java
new file mode 100644
index 00000000..990d37ed
--- /dev/null
+++ b/docs-android/app/src/main/java/com/sismics/docs/resource/cookie/PersistentCookieStore.java
@@ -0,0 +1,229 @@
+package com.sismics.docs.resource.cookie;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.CookieStore;
+import java.net.HttpCookie;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * A persistent cookie store which implements the Apache HttpClient CookieStore interface.
+ * Cookies are stored and will persist on the user's device between application sessions since they
+ * are serialized and stored in SharedPreferences.
+ */
+public class PersistentCookieStore implements CookieStore {
+
+ private static final String LOG_TAG = "PersistentCookieStore";
+ private static final String COOKIE_PREFS = "CookiePrefsFileOkHttp";
+ private static final String COOKIE_NAME_PREFIX = "cookie_okhttp_";
+
+ private final HashMap> cookies;
+ private final SharedPreferences cookiePrefs;
+
+ /**
+ * Construct a persistent cookie store.
+ *
+ * @param context Context to attach cookie store to
+ */
+ public PersistentCookieStore(Context context) {
+ cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0);
+ cookies = new HashMap<>();
+
+ // Load any previously stored cookies into the store
+ Map prefsMap = cookiePrefs.getAll();
+ for (Map.Entry entry : prefsMap.entrySet()) {
+ if (entry.getValue() != null && !((String) entry.getValue()).startsWith(COOKIE_NAME_PREFIX)) {
+ String[] cookieNames = TextUtils.split((String) entry.getValue(), ",");
+ for (String name : cookieNames) {
+ String encodedCookie = cookiePrefs.getString(COOKIE_NAME_PREFIX + name, null);
+ if (encodedCookie != null) {
+ HttpCookie decodedCookie = decodeCookie(encodedCookie);
+ if (decodedCookie != null) {
+ if (!cookies.containsKey(entry.getKey()))
+ cookies.put(entry.getKey(), new ConcurrentHashMap());
+ cookies.get(entry.getKey()).put(name, decodedCookie);
+ }
+ }
+ }
+
+ }
+ }
+ }
+
+ @Override
+ public void add(URI uri, HttpCookie cookie) {
+ String name = getCookieToken(uri, cookie);
+
+ // Save cookie into local store, or remove if expired
+ if (!cookie.hasExpired()) {
+ if (!cookies.containsKey(uri.getHost()))
+ cookies.put(uri.getHost(), new ConcurrentHashMap());
+ cookies.get(uri.getHost()).put(name, cookie);
+ } else {
+ if (cookies.containsKey(uri.toString()))
+ cookies.get(uri.getHost()).remove(name);
+ }
+
+ // Save cookie into persistent store
+ SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
+ prefsWriter.putString(uri.getHost(), TextUtils.join(",", cookies.get(uri.getHost()).keySet()));
+ prefsWriter.putString(COOKIE_NAME_PREFIX + name, encodeCookie(new SerializableHttpCookie(cookie)));
+ prefsWriter.apply();
+ }
+
+ protected String getCookieToken(URI uri, HttpCookie cookie) {
+ return cookie.getName() + cookie.getDomain();
+ }
+
+ @Override
+ public List get(URI uri) {
+ ArrayList ret = new ArrayList<>();
+ if (cookies.containsKey(uri.getHost()))
+ ret.addAll(cookies.get(uri.getHost()).values());
+ return ret;
+ }
+
+ @Override
+ public boolean removeAll() {
+ SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
+ prefsWriter.clear();
+ prefsWriter.apply();
+ cookies.clear();
+ return true;
+ }
+
+
+ @Override
+ public boolean remove(URI uri, HttpCookie cookie) {
+ String name = getCookieToken(uri, cookie);
+
+ if (cookies.containsKey(uri.getHost()) && cookies.get(uri.getHost()).containsKey(name)) {
+ cookies.get(uri.getHost()).remove(name);
+
+ SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
+ if (cookiePrefs.contains(COOKIE_NAME_PREFIX + name)) {
+ prefsWriter.remove(COOKIE_NAME_PREFIX + name);
+ }
+ prefsWriter.putString(uri.getHost(), TextUtils.join(",", cookies.get(uri.getHost()).keySet()));
+ prefsWriter.apply();
+
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public List getCookies() {
+ ArrayList ret = new ArrayList<>();
+ for (String key : cookies.keySet())
+ ret.addAll(cookies.get(key).values());
+
+ return ret;
+ }
+
+ @Override
+ public List getURIs() {
+ ArrayList ret = new ArrayList<>();
+ for (String key : cookies.keySet())
+ try {
+ ret.add(new URI(key));
+ } catch (URISyntaxException e) {
+ e.printStackTrace();
+ }
+
+ return ret;
+ }
+
+ /**
+ * Serializes Cookie object into String
+ *
+ * @param cookie cookie to be encoded, can be null
+ * @return cookie encoded as String
+ */
+ protected String encodeCookie(SerializableHttpCookie cookie) {
+ if (cookie == null)
+ return null;
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ try {
+ ObjectOutputStream outputStream = new ObjectOutputStream(os);
+ outputStream.writeObject(cookie);
+ } catch (IOException e) {
+ Log.d(LOG_TAG, "IOException in encodeCookie", e);
+ return null;
+ }
+
+ return byteArrayToHexString(os.toByteArray());
+ }
+
+ /**
+ * Returns cookie decoded from cookie string
+ *
+ * @param cookieString string of cookie as returned from http request
+ * @return decoded cookie or null if exception occured
+ */
+ protected HttpCookie decodeCookie(String cookieString) {
+ byte[] bytes = hexStringToByteArray(cookieString);
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
+ HttpCookie cookie = null;
+ try {
+ ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
+ cookie = ((SerializableHttpCookie) objectInputStream.readObject()).getCookie();
+ } catch (IOException e) {
+ Log.d(LOG_TAG, "IOException in decodeCookie", e);
+ } catch (ClassNotFoundException e) {
+ Log.d(LOG_TAG, "ClassNotFoundException in decodeCookie", e);
+ }
+
+ return cookie;
+ }
+
+ /**
+ * Using some super basic byte array <-> hex conversions so we don't have to rely on any
+ * large Base64 libraries. Can be overridden if you like!
+ *
+ * @param bytes byte array to be converted
+ * @return string containing hex values
+ */
+ protected String byteArrayToHexString(byte[] bytes) {
+ StringBuilder sb = new StringBuilder(bytes.length * 2);
+ for (byte element : bytes) {
+ int v = element & 0xff;
+ if (v < 16) {
+ sb.append('0');
+ }
+ sb.append(Integer.toHexString(v));
+ }
+ return sb.toString().toUpperCase(Locale.US);
+ }
+
+ /**
+ * Converts hex values from strings to byte arra
+ *
+ * @param hexString string of hex-encoded values
+ * @return decoded byte array
+ */
+ protected byte[] hexStringToByteArray(String hexString) {
+ int len = hexString.length();
+ byte[] data = new byte[len / 2];
+ for (int i = 0; i < len; i += 2) {
+ data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16));
+ }
+ return data;
+ }
+}
\ No newline at end of file
diff --git a/docs-android/app/src/main/java/com/sismics/docs/resource/cookie/SerializableHttpCookie.java b/docs-android/app/src/main/java/com/sismics/docs/resource/cookie/SerializableHttpCookie.java
new file mode 100644
index 00000000..5991096e
--- /dev/null
+++ b/docs-android/app/src/main/java/com/sismics/docs/resource/cookie/SerializableHttpCookie.java
@@ -0,0 +1,55 @@
+package com.sismics.docs.resource.cookie;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.net.HttpCookie;
+
+public class SerializableHttpCookie implements Serializable {
+ private static final long serialVersionUID = 6374381323722046732L;
+
+ private transient final HttpCookie cookie;
+ private transient HttpCookie clientCookie;
+
+ public SerializableHttpCookie(HttpCookie cookie) {
+ this.cookie = cookie;
+ }
+
+ public HttpCookie getCookie() {
+ HttpCookie bestCookie = cookie;
+ if (clientCookie != null) {
+ bestCookie = clientCookie;
+ }
+ return bestCookie;
+ }
+
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ out.writeObject(cookie.getName());
+ out.writeObject(cookie.getValue());
+ out.writeObject(cookie.getComment());
+ out.writeObject(cookie.getCommentURL());
+ out.writeObject(cookie.getDomain());
+ out.writeLong(cookie.getMaxAge());
+ out.writeObject(cookie.getPath());
+ out.writeObject(cookie.getPortlist());
+ out.writeInt(cookie.getVersion());
+ out.writeBoolean(cookie.getSecure());
+ out.writeBoolean(cookie.getDiscard());
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ String name = (String) in.readObject();
+ String value = (String) in.readObject();
+ clientCookie = new HttpCookie(name, value);
+ clientCookie.setComment((String) in.readObject());
+ clientCookie.setCommentURL((String) in.readObject());
+ clientCookie.setDomain((String) in.readObject());
+ clientCookie.setMaxAge(in.readLong());
+ clientCookie.setPath((String) in.readObject());
+ clientCookie.setPortlist((String) in.readObject());
+ clientCookie.setVersion(in.readInt());
+ clientCookie.setSecure(in.readBoolean());
+ clientCookie.setDiscard(in.readBoolean());
+ }
+}
\ No newline at end of file
diff --git a/docs-android/app/src/main/java/com/sismics/docs/service/FileUploadService.java b/docs-android/app/src/main/java/com/sismics/docs/service/FileUploadService.java
index 0157c358..79903b77 100644
--- a/docs-android/app/src/main/java/com/sismics/docs/service/FileUploadService.java
+++ b/docs-android/app/src/main/java/com/sismics/docs/service/FileUploadService.java
@@ -15,12 +15,12 @@ import com.sismics.docs.event.FileAddEvent;
import com.sismics.docs.listener.JsonHttpResponseHandler;
import com.sismics.docs.resource.FileResource;
-import org.apache.http.Header;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
+import cz.msebera.android.httpclient.Header;
import de.greenrobot.event.EventBus;
/**
diff --git a/docs-android/app/src/main/java/com/sismics/docs/util/OkHttpUtil.java b/docs-android/app/src/main/java/com/sismics/docs/util/OkHttpUtil.java
new file mode 100644
index 00000000..e6756cdf
--- /dev/null
+++ b/docs-android/app/src/main/java/com/sismics/docs/util/OkHttpUtil.java
@@ -0,0 +1,191 @@
+package com.sismics.docs.util;
+
+import android.content.Context;
+import android.os.Build;
+import android.util.Log;
+
+import com.jakewharton.picasso.OkHttp3Downloader;
+import com.sismics.docs.resource.cookie.PersistentCookieStore;
+import com.squareup.picasso.Picasso;
+
+import java.io.IOException;
+import java.net.CookieManager;
+import java.net.CookiePolicy;
+import java.net.HttpCookie;
+import java.net.URI;
+import java.security.cert.CertificateException;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import okhttp3.Cache;
+import okhttp3.Interceptor;
+import okhttp3.JavaNetCookieJar;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+/**
+ * Utilities for OkHttp.
+ *
+ * @author bgamard.
+ */
+public class OkHttpUtil {
+ /**
+ * OkHttp singleton client.
+ */
+ private static OkHttpClient okHttpClient = new OkHttpClient();
+
+ /**
+ * Singleton cache.
+ */
+ private static Cache cache = null;
+
+ /**
+ * User-Agent to use.
+ */
+ protected static String userAgent = null;
+
+ /**
+ * Accept-Language header.
+ */
+ protected static String acceptLanguage = null;
+
+ static {
+ // OkHttp configuration
+ try {
+ // Create a trust manager that does not validate certificate chains
+ final TrustManager[] trustAllCerts = new TrustManager[] {
+ new X509TrustManager() {
+ @Override
+ public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
+ }
+
+ @Override
+ public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
+ }
+
+ @Override
+ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ }
+ };
+
+ // Install the all-trusting trust manager
+ final SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
+ final javax.net.ssl.SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
+
+ // Configure OkHttpClient
+ okHttpClient = okHttpClient.newBuilder()
+ .connectTimeout(30, TimeUnit.SECONDS)
+ .readTimeout(30, TimeUnit.SECONDS)
+ .writeTimeout(30, TimeUnit.SECONDS)
+ .sslSocketFactory(sslSocketFactory)
+ .build();
+ } catch (Exception e) {
+ // NOP
+ }
+ }
+
+ /**
+ * Build a Picasso object with base config.
+ *
+ * @param context Context
+ * @return Picasso object
+ */
+ public static Picasso picasso(Context context) {
+ OkHttpClient okHttpClient = buildClient(context)
+ .newBuilder()
+ .addInterceptor(new Interceptor() {
+ @Override
+ public Response intercept(Interceptor.Chain chain) throws IOException { // Override cache configuration
+ final Request original = chain.request();
+ final Request.Builder requestBuilder = original.newBuilder()
+ .header("Cache-Control", "max-age=" + (3600 * 24 * 365))
+ .method(original.method(), original.body());
+ return chain.proceed(requestBuilder.build());
+ }
+ })
+ .cache(getCache(context))
+ .build();
+
+ Picasso picasso = new Picasso.Builder(context)
+ .downloader(new OkHttp3Downloader(okHttpClient))
+ .build();
+ picasso.setIndicatorsEnabled(false); // Debug stuff
+ return picasso;
+ }
+
+ /**
+ * Get and eventually build the singleton cache.
+ *
+ * @param context Context
+ * @return Cache
+ */
+ private static Cache getCache(Context context) {
+ if (cache == null) {
+ cache = new Cache(context.getCacheDir(),
+ PreferenceUtil.getIntegerPreference(context, PreferenceUtil.PREF_CACHE_SIZE, 0) * 1000000);
+ }
+ return cache;
+ }
+
+ /**
+ * Clear the HTTP cache.
+ *
+ * @param context Context
+ */
+ public static void clearCache(Context context) {
+ Cache cache = getCache(context);
+ try {
+ cache.evictAll();
+ } catch (IOException e) {
+ Log.e("OKHttpUtil", "Error clearing cache", e);
+ }
+ }
+
+ /**
+ * Build an OkHttpClient.
+ *
+ * @param context Context
+ * @return OkHttpClient
+ */
+ public static OkHttpClient buildClient(final Context context) {
+ // One-time header computation
+ if (userAgent == null) {
+ userAgent = "Sismics Docs Android " + ApplicationUtil.getVersionName(context) + "/Android " + Build.VERSION.RELEASE + "/" + Build.MODEL;
+ }
+
+ if (acceptLanguage == null) {
+ Locale locale = Locale.getDefault();
+ acceptLanguage = locale.getLanguage() + "_" + locale.getCountry();
+ }
+
+ // Cookie handling
+ PersistentCookieStore cookieStore = new PersistentCookieStore(context);
+ CookieManager cookieManager = new CookieManager(cookieStore, CookiePolicy.ACCEPT_ALL);
+ cookieStore.add(URI.create(PreferenceUtil.getServerUrl(context)),
+ new HttpCookie("auth_token", PreferenceUtil.getAuthToken(context))); // TODO Remove me when async http is ditched
+
+ // Runtime configuration
+ return okHttpClient.newBuilder()
+ .cookieJar(new JavaNetCookieJar(cookieManager))
+ .addNetworkInterceptor(new Interceptor() {
+ @Override
+ public Response intercept(Chain chain) throws IOException {
+ Request originalRequest = chain.request();
+ return chain.proceed(originalRequest.newBuilder()
+ .header("User-Agent", userAgent)
+ .header("Accept-Language", acceptLanguage)
+ // TODO necessary?? .method(originalRequest.method(), originalRequest.body())
+ .build());
+ }
+ })
+ .build();
+ }
+}
diff --git a/docs-android/app/src/main/java/com/sismics/docs/util/PreferenceUtil.java b/docs-android/app/src/main/java/com/sismics/docs/util/PreferenceUtil.java
index 629d7714..581bc139 100644
--- a/docs-android/app/src/main/java/com/sismics/docs/util/PreferenceUtil.java
+++ b/docs-android/app/src/main/java/com/sismics/docs/util/PreferenceUtil.java
@@ -7,11 +7,12 @@ import android.preference.PreferenceManager;
import com.loopj.android.http.PersistentCookieStore;
-import org.apache.http.cookie.Cookie;
import org.json.JSONObject;
import java.util.List;
+import cz.msebera.android.httpclient.cookie.Cookie;
+
/**
* Utility class on preferences.
*
diff --git a/docs-android/app/src/main/res/drawable-xhdpi/ic_description_grey600_24dp.png b/docs-android/app/src/main/res/drawable-xhdpi/ic_description_grey600_24dp.png
new file mode 100644
index 00000000..50b1541e
Binary files /dev/null and b/docs-android/app/src/main/res/drawable-xhdpi/ic_description_grey600_24dp.png differ
diff --git a/docs-android/app/src/main/res/drawable-xxhdpi/ic_description_grey600_24dp.png b/docs-android/app/src/main/res/drawable-xxhdpi/ic_description_grey600_24dp.png
new file mode 100644
index 00000000..50f854ea
Binary files /dev/null and b/docs-android/app/src/main/res/drawable-xxhdpi/ic_description_grey600_24dp.png differ
diff --git a/docs-android/app/src/main/res/layout/document_export_pdf_dialog.xml b/docs-android/app/src/main/res/layout/document_export_pdf_dialog.xml
new file mode 100644
index 00000000..2ab84d61
--- /dev/null
+++ b/docs-android/app/src/main/res/layout/document_export_pdf_dialog.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs-android/app/src/main/res/layout/document_view_activity.xml b/docs-android/app/src/main/res/layout/document_view_activity.xml
index a0609e89..e13fd81b 100644
--- a/docs-android/app/src/main/res/layout/document_view_activity.xml
+++ b/docs-android/app/src/main/res/layout/document_view_activity.xml
@@ -207,6 +207,17 @@
android:orientation="horizontal"
style="?android:buttonBarStyle">
+
+
Delete comment
Deleting comment
Error deleting comment
+ Export PDF
+ Download
diff --git a/docs-android/gradle/wrapper/gradle-wrapper.properties b/docs-android/gradle/wrapper/gradle-wrapper.properties
index 476556cf..697e9cd9 100644
--- a/docs-android/gradle/wrapper/gradle-wrapper.properties
+++ b/docs-android/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Mon Nov 23 20:12:30 CET 2015
+#Sat Jan 16 19:15:13 CET 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
diff --git a/docs-core/pom.xml b/docs-core/pom.xml
index 3ad8e390..d5f915ab 100644
--- a/docs-core/pom.xml
+++ b/docs-core/pom.xml
@@ -113,11 +113,6 @@
bcprov-jdk15on
-
- com.levigo.jbig2
- levigo-jbig2-imageio
-
-
fr.opensagres.xdocreport
org.odftoolkit.odfdom.converter.pdf
@@ -127,16 +122,26 @@
fr.opensagres.xdocreport
org.apache.poi.xwpf.converter.pdf
-
-
+
- jna
+ net.java.dev.jna
jna
+
+
+
+ com.levigo.jbig2
+ levigo-jbig2-imageio
+
+
+
+ com.twelvemonkeys.imageio
+ imageio-jpeg
+
- jai
- imageio
+ com.github.jai-imageio
+ jai-imageio-core
diff --git a/docs-core/src/main/java/com/sismics/tess4j/ImageIOHelper.java b/docs-core/src/main/java/com/sismics/tess4j/ImageIOHelper.java
index 925f1c25..7c56a5ec 100644
--- a/docs-core/src/main/java/com/sismics/tess4j/ImageIOHelper.java
+++ b/docs-core/src/main/java/com/sismics/tess4j/ImageIOHelper.java
@@ -38,9 +38,9 @@ import javax.imageio.stream.ImageOutputStream;
import org.w3c.dom.NodeList;
-import com.sun.media.imageio.plugins.tiff.TIFFImageWriteParam;
-import com.sun.media.imageioimpl.plugins.tiff.TIFFImageReaderSpi;
-import com.sun.media.imageioimpl.plugins.tiff.TIFFImageWriterSpi;
+import com.github.jaiimageio.impl.plugins.tiff.TIFFImageReaderSpi;
+import com.github.jaiimageio.impl.plugins.tiff.TIFFImageWriterSpi;
+import com.github.jaiimageio.plugins.tiff.TIFFImageWriteParam;
public class ImageIOHelper {
@@ -51,26 +51,26 @@ public class ImageIOHelper {
* Gets pixel data of an
* IIOImage
object.
*
- * @param image an
+ * @param oimage an
* IIOImage
object
* @return a byte buffer of pixel data
* @throws Exception
*/
- public static ByteBuffer getImageByteBuffer(IIOImage image) throws IOException {
- //Set up the writeParam
- TIFFImageWriteParam tiffWriteParam = new TIFFImageWriteParam(Locale.US);
- tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_DISABLED);
-
- //Get tif writer and set output to file
+ public static ByteBuffer getImageByteBuffer(BufferedImage oimage) throws IOException {
+ // Get tif writer and set output to file
ImageWriter writer = new TIFFImageWriterSpi().createWriterInstance();
- //Get the stream metadata
+ // Set up the writeParam
+ // We are using the old JAI ImageIO plugin, because for some reason, OCR don't work with TwelveMonkeys' plugin
+ ImageWriteParam tiffWriteParam = new TIFFImageWriteParam(Locale.US);
+ tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_DISABLED);
+
+ // Get the stream metadata
IIOMetadata streamMetadata = writer.getDefaultStreamMetadata(tiffWriteParam);
-
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageOutputStream ios = ImageIO.createImageOutputStream(outputStream);
writer.setOutput(ios);
- writer.write(streamMetadata, new IIOImage(image.getRenderedImage(), null, null), tiffWriteParam);
+ writer.write(streamMetadata, new IIOImage(oimage, null, null), tiffWriteParam);
writer.dispose();
// Read the writed image
diff --git a/docs-core/src/main/java/com/sismics/tess4j/Tesseract.java b/docs-core/src/main/java/com/sismics/tess4j/Tesseract.java
index 84719470..4111ce4c 100644
--- a/docs-core/src/main/java/com/sismics/tess4j/Tesseract.java
+++ b/docs-core/src/main/java/com/sismics/tess4j/Tesseract.java
@@ -17,7 +17,6 @@ package com.sismics.tess4j;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
-import java.awt.image.RenderedImage;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@@ -25,8 +24,6 @@ import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
-import javax.imageio.IIOImage;
-
import com.sun.jna.Pointer;
/**
@@ -169,9 +166,8 @@ public class Tesseract {
* @throws TesseractException
*/
public String doOCR(BufferedImage bi, Rectangle rect) throws TesseractException {
- IIOImage oimage = new IIOImage(bi, null, null);
- List imageList = new ArrayList();
- imageList.add(oimage);
+ List imageList = new ArrayList();
+ imageList.add(bi);
return doOCR(imageList, rect);
}
@@ -179,23 +175,22 @@ public class Tesseract {
* Performs OCR operation.
*
* @param imageList a list of
- * IIOImage
objects
+ * BufferedImage
objects
* @param rect the bounding rectangle defines the region of the image to be
* recognized. A rectangle of zero dimension or
* null
indicates the whole image.
* @return the recognized text
* @throws TesseractException
*/
- public String doOCR(List imageList, Rectangle rect) throws TesseractException {
+ public String doOCR(List imageList, Rectangle rect) throws TesseractException {
StringBuilder sb = new StringBuilder();
pageNum = 0;
- for (IIOImage oimage : imageList) {
+ for (BufferedImage oimage : imageList) {
pageNum++;
try {
ByteBuffer buf = ImageIOHelper.getImageByteBuffer(oimage);
- RenderedImage ri = oimage.getRenderedImage();
- String pageText = doOCR(ri.getWidth(), ri.getHeight(), buf, rect, ri.getColorModel().getPixelSize());
+ String pageText = doOCR(oimage.getWidth(), oimage.getHeight(), buf, rect, oimage.getColorModel().getPixelSize());
sb.append(pageText);
} catch (IOException ioe) {
//skip the problematic image
diff --git a/docs-parent/lib/jai_imageio.jar b/docs-parent/lib/jai_imageio.jar
deleted file mode 100644
index 571aa199..00000000
Binary files a/docs-parent/lib/jai_imageio.jar and /dev/null differ
diff --git a/docs-parent/lib/jna.jar b/docs-parent/lib/jna.jar
deleted file mode 100644
index 0827fcfa..00000000
Binary files a/docs-parent/lib/jna.jar and /dev/null differ
diff --git a/docs-parent/pom.xml b/docs-parent/pom.xml
index cf977d0b..7dbec5a4 100644
--- a/docs-parent/pom.xml
+++ b/docs-parent/pom.xml
@@ -35,8 +35,11 @@
2.9.1
4.1.0.Final
3.1.0
- 1.6.3
1.0.5
+ 4.2.1
+ 3.2.1
+ 1.6.5
+ 1.3.1
9.2.13.v20150730
9.2.13.v20150730
@@ -69,14 +72,7 @@
true
-
-
- jbig2.googlecode
- JBIG2 ImageIO-Plugin repository at googlecode.com
- http://jbig2-imageio.googlecode.com/svn/maven-repository
-
-
@@ -369,90 +365,50 @@
- fr.opensagres.xdocreport
- org.odftoolkit.odfdom.converter.pdf
- ${fr.opensagres.xdocreport.version}
-
-
-
- fr.opensagres.xdocreport
- org.apache.poi.xwpf.converter.pdf
- ${fr.opensagres.xdocreport.version}
-
-
-
+ fr.opensagres.xdocreport
+ org.odftoolkit.odfdom.converter.pdf
+ ${fr.opensagres.xdocreport.version}
+
+
+ fr.opensagres.xdocreport
+ org.apache.poi.xwpf.converter.pdf
+ ${fr.opensagres.xdocreport.version}
+
+
+
+ com.twelvemonkeys.servlet
+ servlet
+ ${com.twelvemonkeys.imageio.version}
+
+
+
+
+ net.java.dev.jna
+ jna
+ ${net.java.dev.jna.jna.version}
+
+
+
+
+ com.twelvemonkeys.imageio
+ imageio-jpeg
+ ${com.twelvemonkeys.imageio.version}
+
+
+
com.levigo.jbig2
levigo-jbig2-imageio
${com.levigo.jbig2.levigo-jbig2-imageio.version}
-
-
- jna
- jna
- 1.0
-
-
-
- jai
- imageio
- 1.0
+
+ com.github.jai-imageio
+ jai-imageio-core
+ ${com.github.jai-imageio.jai-imageio-core.version}
-
-
- init
-
-
-
-
- org.apache.maven.plugins
- maven-install-plugin
- 2.3.1
-
-
-
- install-jna
- validate
-
- ${project.basedir}/lib/jna.jar
- default
- jna
- jna
- 1.0
- jar
- true
-
-
- install-file
-
-
-
-
- install-jai-imageio
- validate
-
- ${project.basedir}/lib/jai_imageio.jar
- default
- jai
- imageio
- 1.0
- jar
- true
-
-
- install-file
-
-
-
-
-
-
-
-
-
diff --git a/docs-web/pom.xml b/docs-web/pom.xml
index 15a7a835..744c698e 100644
--- a/docs-web/pom.xml
+++ b/docs-web/pom.xml
@@ -83,6 +83,11 @@
com.h2database
h2
+
+
+ com.twelvemonkeys.servlet
+ servlet
+
diff --git a/docs-web/src/main/webapp/WEB-INF/web.xml b/docs-web/src/main/webapp/WEB-INF/web.xml
index 0c5629fb..3b14ff94 100644
--- a/docs-web/src/main/webapp/WEB-INF/web.xml
+++ b/docs-web/src/main/webapp/WEB-INF/web.xml
@@ -7,6 +7,12 @@
metadata-complete="true">
Docs
+
+
+ ImageIO service provider loader/unloader
+ com.twelvemonkeys.servlet.image.IIOProviderContextListener
+
+
requestContextFilter
diff --git a/docs-web/src/main/webapp/src/app/share/controller/Share.js b/docs-web/src/main/webapp/src/app/share/controller/Share.js
index c85f65c8..981aea98 100644
--- a/docs-web/src/main/webapp/src/app/share/controller/Share.js
+++ b/docs-web/src/main/webapp/src/app/share/controller/Share.js
@@ -3,7 +3,7 @@
/**
* Share controller.
*/
-angular.module('share').controller('Share', function($scope, $state, $stateParams, Restangular) {
+angular.module('share').controller('Share', function($scope, $state, $stateParams, Restangular, $modal) {
// Load document
Restangular.one('document', $stateParams.documentId).get({ share: $stateParams.shareId })
.then(function (data) {
@@ -33,4 +33,16 @@ angular.module('share').controller('Share', function($scope, $state, $stateParam
$scope.openFile = function (file) {
$state.go('share.file', { documentId: $stateParams.documentId, shareId: $stateParams.shareId, fileId: file.id })
};
+
+ /**
+ * Export the current document to PDF.
+ */
+ $scope.exportPdf = function() {
+ $modal.open({
+ templateUrl: 'partial/share/share.pdf.html',
+ controller: 'ShareModalPdf'
+ });
+
+ return false;
+ };
});
\ No newline at end of file
diff --git a/docs-web/src/main/webapp/src/app/share/controller/ShareModalPdf.js b/docs-web/src/main/webapp/src/app/share/controller/ShareModalPdf.js
new file mode 100644
index 00000000..ff6b2cd9
--- /dev/null
+++ b/docs-web/src/main/webapp/src/app/share/controller/ShareModalPdf.js
@@ -0,0 +1,30 @@
+'use strict';
+
+/**
+ * Document modal PDF controller.
+ */
+angular.module('share').controller('ShareModalPdf', function ($scope, $window, $stateParams, $modalInstance) {
+ $scope.export = {
+ metadata: false,
+ comments: false,
+ fitimagetopage: true,
+ margin: 10
+ };
+
+ // Export to PDF
+ $scope.exportPdf = function() {
+ $window.open('../api/document/' + $stateParams.documentId
+ + '/pdf?metadata=' + $scope.export.metadata
+ + '&comments=' + $scope.export.comments
+ + '&fitimagetopage=' + $scope.export.fitimagetopage
+ + '&margin=' + $scope.export.margin
+ + '&share=' + $stateParams.shareId);
+
+ $modalInstance.close();
+ };
+
+ // Close the modal
+ $scope.close = function () {
+ $modalInstance.close();
+ }
+});
\ No newline at end of file
diff --git a/docs-web/src/main/webapp/src/partial/share/share.html b/docs-web/src/main/webapp/src/partial/share/share.html
index d03fe326..1f1e1d26 100644
--- a/docs-web/src/main/webapp/src/partial/share/share.html
+++ b/docs-web/src/main/webapp/src/partial/share/share.html
@@ -1,11 +1,32 @@