Compare commits
426 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea210d6aac | ||
|
|
f70cded634 | ||
|
|
cc6e1b4052 | ||
|
|
1ce5ba4f06 | ||
|
|
2b23a1d048 | ||
|
|
37f262177d | ||
|
|
7ded510625 | ||
|
|
797a987e2b | ||
|
|
062dee987f | ||
|
|
1054931e63 | ||
|
|
3720a881a4 | ||
|
|
f379b4e5ab | ||
|
|
9a9e86829e | ||
|
|
66cd4abd8b | ||
|
|
ba4470f155 | ||
|
|
b95ec019de | ||
|
|
d3a40ebca8 | ||
|
|
5d335049a2 | ||
|
|
bd51e2ab55 | ||
|
|
f8278bd44e | ||
|
|
4797cd1eca | ||
|
|
706d244ff8 | ||
|
|
5b8cd18128 | ||
|
|
8a854bb37d | ||
|
|
6e6f892cb0 | ||
|
|
2b4ddfa072 | ||
|
|
c9adff5a25 | ||
|
|
503cfff82e | ||
|
|
5e713f0c2a | ||
|
|
e035007070 | ||
|
|
17a94395f3 | ||
|
|
0ab6c8e4b0 | ||
|
|
8284169923 | ||
|
|
5f9094c540 | ||
|
|
332ac9c109 | ||
|
|
9ba49f35ff | ||
|
|
d0646f12e6 | ||
|
|
a8817c75b0 | ||
|
|
7fc50f2629 | ||
|
|
b2d9684738 | ||
|
|
8ab284ff98 | ||
|
|
a099d29524 | ||
|
|
cf44af0065 | ||
|
|
b5bf8b6545 | ||
|
|
4cc3fa4d89 | ||
|
|
ed50e202d7 | ||
|
|
6d865af15a | ||
|
|
66d331ddb8 | ||
|
|
0903c03a29 | ||
|
|
5f546b6c6d | ||
|
|
9bb73bb35b | ||
|
|
2ca5b04de2 | ||
|
|
e883c1e678 | ||
|
|
4fc434a222 | ||
|
|
ecfa747a2c | ||
|
|
dc28ebfa50 | ||
|
|
6596eba6ca | ||
|
|
7194f9aac0 | ||
|
|
e4fe1cfa90 | ||
|
|
5bc73548b3 | ||
|
|
2156848e4a | ||
|
|
3f807b3e51 | ||
|
|
d786862a60 | ||
|
|
fb75bafe96 | ||
|
|
66f781b716 | ||
|
|
b3dc409926 | ||
|
|
287ed06b6a | ||
|
|
df1d013b1c | ||
|
|
fdb95484c1 | ||
|
|
4cf1f29e0a | ||
|
|
332fd9d1f6 | ||
|
|
039d881a07 | ||
|
|
b8176a9fe9 | ||
|
|
65937d6f4c | ||
|
|
590bf74e98 | ||
|
|
642a3e10ce | ||
|
|
52a8cf92c8 | ||
|
|
fc68ee56d5 | ||
|
|
644f4803df | ||
|
|
eda6106de8 | ||
|
|
3a1691066e | ||
|
|
b02039bad4 | ||
|
|
6527a9e9bb | ||
|
|
c59ad4d446 | ||
|
|
f475cbc5d8 | ||
|
|
00452cc505 | ||
|
|
23660961bd | ||
|
|
742ff183bf | ||
|
|
dca8c28b84 | ||
|
|
46079393d5 | ||
|
|
517e4a4507 | ||
|
|
6f3ae6da9d | ||
|
|
273136ab23 | ||
|
|
e74f86e118 | ||
|
|
84d4d3b165 | ||
|
|
c355cb8bd5 | ||
|
|
2957034286 | ||
|
|
36b4fbd303 | ||
|
|
f57cf46313 | ||
|
|
244ddc7ce2 | ||
|
|
4d161aea07 | ||
|
|
cf9101d157 | ||
|
|
614c8a1d13 | ||
|
|
879ab7951d | ||
|
|
311b42ad25 | ||
|
|
0ebbbac9a6 | ||
|
|
d2f9fcdda0 | ||
|
|
cfe5690a73 | ||
|
|
a055b3ff5c | ||
|
|
18f37ec2a8 | ||
|
|
14b4e5aeec | ||
|
|
a980930e69 | ||
|
|
1b4eb70d8d | ||
|
|
1856ccc3aa | ||
|
|
3217c67ff6 | ||
|
|
54d5f1cb1b | ||
|
|
e49d002941 | ||
|
|
4822b8bf23 | ||
|
|
198a6d5665 | ||
|
|
c7b9ec3a4c | ||
|
|
f46e10e11c | ||
|
|
b9acc4ecf8 | ||
|
|
403d094a3d | ||
|
|
3b1f11e5a8 | ||
|
|
ddba06cca3 | ||
|
|
bf89af0da9 | ||
|
|
5a30164848 | ||
|
|
0c4e200900 | ||
|
|
fe8e8f041c | ||
|
|
43084d9d86 | ||
|
|
e1e1b4e278 | ||
|
|
14f8239ba3 | ||
|
|
e660a70d00 | ||
|
|
119d30bb16 | ||
|
|
5686de56e2 | ||
|
|
e0214a6a9f | ||
|
|
330de495db | ||
|
|
dcc7fe55f4 | ||
|
|
3274b4c79a | ||
|
|
cbfa4b1c41 | ||
|
|
5f7d2f2a68 | ||
|
|
e38bdbe508 | ||
|
|
c352b94b38 | ||
|
|
6b0106e385 | ||
|
|
60021e5123 | ||
|
|
76d3157247 | ||
|
|
7c24778460 | ||
|
|
8231db4a5a | ||
|
|
fe5dd5e8dc | ||
|
|
5872928812 | ||
|
|
8a8f4bb388 | ||
|
|
0e8d5fd5ec | ||
|
|
b9344149b0 | ||
|
|
5053a6852f | ||
|
|
bb3faca533 | ||
|
|
4f7fcbfdf0 | ||
|
|
87c1cc88be | ||
|
|
1d78551f4c | ||
|
|
b36d08db8e | ||
|
|
c99b1a1867 | ||
|
|
fc4380e5cc | ||
|
|
850ed7f76b | ||
|
|
0f6aa3befb | ||
|
|
ddd976162c | ||
|
|
cdd19e182b | ||
|
|
afc22a547e | ||
|
|
79ca54c5af | ||
|
|
0aacf20c16 | ||
|
|
cdfb43dbd8 | ||
|
|
35ec8b951c | ||
|
|
05bfaa0035 | ||
|
|
f5705b1153 | ||
|
|
ed1353a4eb | ||
|
|
a79922d7c9 | ||
|
|
7a7cbd570c | ||
|
|
d7865cfaf0 | ||
|
|
ead01ce1d0 | ||
|
|
8aca012c99 | ||
|
|
67a4dc63ca | ||
|
|
ce0678784b | ||
|
|
cbc4bbb818 | ||
|
|
1c558a884d | ||
|
|
d84d1428b2 | ||
|
|
b870ee8d62 | ||
|
|
ef18581e71 | ||
|
|
177bbceaf4 | ||
|
|
a13174ac4d | ||
|
|
e181b7d24b | ||
|
|
e631aa0e8a | ||
|
|
575ad75a0a | ||
|
|
394f667ab0 | ||
|
|
c695572b28 | ||
|
|
f80cf43ae8 | ||
|
|
79141edf70 | ||
|
|
b1e58396d1 | ||
|
|
b9cd113dc0 | ||
|
|
4a512af178 | ||
|
|
9506e9b8b4 | ||
|
|
6b57d29f51 | ||
|
|
3ff41d2002 | ||
|
|
f41dafe76d | ||
|
|
f2c4dde56e | ||
|
|
6f89a50fe5 | ||
|
|
3a22132363 | ||
|
|
e234440ce6 | ||
|
|
26685334a1 | ||
|
|
4d79dd7076 | ||
|
|
f5394534f7 | ||
|
|
faa66e01b6 | ||
|
|
bf4cb02de5 | ||
|
|
642b9a63d3 | ||
|
|
1ed7422171 | ||
|
|
3dd8a52f7d | ||
|
|
a55c55bbdb | ||
|
|
b851fd0ecc | ||
|
|
c8f7fe15ef | ||
|
|
73133f5ba5 | ||
|
|
eaf2e816b4 | ||
|
|
62020864ef | ||
|
|
f12e3ec663 | ||
|
|
5226df53a2 | ||
|
|
a59c67d774 | ||
|
|
1b1d5e9b4c | ||
|
|
37fc2d09bb | ||
|
|
bc94466cf7 | ||
|
|
6af7b6fce9 | ||
|
|
f2ae899938 | ||
|
|
c398a3c4f5 | ||
|
|
27027ec412 | ||
|
|
ddf9e83a9b | ||
|
|
0f661e5a34 | ||
|
|
09a53d5c4e | ||
|
|
542ab737a2 | ||
|
|
6e1276293f | ||
|
|
4e768e9103 | ||
|
|
55cdca0c7d | ||
|
|
9b52395786 | ||
|
|
50b02c800c | ||
|
|
2bdae5ea5c | ||
|
|
c49827ce25 | ||
|
|
64db701498 | ||
|
|
e16ce4b4f1 | ||
|
|
77d1e87fdb | ||
|
|
7d7adeeca0 | ||
|
|
8ad9c529b6 | ||
|
|
274512a58e | ||
|
|
ef16561272 | ||
|
|
767099b7ea | ||
|
|
98350860eb | ||
|
|
1343948d33 | ||
|
|
e616add75a | ||
|
|
b33b7115ef | ||
|
|
fb0bb62eaf | ||
|
|
5f84da61c8 | ||
|
|
6e6babd2e3 | ||
|
|
b28e08e2c7 | ||
|
|
718728a672 | ||
|
|
5de77e35dc | ||
|
|
6aef7246a0 | ||
|
|
5a41e9555e | ||
|
|
6598b585a2 | ||
|
|
a81474b40a | ||
|
|
ee159f5b36 | ||
|
|
ced64a5d1f | ||
|
|
689a4e6aae | ||
|
|
21b3ba2bf6 | ||
|
|
7be2e1b9e5 | ||
|
|
c1c2228937 | ||
|
|
3b9a66d1d8 | ||
|
|
a5ce5bf9ec | ||
|
|
43a1575187 | ||
|
|
eb5f207cc1 | ||
|
|
de3f055323 | ||
|
|
6012cdd9a5 | ||
|
|
0fab8ff935 | ||
|
|
00ee2d3bf6 | ||
|
|
c2a2e9f585 | ||
|
|
31fff7e021 | ||
|
|
0dda01269f | ||
|
|
d58b0e8f74 | ||
|
|
1bbb21c7c6 | ||
|
|
24713f54e2 | ||
|
|
5e2bd76e10 | ||
|
|
78d4b5797b | ||
|
|
ff91521a67 | ||
|
|
0525754337 | ||
|
|
ca8c525de0 | ||
|
|
1e7d2fcfd9 | ||
|
|
7e983bebb9 | ||
|
|
f927193ae9 | ||
|
|
a102bf04f4 | ||
|
|
1f6d9f0211 | ||
|
|
919948489d | ||
|
|
12efd5c11f | ||
|
|
25a2144b31 | ||
|
|
59682b5ba6 | ||
|
|
7deaeca7b5 | ||
|
|
a7a6adfa34 | ||
|
|
7f19f8c112 | ||
|
|
943465a390 | ||
|
|
2824878065 | ||
|
|
508a1230e9 | ||
|
|
0ad7ef43d5 | ||
|
|
67171e05b9 | ||
|
|
adebb7ff6d | ||
|
|
6fbcd46a76 | ||
|
|
ef3a592807 | ||
|
|
d8d01b077d | ||
|
|
831e2e60ed | ||
|
|
2d858e6e11 | ||
|
|
f9c3715d8d | ||
|
|
359f5b5f49 | ||
|
|
ed51b77b0e | ||
|
|
47082ceee9 | ||
|
|
98497f2a37 | ||
|
|
d3a74ed361 | ||
|
|
7f2f480b25 | ||
|
|
34d1422868 | ||
|
|
3248637e8c | ||
|
|
509ab82745 | ||
|
|
7f325e3eb5 | ||
|
|
e23ca4b8c1 | ||
|
|
a0f309c957 | ||
|
|
0db4f1643d | ||
|
|
cfa5888be9 | ||
|
|
bf8e0827e2 | ||
|
|
3172a5f216 | ||
|
|
456fc5b991 | ||
|
|
d9509474b0 | ||
|
|
e7a289ffb5 | ||
|
|
b9a4f0f1e0 | ||
|
|
0f4e5a8f6d | ||
|
|
83e1191a8a | ||
|
|
ad1e57316f | ||
|
|
2c791f5123 | ||
|
|
25a17ae2da | ||
|
|
0591f8a39f | ||
|
|
eb61b06784 | ||
|
|
0d1a4ec7ea | ||
|
|
5f82752416 | ||
|
|
332de409b8 | ||
|
|
737b3299ff | ||
|
|
24d8784e1b | ||
|
|
7708f61343 | ||
|
|
1a37d97a61 | ||
|
|
046984a447 | ||
|
|
1934bb71f0 | ||
|
|
5f516047bd | ||
|
|
e930ce4d47 | ||
|
|
3dbdf88124 | ||
|
|
b3ef9b0476 | ||
|
|
d428ce162b | ||
|
|
bc323e9945 | ||
|
|
ef69feeae7 | ||
|
|
8477920475 | ||
|
|
e36143b61c | ||
|
|
aa97253ec7 | ||
|
|
0fab0e4fc0 | ||
|
|
90a4949d76 | ||
|
|
1466fb4d6c | ||
|
|
d41172abb6 | ||
|
|
24ca81e91c | ||
|
|
1cae964c09 | ||
|
|
dd671795e6 | ||
|
|
2948c0c860 | ||
|
|
978fbf2cf9 | ||
|
|
60ee000b6c | ||
|
|
634ab7ec38 | ||
|
|
f98a12b96f | ||
|
|
7e5aa9aecf | ||
|
|
1c7381376c | ||
|
|
c7ce42fb3f | ||
|
|
fc3a8bb4ae | ||
|
|
bee8a4fcdc | ||
|
|
82b39586f0 | ||
|
|
c365c6f6e0 | ||
|
|
9afd52108b | ||
|
|
97252bb5da | ||
|
|
7eeaeb01a0 | ||
|
|
b3e44b84d2 | ||
|
|
f984595b97 | ||
|
|
af23cd4948 | ||
|
|
dc05ca0484 | ||
|
|
f94e069792 | ||
|
|
f01d78a9ea | ||
|
|
cd32f452e9 | ||
|
|
66cb7333c1 | ||
|
|
08633a993d | ||
|
|
2c782a23d8 | ||
|
|
c7b7527183 | ||
|
|
80bd11b44e | ||
|
|
99a596b2e1 | ||
|
|
cfde218d32 | ||
|
|
d8cefddebd | ||
|
|
50c7066f88 | ||
|
|
97f25de0dc | ||
|
|
a95dcf488d | ||
|
|
0fe51d355c | ||
|
|
97694d5d59 | ||
|
|
3d1b5a7394 | ||
|
|
e72fe3683c | ||
|
|
df1eaf54c8 | ||
|
|
44c10b60cd | ||
|
|
467d14bacb | ||
|
|
6d73554967 | ||
|
|
36b5bf3bb2 | ||
|
|
d14db1d3fb | ||
|
|
9c97ab14f8 | ||
|
|
6558ff7e05 | ||
|
|
86473d5639 | ||
|
|
374310d13c | ||
|
|
4396ef83a3 | ||
|
|
6b9ef4ab31 | ||
|
|
3851408100 | ||
|
|
794c5012ad | ||
|
|
1ba9f7d7d9 | ||
|
|
9b4330d618 | ||
|
|
3a32b742e8 | ||
|
|
87b3d25c0f | ||
|
|
9f28649a3a | ||
|
|
8f9df8961b | ||
|
|
9e9217bfcb | ||
|
|
ffdd5a3631 | ||
|
|
db4bf8d35a | ||
|
|
7b2859f96e | ||
|
|
03c5c33ea7 |
6
.gitignore
vendored
@@ -5,7 +5,9 @@
|
||||
/*/gen
|
||||
/*/target
|
||||
/*/build
|
||||
/*/*.iml
|
||||
/out
|
||||
/.idea
|
||||
/.project
|
||||
/.project
|
||||
*.iml
|
||||
node_modules
|
||||
import_test
|
||||
|
||||
24
.travis.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
sudo: required
|
||||
dist: trusty
|
||||
language: java
|
||||
before_install:
|
||||
- sudo apt-get -qq update
|
||||
- sudo apt-get -y -q install tesseract-ocr tesseract-ocr-fra tesseract-ocr-ita tesseract-ocr-kor tesseract-ocr-rus tesseract-ocr-ukr tesseract-ocr-spa tesseract-ocr-ara tesseract-ocr-hin tesseract-ocr-deu tesseract-ocr-pol tesseract-ocr-jpn tesseract-ocr-por tesseract-ocr-tha tesseract-ocr-jpn tesseract-ocr-chi-sim tesseract-ocr-chi-tra
|
||||
- sudo apt-get -y -q install haveged && sudo service haveged start
|
||||
after_success:
|
||||
- mvn -Pprod -DskipTests clean install
|
||||
- docker login -u $DOCKER_USER -p $DOCKER_PASS
|
||||
- export REPO=sismics/docs
|
||||
- export TAG=`if [ "$TRAVIS_BRANCH" == "master" ]; then echo "latest"; else echo $TRAVIS_BRANCH ; fi`
|
||||
- docker build -f Dockerfile -t $REPO:$COMMIT .
|
||||
- docker tag $REPO:$COMMIT $REPO:$TAG
|
||||
- docker tag $REPO:$COMMIT $REPO:travis-$TRAVIS_BUILD_NUMBER
|
||||
- docker push $REPO
|
||||
env:
|
||||
global:
|
||||
- TESSDATA_PREFIX=/usr/share/tesseract-ocr
|
||||
- LC_NUMERIC=C
|
||||
- secure: LRGpjWORb0qy6VuypZjTAfA8uRHlFUMTwb77cenS9PPRBxuSnctC531asS9Xg3DqC5nsRxBBprgfCKotn5S8nBSD1ceHh84NASyzLSBft3xSMbg7f/2i7MQ+pGVwLncusBU6E/drnMFwZBleo+9M8Tf96axY5zuUp90MUTpSgt0=
|
||||
- secure: bCDDR6+I7PmSkuTYZv1HF/z98ANX/SFEESUCqxVmV5Gs0zFC0vQXaPJQ2xaJNRop1HZBFMZLeMMPleb0iOs985smpvK2F6Rbop9Tu+Vyo0uKqv9tbZ7F8Nfgnv9suHKZlL84FNeUQZJX6vsFIYPEJ/r7K5P/M0PdUy++fEwxEhU=
|
||||
- secure: ewXnzbkgCIHpDWtaWGMa1OYZJ/ki99zcIl4jcDPIC0eB3njX/WgfcC6i0Ke9mLqDqwXarWJ6helm22sNh+xtQiz6isfBtBX+novfRt9AANrBe3koCMUemMDy7oh5VflBaFNP0DVb8LSCnwf6dx6ZB5E9EB8knvk40quc/cXpGjY=
|
||||
- COMMIT=${TRAVIS_COMMIT::8}
|
||||
@@ -1,10 +1,11 @@
|
||||
FROM sismics/debian-java7-jetty9
|
||||
MAINTAINER benjamin.gam@gmail.com
|
||||
FROM sismics/jetty:9.2.20-jdk7
|
||||
MAINTAINER b.gamard@sismics.com
|
||||
|
||||
RUN apt-get -y -q install tesseract-ocr tesseract-ocr-fra tesseract-ocr-jpn
|
||||
RUN apt-get update && apt-get -y -q install tesseract-ocr tesseract-ocr-fra tesseract-ocr-ita tesseract-ocr-kor tesseract-ocr-rus tesseract-ocr-ukr tesseract-ocr-spa tesseract-ocr-ara tesseract-ocr-hin tesseract-ocr-deu tesseract-ocr-pol tesseract-ocr-jpn tesseract-ocr-por tesseract-ocr-tha tesseract-ocr-jpn tesseract-ocr-chi-sim tesseract-ocr-chi-tra && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV TESSDATA_PREFIX /usr/share/tesseract-ocr
|
||||
ENV LC_NUMERIC C
|
||||
|
||||
ADD docs-web/target/docs-web-*.war /opt/jetty/webapps/docs.war
|
||||
ADD docs.xml /opt/jetty/webapps/docs.xml
|
||||
ADD docs-web/target/docs-web-*.war /opt/jetty/webapps/docs.war
|
||||
|
||||
70
README.md
@@ -1,41 +1,69 @@
|
||||
Sismics Docs
|
||||
============
|
||||
<h3 align="center">
|
||||
<img src="https://www.sismicsdocs.com/img/github-title.png" alt="Sismics Docs" width=500 />
|
||||
</h3>
|
||||
|
||||
_Web interface_
|
||||
[](https://twitter.com/sismicsdocs)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](http://travis-ci.org/sismics/docs)
|
||||
|
||||

|
||||
Docs is an open source, lightweight document management system for individuals and businesses.
|
||||
|
||||
_Android application_
|
||||
<hr />
|
||||
<h2 align="center">
|
||||
✨ We just launched a Cloud version of Sismics Docs! Head to <a href="https://www.sismicsdocs.com/">sismicsdocs.com</a> for more informations ✨
|
||||
</h2>
|
||||
<hr />
|
||||
|
||||
  
|
||||

|
||||
|
||||
What is Docs?
|
||||
---------------
|
||||
Demo
|
||||
----
|
||||
|
||||
Docs is an open source, lightweight document management system.
|
||||
|
||||
Docs is written in Java, and may be run on any operating system with Java support.
|
||||
A demo is available at [demo.sismicsdocs.com](https://demo.sismicsdocs.com)
|
||||
- Guest login is enabled with read access on all documents
|
||||
- "admin" login with "admin" password
|
||||
- "demo" login with "password" password
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- Responsive user interface
|
||||
- Optical character recognition
|
||||
- Support image and PDF files
|
||||
- Support image, PDF, ODT and DOCX files
|
||||
- Flexible search engine
|
||||
- Full text search in image and PDF
|
||||
- SHA-256 encryption
|
||||
- Tag system
|
||||
- Multi-users ACL system
|
||||
- Full text search in all supported files
|
||||
- All [Dublin Core](http://dublincore.org/) metadata
|
||||
- Workflow system 
|
||||
- 256-bit AES encryption of stored files
|
||||
- Tag system with nesting
|
||||
- Import document from email (EML format) 
|
||||
- Automatic inbox scanning and importing 
|
||||
- User/group permission system
|
||||
- 2-factor authentication
|
||||
- Hierarchical groups
|
||||
- Audit log
|
||||
- Comments
|
||||
- Storage quota per user
|
||||
- Document sharing by URL
|
||||
- RESTful Web API
|
||||
- Modern Android client
|
||||
- Fully featured Android client
|
||||
- [Mass files importer](https://github.com/sismics/docs/tree/master/docs-importer) (single or scan mode) 
|
||||
- Tested to 100k documents
|
||||
|
||||
Download
|
||||
--------
|
||||
|
||||
The latest release is downloadable here: <https://github.com/sismics/docs/releases> in WAR format.
|
||||
You will need a Java webapp server to run it, like [Jetty](http://eclipse.org/jetty/) or [Tomcat](http://tomcat.apache.org/)
|
||||
You will need a Java webapp server to run it, like [Jetty](http://eclipse.org/jetty/) or [Tomcat](http://tomcat.apache.org/).
|
||||
The default admin password is "admin". Don't forget to change it before going to production.
|
||||
|
||||
Install with Docker
|
||||
-------------------
|
||||
|
||||
From a Docker host, run this command to download and install Sismics Docs. The server will run on <http://[your-docker-host-ip]:8100>.
|
||||
The default admin password is "admin". Don't forget to change it before going to production.
|
||||
|
||||
docker run --rm --name sismics_docs_latest -d -p 8100:8080 -v sismics_docs_latest:/data sismics/docs:latest
|
||||
|
||||
How to build Docs from the sources
|
||||
----------------------------------
|
||||
@@ -44,7 +72,6 @@ Prerequisites: JDK 7 with JCE, Maven 3, Tesseract 3.02
|
||||
|
||||
Docs is organized in several Maven modules:
|
||||
|
||||
- docs-parent
|
||||
- docs-core
|
||||
- docs-web
|
||||
- docs-web-common
|
||||
@@ -54,9 +81,8 @@ or download the sources from GitHub.
|
||||
|
||||
#### Launch the build
|
||||
|
||||
From the `docs-parent` directory:
|
||||
From the root directory:
|
||||
|
||||
mvn -Pinit validate -N
|
||||
mvn clean -DskipTests install
|
||||
|
||||
#### Run a stand-alone version
|
||||
@@ -71,7 +97,7 @@ From the `docs-web` directory:
|
||||
|
||||
mvn -Pprod -DskipTests clean install
|
||||
|
||||
You will get your deployable WAR in the `target` directory.
|
||||
You will get your deployable WAR in the `docs-web/target` directory.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="docs-android" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="android-gradle" name="Android-Gradle">
|
||||
<configuration>
|
||||
<option name="GRADLE_PROJECT_PATH" value=":app" />
|
||||
</configuration>
|
||||
</facet>
|
||||
<facet type="android" name="Android">
|
||||
<configuration>
|
||||
<option name="SELECTED_BUILD_VARIANT" value="debug" />
|
||||
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
|
||||
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
|
||||
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
|
||||
<option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" />
|
||||
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
|
||||
<option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
|
||||
<option name="TEST_SOURCE_GEN_TASK_NAME" value="generateDebugAndroidTestSources" />
|
||||
<option name="ALLOW_USER_CONFIGURATION" value="false" />
|
||||
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
|
||||
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
|
||||
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
|
||||
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="false">
|
||||
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
|
||||
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/androidTest/debug" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/apk" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/assets" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/classes" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/dependency-cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/dex" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/incremental" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/22.1.1/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/22.0.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/22.1.1/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.shamanland/fab/0.0.6/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/it.sephiroth.android.library.easing/android-easing/1.0.3/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/it.sephiroth.android.library.imagezoom/imagezoom/1.0.5/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/libs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/manifests" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/ndk" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/pre-dexed" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/res" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/rs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/source" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/symbols" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Android API 22 Platform" jdkType="Android SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" exported="" name="appcompat-v7-22.1.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="fab-0.0.6" level="project" />
|
||||
<orderEntry type="library" exported="" name="android-easing-1.0.3" level="project" />
|
||||
<orderEntry type="library" exported="" name="imagezoom-1.0.5" level="project" />
|
||||
<orderEntry type="library" exported="" name="eventbus-2.4.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="android-query.0.26.8" level="project" />
|
||||
<orderEntry type="library" exported="" name="tokenautocomplete-1.2.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-v4-22.1.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-annotations-22.1.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="recyclerview-v7-22.0.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="android-async-http-1.4.6" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -1,46 +1,27 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:1.2.3'
|
||||
classpath 'com.android.tools.build:gradle:3.0.1'
|
||||
}
|
||||
}
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
google()
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 22
|
||||
buildToolsVersion "22.0.1"
|
||||
compileSdkVersion 26
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 22
|
||||
targetSdkVersion 26
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
storeFile file(System.getenv("TRACKINO_STORE_PATH"))
|
||||
storePassword System.getenv("TRACKINO_STORE_PASS")
|
||||
keyAlias System.getenv("TRACKINO_STORE_ALIAS")
|
||||
keyPassword System.getenv("TRACKINO_STORE_KEYPASS")
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
versionName '1.0'
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
@@ -50,10 +31,13 @@ android {
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: '*.jar')
|
||||
compile 'com.android.support:appcompat-v7:22.1.1'
|
||||
compile 'com.android.support:recyclerview-v7:22.0.0'
|
||||
compile 'com.loopj.android:android-async-http:1.4.6'
|
||||
compile 'com.android.support:appcompat-v7:26.1.0'
|
||||
compile 'com.android.support:recyclerview-v7:26.1.0'
|
||||
compile 'com.android.support:design:26.1.0'
|
||||
compile 'it.sephiroth.android.library.imagezoom:imagezoom:1.0.5'
|
||||
compile 'de.greenrobot:eventbus:2.4.0'
|
||||
compile 'com.shamanland:fab:0.0.6'
|
||||
compile 'org.greenrobot:eventbus:3.0.0'
|
||||
compile 'com.squareup.picasso:picasso:2.5.2'
|
||||
compile 'com.squareup.okhttp3:okhttp:3.7.0'
|
||||
compile 'com.squareup.okhttp3:okhttp-urlconnection:3.4.0'
|
||||
compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.sismics.docs" >
|
||||
package="com.sismics.docs"
|
||||
android:installLocation="auto">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
@@ -47,6 +48,16 @@
|
||||
android:name=".activity.DocumentEditActivity"
|
||||
android:label="@string/new_document">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.AuditLogActivity"
|
||||
android:label="@string/latest_activity">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.UserProfileActivity">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.GroupProfileActivity">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.SettingsActivity"
|
||||
android:label="@string/settings">
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -20,14 +19,8 @@ public class MainApplication extends Application {
|
||||
JSONObject json = PreferenceUtil.getCachedJson(getApplicationContext(), PreferenceUtil.PREF_CACHED_USER_INFO_JSON);
|
||||
ApplicationContext.getInstance().setUserInfo(getApplicationContext(), json);
|
||||
|
||||
// TODO google docs app: right drawer with all actions, with acls, with deep metadatas
|
||||
// TODO Provide documents to intent action get content
|
||||
|
||||
super.onCreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLowMemory() {
|
||||
BitmapAjaxCallback.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.sismics.docs.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.adapter.AuditLogListAdapter;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.model.application.ApplicationContext;
|
||||
import com.sismics.docs.resource.AuditLogResource;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Audit log activity.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class AuditLogActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Check if logged in
|
||||
if (!ApplicationContext.getInstance().isLoggedIn()) {
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle activity context
|
||||
if (getIntent() == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Input document ID (optional)
|
||||
final String documentId = getIntent().getStringExtra("documentId");
|
||||
|
||||
// Setup the activity
|
||||
setContentView(R.layout.auditlog_activity);
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setHomeButtonEnabled(true);
|
||||
}
|
||||
|
||||
// Configure the swipe refresh layout
|
||||
SwipeRefreshLayout swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
|
||||
swipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright,
|
||||
android.R.color.holo_green_light,
|
||||
android.R.color.holo_orange_light,
|
||||
android.R.color.holo_red_light);
|
||||
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
refreshView(documentId);
|
||||
}
|
||||
});
|
||||
|
||||
// Navigate to user profile on click
|
||||
final ListView auditLogListView = (ListView) findViewById(R.id.auditLogListView);
|
||||
auditLogListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (auditLogListView.getAdapter() == null) {
|
||||
return;
|
||||
}
|
||||
AuditLogListAdapter adapter = (AuditLogListAdapter) auditLogListView.getAdapter();
|
||||
String username = adapter.getItem(position).optString("username");
|
||||
Intent intent = new Intent(AuditLogActivity.this, UserProfileActivity.class);
|
||||
intent.putExtra("username", username);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
// Get audit log list
|
||||
refreshView(documentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the view.
|
||||
*/
|
||||
private void refreshView(String documentId) {
|
||||
final SwipeRefreshLayout swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
|
||||
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
|
||||
final ListView auditLogListView = (ListView) findViewById(R.id.auditLogListView);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
auditLogListView.setVisibility(View.GONE);
|
||||
AuditLogResource.list(this, documentId, new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(JSONObject response) {
|
||||
auditLogListView.setAdapter(new AuditLogListAdapter(response.optJSONArray("logs")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
auditLogListView.setVisibility(View.VISIBLE);
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.sismics.docs.activity;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
@@ -17,7 +16,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 +24,7 @@ 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.greenrobot.eventbus.EventBus;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@@ -36,8 +35,6 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Document edition activity.
|
||||
*
|
||||
@@ -104,7 +101,7 @@ public class DocumentEditActivity extends AppCompatActivity {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
JSONArray tagArray = tags.optJSONArray("stats");
|
||||
JSONArray tagArray = tags.optJSONArray("tags");
|
||||
|
||||
List<JSONObject> tagList = new ArrayList<>();
|
||||
for (int i = 0; i < tagArray.length(); i++) {
|
||||
@@ -124,7 +121,7 @@ public class DocumentEditActivity extends AppCompatActivity {
|
||||
} else {
|
||||
setTitle(R.string.edit_document);
|
||||
titleEditText.setText(document.optString("title"));
|
||||
descriptionEditText.setText(document.optString("description"));
|
||||
descriptionEditText.setText(document.isNull("description") ? "" : document.optString("description"));
|
||||
datePickerView.setDate(new Date(document.optLong("create_date")));
|
||||
languageSpinner.setSelection(languageAdapter.getItemPosition(document.optString("language")));
|
||||
JSONArray documentTags = document.optJSONArray("tags");
|
||||
@@ -165,18 +162,12 @@ public class DocumentEditActivity extends AppCompatActivity {
|
||||
// Cancellable progress dialog
|
||||
final ProgressDialog progressDialog = ProgressDialog.show(this,
|
||||
getString(R.string.please_wait),
|
||||
getString(R.string.document_editing_message), true, true,
|
||||
new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
DocumentResource.cancel(DocumentEditActivity.this);
|
||||
}
|
||||
});
|
||||
getString(R.string.document_editing_message), true, true);
|
||||
|
||||
// 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 +202,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();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
package com.sismics.docs.activity;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.DownloadManager;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.ClipData;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.view.GravityCompat;
|
||||
import android.support.v4.view.ViewPager;
|
||||
@@ -19,11 +16,16 @@ import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateFormat;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
@@ -31,22 +33,30 @@ import android.widget.Toast;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.adapter.AclListAdapter;
|
||||
import com.sismics.docs.adapter.CommentListAdapter;
|
||||
import com.sismics.docs.adapter.FilePagerAdapter;
|
||||
import com.sismics.docs.event.CommentAddEvent;
|
||||
import com.sismics.docs.event.CommentDeleteEvent;
|
||||
import com.sismics.docs.event.DocumentDeleteEvent;
|
||||
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.JsonHttpResponseHandler;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.model.application.ApplicationContext;
|
||||
import com.sismics.docs.resource.CommentResource;
|
||||
import com.sismics.docs.resource.DocumentResource;
|
||||
import com.sismics.docs.resource.FileResource;
|
||||
import com.sismics.docs.service.FileUploadService;
|
||||
import com.sismics.docs.util.NetworkUtil;
|
||||
import com.sismics.docs.util.PreferenceUtil;
|
||||
import com.sismics.docs.util.TagUtil;
|
||||
import com.sismics.docs.util.SpannableUtil;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@@ -55,8 +65,6 @@ import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Document activity.
|
||||
*
|
||||
@@ -68,11 +76,6 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
*/
|
||||
public static final int REQUEST_CODE_ADD_FILE = 1;
|
||||
|
||||
/**
|
||||
* Request code of editing document.
|
||||
*/
|
||||
public static final int REQUEST_CODE_EDIT_DOCUMENT = 2;
|
||||
|
||||
/**
|
||||
* File view pager.
|
||||
*/
|
||||
@@ -83,6 +86,11 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
*/
|
||||
private FilePagerAdapter filePagerAdapter;
|
||||
|
||||
/**
|
||||
* Comment list adapter.
|
||||
*/
|
||||
private CommentListAdapter commentListAdapter;
|
||||
|
||||
/**
|
||||
* Document displayed.
|
||||
*/
|
||||
@@ -142,7 +150,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
*
|
||||
* @param document Document in JSON format
|
||||
*/
|
||||
private void refreshDocument(JSONObject document) {
|
||||
private void refreshDocument(final JSONObject document) {
|
||||
this.document = document;
|
||||
|
||||
String title = document.optString("title");
|
||||
@@ -164,28 +172,33 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
// Fill the layout
|
||||
// Create date
|
||||
TextView createdDateTextView = (TextView) findViewById(R.id.createdDateTextView);
|
||||
createdDateTextView.setText(date);
|
||||
|
||||
// Description
|
||||
TextView descriptionTextView = (TextView) findViewById(R.id.descriptionTextView);
|
||||
if (description == null || description.isEmpty()) {
|
||||
if (description.isEmpty() || document.isNull("description")) {
|
||||
descriptionTextView.setVisibility(View.GONE);
|
||||
} else {
|
||||
descriptionTextView.setVisibility(View.VISIBLE);
|
||||
descriptionTextView.setText(description);
|
||||
}
|
||||
|
||||
// Tags
|
||||
TextView tagTextView = (TextView) findViewById(R.id.tagTextView);
|
||||
if (tags.length() == 0) {
|
||||
tagTextView.setVisibility(View.GONE);
|
||||
} else {
|
||||
tagTextView.setVisibility(View.VISIBLE);
|
||||
tagTextView.setText(TagUtil.buildSpannable(tags));
|
||||
tagTextView.setText(SpannableUtil.buildSpannableTags(tags));
|
||||
}
|
||||
|
||||
// Language
|
||||
ImageView languageImageView = (ImageView) findViewById(R.id.languageImageView);
|
||||
languageImageView.setImageResource(getResources().getIdentifier(language, "drawable", getPackageName()));
|
||||
|
||||
// Shared status
|
||||
ImageView sharedImageView = (ImageView) findViewById(R.id.sharedImageView);
|
||||
sharedImageView.setVisibility(shared ? View.VISIBLE : View.GONE);
|
||||
|
||||
@@ -196,7 +209,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
public void onClick(View view) {
|
||||
Intent intent = new Intent(DocumentViewActivity.this, DocumentEditActivity.class);
|
||||
intent.putExtra("document", DocumentViewActivity.this.document.toString());
|
||||
startActivityForResult(intent, REQUEST_CODE_EDIT_DOCUMENT);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -231,20 +244,75 @@ 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(
|
||||
document.optString("id"), document.optString("title"));
|
||||
dialog.show(getSupportFragmentManager(), "DocExportPdfFragment");
|
||||
}
|
||||
});
|
||||
|
||||
// Action share
|
||||
button = (Button) findViewById(R.id.actionSharing);
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
DialogFragment dialog = DocShareFragment.newInstance(DocumentViewActivity.this.document.optString("id"));
|
||||
DialogFragment dialog = DocShareFragment.newInstance(document.optString("id"));
|
||||
dialog.show(getSupportFragmentManager(), "DocShareFragment");
|
||||
}
|
||||
});
|
||||
|
||||
// Action audit log
|
||||
button = (Button) findViewById(R.id.actionAuditLog);
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Intent intent = new Intent(DocumentViewActivity.this, AuditLogActivity.class);
|
||||
intent.putExtra("documentId", document.optString("id"));
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
// Button add a comment
|
||||
ImageButton imageButton = (ImageButton) findViewById(R.id.addCommentBtn);
|
||||
imageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
final EditText commentEditText = (EditText) findViewById(R.id.commentEditText);
|
||||
if (commentEditText.getText().length() == 0) {
|
||||
// No content for the new comment
|
||||
return;
|
||||
}
|
||||
|
||||
Toast.makeText(DocumentViewActivity.this, R.string.adding_comment, Toast.LENGTH_LONG).show();
|
||||
|
||||
CommentResource.add(DocumentViewActivity.this,
|
||||
DocumentViewActivity.this.document.optString("id"),
|
||||
commentEditText.getText().toString(),
|
||||
new HttpCallback() {
|
||||
public void onSuccess(JSONObject response) {
|
||||
EventBus.getDefault().post(new CommentAddEvent(response));
|
||||
commentEditText.setText("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
Toast.makeText(DocumentViewActivity.this, R.string.comment_add_failure, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Grab the comments
|
||||
updateComments();
|
||||
|
||||
// Grab the attached files
|
||||
updateFiles();
|
||||
|
||||
// Grab the full document (used for ACLs and writable status)
|
||||
// Grab the full document (used for ACLs, remaining metadata and writable status)
|
||||
updateDocument();
|
||||
}
|
||||
|
||||
@@ -268,6 +336,15 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
}
|
||||
return true;
|
||||
|
||||
case R.id.comments:
|
||||
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
|
||||
if (drawerLayout.isDrawerVisible(GravityCompat.START)) {
|
||||
drawerLayout.closeDrawer(GravityCompat.START);
|
||||
} else {
|
||||
drawerLayout.openDrawer(GravityCompat.START);
|
||||
}
|
||||
return true;
|
||||
|
||||
case R.id.download_file:
|
||||
downloadCurrentFile();
|
||||
return true;
|
||||
@@ -299,11 +376,11 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
int position = fileViewPager.getCurrentItem();
|
||||
if (mimeType == null || !mimeType.contains("/")) return;
|
||||
String ext = mimeType.split("/")[1];
|
||||
String fileName = getTitle() + "-" + position + "." + ext;
|
||||
String fileName = document.optString("title") + "-" + position + "." + ext;
|
||||
|
||||
// Download the file
|
||||
String fileUrl = PreferenceUtil.getServerUrl(this) + "/api/file/" + file.optString("id") + "/data";
|
||||
downloadFile(fileUrl, fileName, getTitle().toString(), getString(R.string.downloading_file, position + 1));
|
||||
NetworkUtil.downloadFile(this, fileUrl, fileName, document.optString("title"), getString(R.string.download_file_title));
|
||||
}
|
||||
|
||||
private void deleteCurrentFile() {
|
||||
@@ -326,24 +403,18 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
// Show a progress dialog while deleting
|
||||
final ProgressDialog progressDialog = ProgressDialog.show(DocumentViewActivity.this,
|
||||
getString(R.string.please_wait),
|
||||
getString(R.string.file_deleting_message), true, true,
|
||||
new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
FileResource.cancel(DocumentViewActivity.this);
|
||||
}
|
||||
});
|
||||
getString(R.string.file_deleting_message), true, true);
|
||||
|
||||
// Actual delete server call
|
||||
final String fileId = file.optString("id");
|
||||
FileResource.delete(DocumentViewActivity.this, fileId, new JsonHttpResponseHandler() {
|
||||
FileResource.delete(DocumentViewActivity.this, fileId, new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||
public void onSuccess(JSONObject response) {
|
||||
EventBus.getDefault().post(new FileDeleteEvent(fileId));
|
||||
}
|
||||
|
||||
@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.file_delete_failure, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
@@ -368,28 +439,8 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
private void downloadZip() {
|
||||
if (document == null) return;
|
||||
String url = PreferenceUtil.getServerUrl(this) + "/api/file/zip?id=" + document.optString("id");
|
||||
String fileName = getTitle() + ".zip";
|
||||
downloadFile(url, fileName, getTitle().toString(), getString(R.string.downloading_document));
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a file using Android download manager.
|
||||
*
|
||||
* @param url URL to download
|
||||
* @param fileName Destination file name
|
||||
* @param title Notification title
|
||||
* @param description Notification description
|
||||
*/
|
||||
private void downloadFile(String url, String fileName, String title, String description) {
|
||||
String authToken = PreferenceUtil.getAuthToken(this);
|
||||
DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
|
||||
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
||||
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
|
||||
request.addRequestHeader("Cookie", "auth_token=" + authToken);
|
||||
request.setTitle(title);
|
||||
request.setDescription(description);
|
||||
downloadManager.enqueue(request);
|
||||
String fileName = document.optString("title") + ".zip";
|
||||
NetworkUtil.downloadFile(this, url, fileName, document.optString("title"), getString(R.string.download_document_title));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -411,24 +462,18 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
// Show a progress dialog while deleting
|
||||
final ProgressDialog progressDialog = ProgressDialog.show(DocumentViewActivity.this,
|
||||
getString(R.string.please_wait),
|
||||
getString(R.string.document_deleting_message), true, true,
|
||||
new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
DocumentResource.cancel(DocumentViewActivity.this);
|
||||
}
|
||||
});
|
||||
getString(R.string.document_deleting_message), true, true);
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
@@ -453,6 +498,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
*
|
||||
* @param event Document fullscreen event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(DocumentFullscreenEvent event) {
|
||||
findViewById(R.id.detailLayout).setVisibility(event.isFullscreen() ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
@@ -462,6 +508,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
*
|
||||
* @param event Document edit event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(DocumentEditEvent event) {
|
||||
if (document == null) return;
|
||||
if (event.getDocument().optString("id").equals(document.optString("id"))) {
|
||||
@@ -475,6 +522,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
*
|
||||
* @param event Document delete event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(DocumentDeleteEvent event) {
|
||||
if (document == null) return;
|
||||
if (event.getDocumentId().equals(document.optString("id"))) {
|
||||
@@ -488,6 +536,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
*
|
||||
* @param event File delete event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(FileDeleteEvent event) {
|
||||
if (filePagerAdapter == null) return;
|
||||
filePagerAdapter.remove(event.getFileId());
|
||||
@@ -500,6 +549,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
*
|
||||
* @param event File add event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(FileAddEvent event) {
|
||||
if (document == null) return;
|
||||
if (document.optString("id").equals(event.getDocumentId())) {
|
||||
@@ -507,6 +557,38 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A comment add event has been fired.
|
||||
*
|
||||
* @param event Comment add event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(CommentAddEvent event) {
|
||||
if (commentListAdapter == null) return;
|
||||
TextView emptyView = (TextView) findViewById(R.id.commentEmptyView);
|
||||
ListView listView = (ListView) findViewById(R.id.commentListView);
|
||||
emptyView.setVisibility(View.GONE);
|
||||
listView.setVisibility(View.VISIBLE);
|
||||
commentListAdapter.add(event.getComment());
|
||||
}
|
||||
|
||||
/**
|
||||
* A comment delete event has been fired.
|
||||
*
|
||||
* @param event Comment add event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(CommentDeleteEvent event) {
|
||||
if (commentListAdapter == null) return;
|
||||
TextView emptyView = (TextView) findViewById(R.id.commentEmptyView);
|
||||
ListView listView = (ListView) findViewById(R.id.commentListView);
|
||||
commentListAdapter.remove(event.getCommentId());
|
||||
if (commentListAdapter.getCount() == 0) {
|
||||
emptyView.setVisibility(View.VISIBLE);
|
||||
listView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (document == null) return;
|
||||
@@ -550,9 +632,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");
|
||||
|
||||
@@ -560,14 +642,171 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
menu.findItem(R.id.delete_file).setVisible(writable);
|
||||
}
|
||||
|
||||
findViewById(R.id.actionEditDocument).setVisibility(writable ? View.VISIBLE : View.INVISIBLE);
|
||||
findViewById(R.id.actionUploadFile).setVisibility(writable ? View.VISIBLE : View.INVISIBLE);
|
||||
findViewById(R.id.actionSharing).setVisibility(writable ? View.VISIBLE : View.INVISIBLE);
|
||||
findViewById(R.id.actionDelete).setVisibility(writable ? View.VISIBLE : View.INVISIBLE);
|
||||
// Action only available if the document is writable
|
||||
findViewById(R.id.actionEditDocument).setVisibility(writable ? View.VISIBLE : View.GONE);
|
||||
findViewById(R.id.actionUploadFile).setVisibility(writable ? View.VISIBLE : View.GONE);
|
||||
findViewById(R.id.actionSharing).setVisibility(writable ? View.VISIBLE : View.GONE);
|
||||
findViewById(R.id.actionDelete).setVisibility(writable ? View.VISIBLE : View.GONE);
|
||||
|
||||
// ACLs
|
||||
ListView aclListView = (ListView) findViewById(R.id.aclListView);
|
||||
aclListView.setAdapter(new AclListAdapter(document.optJSONArray("acls")));
|
||||
final AclListAdapter aclListAdapter = new AclListAdapter(document.optJSONArray("acls"));
|
||||
aclListView.setAdapter(aclListAdapter);
|
||||
aclListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
AclListAdapter.AclItem acl = aclListAdapter.getItem(position);
|
||||
if (acl.getType().equals("USER")) {
|
||||
Intent intent = new Intent(DocumentViewActivity.this, UserProfileActivity.class);
|
||||
intent.putExtra("username", acl.getName());
|
||||
startActivity(intent);
|
||||
} else if (acl.getType().equals("GROUP")) {
|
||||
Intent intent = new Intent(DocumentViewActivity.this, GroupProfileActivity.class);
|
||||
intent.putExtra("name", acl.getName());
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Remaining metadata
|
||||
TextView creatorTextView = (TextView) findViewById(R.id.creatorTextView);
|
||||
final String creator = document.optString("creator");
|
||||
creatorTextView.setText(creator);
|
||||
creatorTextView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(DocumentViewActivity.this, UserProfileActivity.class);
|
||||
intent.putExtra("username", creator);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
// Contributors
|
||||
TextView contributorsTextView = (TextView) findViewById(R.id.contributorsTextView);
|
||||
contributorsTextView.setText(SpannableUtil.buildSpannableContributors(document.optJSONArray("contributors")));
|
||||
|
||||
// Relations
|
||||
JSONArray relations = document.optJSONArray("relations");
|
||||
if (relations.length() > 0) {
|
||||
TextView relationsTextView = (TextView) findViewById(R.id.relationsTextView);
|
||||
relationsTextView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
relationsTextView.setText(SpannableUtil.buildSpannableRelations(relations));
|
||||
} else {
|
||||
findViewById(R.id.relationsLayout).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// Additional dublincore metadata
|
||||
displayDublincoreMetadata(R.id.subjectTextView, R.id.subjectLayout, "subject");
|
||||
displayDublincoreMetadata(R.id.identifierTextView, R.id.identifierLayout, "identifier");
|
||||
displayDublincoreMetadata(R.id.publisherTextView, R.id.publisherLayout, "publisher");
|
||||
displayDublincoreMetadata(R.id.formatTextView, R.id.formatLayout, "format");
|
||||
displayDublincoreMetadata(R.id.sourceTextView, R.id.sourceLayout, "source");
|
||||
displayDublincoreMetadata(R.id.typeTextView, R.id.typeLayout, "type");
|
||||
displayDublincoreMetadata(R.id.coverageTextView, R.id.coverageLayout, "coverage");
|
||||
displayDublincoreMetadata(R.id.rightsTextView, R.id.rightsLayout, "rights");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a dublincore metadata.
|
||||
*
|
||||
* @param textViewId TextView ID
|
||||
* @param blockViewId View ID
|
||||
* @param name Name
|
||||
*/
|
||||
private void displayDublincoreMetadata(int textViewId, int blockViewId, String name) {
|
||||
if (document == null) return;
|
||||
String value = document.optString(name);
|
||||
if (document.isNull(name) || value.isEmpty()) {
|
||||
findViewById(blockViewId).setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
findViewById(blockViewId).setVisibility(View.VISIBLE);
|
||||
TextView textView = (TextView) findViewById(textViewId);
|
||||
textView.setText(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
|
||||
switch (view.getId()) {
|
||||
case R.id.commentListView:
|
||||
if (commentListAdapter == null || document == null) return;
|
||||
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
|
||||
JSONObject comment = commentListAdapter.getItem(info.position);
|
||||
boolean writable = document.optBoolean("writable");
|
||||
String creator = comment.optString("creator");
|
||||
String username = ApplicationContext.getInstance().getUserInfo().optString("username");
|
||||
if (writable || creator.equals(username)) {
|
||||
menu.add(Menu.NONE, 0, 0, getString(R.string.comment_delete));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem item) {
|
||||
// Use real ids if more than one item someday
|
||||
if (item.getItemId() == 0) {
|
||||
// Delete a comment
|
||||
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
|
||||
if (commentListAdapter == null) return false;
|
||||
JSONObject comment = commentListAdapter.getItem(info.position);
|
||||
final String commentId = comment.optString("id");
|
||||
Toast.makeText(DocumentViewActivity.this, R.string.deleting_comment, Toast.LENGTH_LONG).show();
|
||||
|
||||
CommentResource.remove(DocumentViewActivity.this, commentId, new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(JSONObject response) {
|
||||
EventBus.getDefault().post(new CommentDeleteEvent(commentId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
Toast.makeText(DocumentViewActivity.this, R.string.error_deleting_comment, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh comments list.
|
||||
*/
|
||||
private void updateComments() {
|
||||
if (document == null) return;
|
||||
|
||||
final View progressBar = findViewById(R.id.commentProgressView);
|
||||
final TextView emptyView = (TextView) findViewById(R.id.commentEmptyView);
|
||||
final ListView listView = (ListView) findViewById(R.id.commentListView);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
emptyView.setVisibility(View.GONE);
|
||||
listView.setVisibility(View.GONE);
|
||||
registerForContextMenu(listView);
|
||||
|
||||
CommentResource.list(this, document.optString("id"), new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(JSONObject response) {
|
||||
JSONArray comments = response.optJSONArray("comments");
|
||||
commentListAdapter = new CommentListAdapter(DocumentViewActivity.this, comments);
|
||||
listView.setAdapter(commentListAdapter);
|
||||
listView.setVisibility(View.VISIBLE);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
if (comments.length() == 0) {
|
||||
listView.setVisibility(View.GONE);
|
||||
emptyView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
emptyView.setText(R.string.error_loading_comments);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
listView.setVisibility(View.GONE);
|
||||
emptyView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -586,9 +825,9 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
filesEmptyView.setVisibility(View.GONE);
|
||||
|
||||
FileResource.list(this, document.optString("id"), new JsonHttpResponseHandler() {
|
||||
FileResource.list(this, document.optString("id"), new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||
public void onSuccess(JSONObject response) {
|
||||
JSONArray files = response.optJSONArray("files");
|
||||
filePagerAdapter = new FilePagerAdapter(DocumentViewActivity.this, files);
|
||||
fileViewPager.setAdapter(filePagerAdapter);
|
||||
@@ -598,7 +837,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
filesEmptyView.setText(R.string.error_loading_files);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
filesEmptyView.setVisibility(View.VISIBLE);
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.sismics.docs.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.model.application.ApplicationContext;
|
||||
import com.sismics.docs.resource.UserResource;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Group profile activity.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class GroupProfileActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Check if logged in
|
||||
if (!ApplicationContext.getInstance().isLoggedIn()) {
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle activity context
|
||||
if (getIntent() == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Input name
|
||||
final String name = getIntent().getStringExtra("name");
|
||||
if (name == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup the activity
|
||||
setTitle(name);
|
||||
setContentView(R.layout.groupprofile_activity);
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setHomeButtonEnabled(true);
|
||||
}
|
||||
|
||||
// Get the group and populate the view
|
||||
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
|
||||
final View layoutView = findViewById(R.id.layout);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
layoutView.setVisibility(View.GONE);
|
||||
UserResource.get(this, name, new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(JSONObject json) {
|
||||
TextView membersTextView = (TextView) findViewById(R.id.membersTextView);
|
||||
JSONArray members = json.optJSONArray("members");
|
||||
String output = "";
|
||||
for (int i = 0; i < members.length(); i++) {
|
||||
output += members.optString(i) + "; ";
|
||||
}
|
||||
membersTextView.setText(output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
layoutView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,11 @@ 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;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.model.application.ApplicationContext;
|
||||
import com.sismics.docs.resource.UserResource;
|
||||
import com.sismics.docs.ui.form.Validator;
|
||||
@@ -22,7 +22,6 @@ 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;
|
||||
|
||||
/**
|
||||
@@ -31,7 +30,6 @@ import org.json.JSONObject;
|
||||
* @author bgamard
|
||||
*/
|
||||
public class LoginActivity extends AppCompatActivity {
|
||||
|
||||
/**
|
||||
* User interface.
|
||||
*/
|
||||
@@ -42,19 +40,18 @@ 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 EditText txtValidationCode = (EditText) findViewById(R.id.txtValidationCode);
|
||||
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);
|
||||
|
||||
@@ -91,9 +88,11 @@ public class LoginActivity extends AppCompatActivity {
|
||||
PreferenceUtil.setServerUrl(LoginActivity.this, txtServer.getText().toString());
|
||||
|
||||
try {
|
||||
UserResource.login(getApplicationContext(), txtUsername.getText().toString(), txtPassword.getText().toString(), new JsonHttpResponseHandler() {
|
||||
UserResource.login(getApplicationContext(), txtUsername.getText().toString(),
|
||||
txtPassword.getText().toString(), txtValidationCode.getText().toString(),
|
||||
new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject json) {
|
||||
public void onSuccess(JSONObject json) {
|
||||
// Empty previous user caches
|
||||
PreferenceUtil.resetUserCache(getApplicationContext());
|
||||
|
||||
@@ -109,12 +108,16 @@ public class LoginActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
loginForm.setVisibility(View.VISIBLE);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
|
||||
if (responseBytes != null && new String(responseBytes).contains("\"ForbiddenError\"")) {
|
||||
if (json != null && json.optString("type").equals("ForbiddenError")) {
|
||||
DialogUtil.showOkDialog(LoginActivity.this, R.string.login_fail_title, R.string.login_fail);
|
||||
} else if (json != null && json.optString("type").equals("ValidationCodeRequired")) {
|
||||
txtValidationCode.setVisibility(View.VISIBLE);
|
||||
validator.addValidable(txtValidationCode, new Required());
|
||||
validator.validate();
|
||||
} else {
|
||||
DialogUtil.showOkDialog(LoginActivity.this, R.string.network_error_title, R.string.network_error);
|
||||
}
|
||||
@@ -151,9 +154,9 @@ public class LoginActivity extends AppCompatActivity {
|
||||
finish();
|
||||
} else {
|
||||
// Trying to get user data
|
||||
UserResource.info(getApplicationContext(), new JsonHttpResponseHandler() {
|
||||
UserResource.info(getApplicationContext(), new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, final JSONObject json) {
|
||||
public void onSuccess(final JSONObject json) {
|
||||
if (json.optBoolean("anonymous", true)) {
|
||||
loginForm.setVisibility(View.VISIBLE);
|
||||
return;
|
||||
@@ -169,7 +172,7 @@ public class LoginActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
DialogUtil.showOkDialog(LoginActivity.this, R.string.network_error_title, R.string.network_error);
|
||||
loginForm.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@@ -18,24 +18,23 @@ 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;
|
||||
import com.sismics.docs.event.SearchEvent;
|
||||
import com.sismics.docs.fragment.SearchFragment;
|
||||
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.model.application.ApplicationContext;
|
||||
import com.sismics.docs.provider.RecentSuggestionsProvider;
|
||||
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.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Main activity.
|
||||
*
|
||||
@@ -43,7 +42,6 @@ import de.greenrobot.event.EventBus;
|
||||
*/
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
private ActionBarDrawerToggle drawerToggle;
|
||||
private MenuItem searchItem;
|
||||
private DrawerLayout drawerLayout;
|
||||
@@ -73,7 +71,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
// between the sliding drawer and the action bar app icon
|
||||
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout,
|
||||
R.string.drawer_open, R.string.drawer_close);
|
||||
drawerLayout.setDrawerListener(drawerToggle);
|
||||
drawerLayout.addDrawerListener(drawerToggle);
|
||||
|
||||
// Fill the drawer user info
|
||||
JSONObject userInfo = ApplicationContext.getInstance().getUserInfo();
|
||||
@@ -89,19 +87,19 @@ public class MainActivity extends AppCompatActivity {
|
||||
tagListView.setEmptyView(tagProgressView);
|
||||
JSONObject cacheTags = PreferenceUtil.getCachedJson(this, PreferenceUtil.PREF_CACHED_TAGS_JSON);
|
||||
if (cacheTags != null) {
|
||||
tagListView.setAdapter(new TagListAdapter(cacheTags.optJSONArray("stats")));
|
||||
tagListView.setAdapter(new TagListAdapter(cacheTags.optJSONArray("tags")));
|
||||
}
|
||||
TagResource.stats(this, new JsonHttpResponseHandler() {
|
||||
TagResource.list(this, new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||
public void onSuccess(JSONObject response) {
|
||||
PreferenceUtil.setCachedJson(MainActivity.this, PreferenceUtil.PREF_CACHED_TAGS_JSON, response);
|
||||
tagListView.setAdapter(new TagListAdapter(response.optJSONArray("stats")));
|
||||
tagListView.setAdapter(new TagListAdapter(response.optJSONArray("tags")));
|
||||
tagProgressView.setVisibility(View.GONE);
|
||||
tagListView.setEmptyView(tagEmptyView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
tagEmptyView.setText(R.string.error_loading_tags);
|
||||
tagProgressView.setVisibility(View.GONE);
|
||||
tagListView.setEmptyView(tagEmptyView);
|
||||
@@ -114,9 +112,9 @@ public class MainActivity extends AppCompatActivity {
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
TagListAdapter adapter = (TagListAdapter) tagListView.getAdapter();
|
||||
if (adapter == null) return;
|
||||
JSONObject tag = adapter.getItem(position);
|
||||
if (tag == null) return;
|
||||
searchQuery("tag:" + tag.optString("name"));
|
||||
TagListAdapter.TagItem tagItem = adapter.getItem(position);
|
||||
if (tagItem == null) return;
|
||||
searchQuery("tag:" + tagItem.getName());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -138,6 +136,15 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
});
|
||||
|
||||
// Click on Latest activity
|
||||
View auditLogLayout = findViewById(R.id.auditLogLayout);
|
||||
auditLogLayout.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
startActivity(new Intent(MainActivity.this, AuditLogActivity.class));
|
||||
}
|
||||
});
|
||||
|
||||
handleIntent(getIntent());
|
||||
|
||||
EventBus.getDefault().register(this);
|
||||
@@ -147,10 +154,11 @@ public class MainActivity extends AppCompatActivity {
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.logout:
|
||||
UserResource.logout(getApplicationContext(), new JsonHttpResponseHandler() {
|
||||
UserResource.logout(getApplicationContext(), new HttpCallback() {
|
||||
@Override
|
||||
public void onFinish() {
|
||||
// Force logout in all cases, so the user is not stuck in case of network error
|
||||
PreferenceUtil.clearAuthToken(MainActivity.this);
|
||||
ApplicationContext.getInstance().setUserInfo(getApplicationContext(), null);
|
||||
startActivity(new Intent(MainActivity.this, LoginActivity.class));
|
||||
finish();
|
||||
@@ -267,6 +275,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
*
|
||||
* @param event Advanced search event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(AdvancedSearchEvent event) {
|
||||
searchQuery(event.getQuery());
|
||||
}
|
||||
@@ -274,10 +283,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();
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import com.sismics.docs.fragment.SettingsFragment;
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class SettingsActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.sismics.docs.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.model.application.ApplicationContext;
|
||||
import com.sismics.docs.resource.UserResource;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* User profile activity.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class UserProfileActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Check if logged in
|
||||
if (!ApplicationContext.getInstance().isLoggedIn()) {
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle activity context
|
||||
if (getIntent() == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Input username
|
||||
final String username = getIntent().getStringExtra("username");
|
||||
if (username == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup the activity
|
||||
setTitle(username);
|
||||
setContentView(R.layout.userprofile_activity);
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setHomeButtonEnabled(true);
|
||||
}
|
||||
|
||||
// Get the user and populate the view
|
||||
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
|
||||
final View layoutView = findViewById(R.id.layout);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
layoutView.setVisibility(View.GONE);
|
||||
UserResource.get(this, username, new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(JSONObject json) {
|
||||
TextView emailTextView = (TextView) findViewById(R.id.emailTextView);
|
||||
emailTextView.setText(json.optString("email"));
|
||||
|
||||
TextView quotaTextView = (TextView) findViewById(R.id.quotaTextView);
|
||||
quotaTextView.setText(getString(R.string.storage_display,
|
||||
Math.round(json.optLong("storage_current") / 1000000),
|
||||
Math.round(json.optLong("storage_quota") / 1000000)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
layoutView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.sismics.docs.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -24,7 +25,7 @@ public class AclListAdapter extends BaseAdapter {
|
||||
/**
|
||||
* Shares.
|
||||
*/
|
||||
private List<JSONObject> acls;
|
||||
private List<AclItem> aclItemList;
|
||||
|
||||
/**
|
||||
* ACL list adapter.
|
||||
@@ -32,28 +33,46 @@ public class AclListAdapter extends BaseAdapter {
|
||||
* @param acls ACLs
|
||||
*/
|
||||
public AclListAdapter(JSONArray acls) {
|
||||
this.acls = new ArrayList<>();
|
||||
this.aclItemList = new ArrayList<>();
|
||||
|
||||
// Extract only share ACLs
|
||||
// Group ACLs
|
||||
for (int i = 0; i < acls.length(); i++) {
|
||||
JSONObject acl = acls.optJSONObject(i);
|
||||
this.acls.add(acl);
|
||||
String type = acl.optString("type");
|
||||
String name = acl.optString("name");
|
||||
String perm = acl.optString("perm");
|
||||
|
||||
boolean found = false;
|
||||
for (AclItem aclItem : aclItemList) {
|
||||
if (aclItem.type.equals(type) && aclItem.name.equals(name)) {
|
||||
aclItem.permList.add(perm);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
AclItem aclItem = new AclItem();
|
||||
aclItem.type = type;
|
||||
aclItem.name = name;
|
||||
aclItem.permList.add(perm);
|
||||
this.aclItemList.add(aclItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return acls.size();
|
||||
return aclItemList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject getItem(int position) {
|
||||
return acls.get(position);
|
||||
public AclItem getItem(int position) {
|
||||
return aclItemList.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return getItem(position).optString("id").hashCode();
|
||||
return getItem(position).hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,14 +83,37 @@ public class AclListAdapter extends BaseAdapter {
|
||||
}
|
||||
|
||||
// Fill the view
|
||||
final JSONObject acl = getItem(position);
|
||||
final AclItem aclItem = getItem(position);
|
||||
TextView typeTextView = (TextView) view.findViewById(R.id.typeTextView);
|
||||
typeTextView.setText(acl.optString("type"));
|
||||
typeTextView.setText(aclItem.type);
|
||||
TextView nameTextView = (TextView) view.findViewById(R.id.nameTextView);
|
||||
nameTextView.setText(acl.optString("name"));
|
||||
nameTextView.setText(aclItem.name);
|
||||
TextView permTextView = (TextView) view.findViewById(R.id.permTextView);
|
||||
permTextView.setText(acl.optString("perm"));
|
||||
permTextView.setText(TextUtils.join(" + ", aclItem.permList));
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* An ACL item in the list.
|
||||
* Permissions are grouped together.
|
||||
*/
|
||||
public static class AclItem {
|
||||
private String type;
|
||||
private String name;
|
||||
private List<String> permList = new ArrayList<>();
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (type + name).hashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.sismics.docs.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateFormat;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Audit log list adapter.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class AuditLogListAdapter extends BaseAdapter {
|
||||
/**
|
||||
* Shares.
|
||||
*/
|
||||
private List<JSONObject> logList;
|
||||
|
||||
/**
|
||||
* Audit log list adapter.
|
||||
*
|
||||
* @param logs Logs
|
||||
*/
|
||||
public AuditLogListAdapter(JSONArray logs) {
|
||||
this.logList = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < logs.length(); i++) {
|
||||
logList.add(logs.optJSONObject(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return logList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject getItem(int position) {
|
||||
return logList.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return getItem(position).hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View view, final ViewGroup parent) {
|
||||
if (view == null) {
|
||||
LayoutInflater vi = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
view = vi.inflate(R.layout.auditlog_list_item, parent, false);
|
||||
}
|
||||
|
||||
// Build message
|
||||
final JSONObject log = getItem(position);
|
||||
StringBuilder message = new StringBuilder(log.optString("class"));
|
||||
switch (log.optString("type")) {
|
||||
case "CREATE": message.append(" created"); break;
|
||||
case "UPDATE": message.append(" updated"); break;
|
||||
case "DELETE": message.append(" deleted"); break;
|
||||
}
|
||||
switch (log.optString("class")) {
|
||||
case "Document":
|
||||
case "Acl":
|
||||
case "Tag":
|
||||
case "User":
|
||||
case "Group":
|
||||
message.append(" : ");
|
||||
message.append(log.optString("message"));
|
||||
break;
|
||||
}
|
||||
|
||||
// Fill the view
|
||||
TextView usernameTextView = (TextView) view.findViewById(R.id.usernameTextView);
|
||||
TextView messageTextView = (TextView) view.findViewById(R.id.messageTextView);
|
||||
TextView dateTextView = (TextView) view.findViewById(R.id.dateTextView);
|
||||
usernameTextView.setText(log.optString("username"));
|
||||
messageTextView.setText(message);
|
||||
String date = DateFormat.getDateFormat(parent.getContext()).format(new Date(log.optLong("create_date")));
|
||||
dateTextView.setText(date);
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.sismics.docs.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.format.DateFormat;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.util.OkHttpUtil;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Comment list adapter.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class CommentListAdapter extends BaseAdapter {
|
||||
/**
|
||||
* Tags.
|
||||
*/
|
||||
private List<JSONObject> commentList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Context.
|
||||
*/
|
||||
private Context context;
|
||||
|
||||
/**
|
||||
* Comment list adapter.
|
||||
*
|
||||
* @param commentsArray Comments
|
||||
*/
|
||||
public CommentListAdapter(Context context, JSONArray commentsArray) {
|
||||
this.context = context;
|
||||
for (int i = 0; i < commentsArray.length(); i++) {
|
||||
commentList.add(commentsArray.optJSONObject(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return commentList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject getItem(int position) {
|
||||
return commentList.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return getItem(position).optString("id").hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View view, ViewGroup parent) {
|
||||
if (view == null) {
|
||||
LayoutInflater vi = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
view = vi.inflate(R.layout.comment_list_item, parent, false);
|
||||
}
|
||||
|
||||
// Fill the view
|
||||
JSONObject comment = getItem(position);
|
||||
TextView creatorTextView = (TextView) view.findViewById(R.id.creatorTextView);
|
||||
TextView dateTextView = (TextView) view.findViewById(R.id.dateTextView);
|
||||
TextView contentTextView = (TextView) view.findViewById(R.id.contentTextView);
|
||||
ImageView gravatarImageView = (ImageView) view.findViewById(R.id.gravatarImageView);
|
||||
creatorTextView.setText(comment.optString("creator"));
|
||||
dateTextView.setText(DateFormat.getDateFormat(dateTextView.getContext()).format(new Date(comment.optLong("create_date"))));
|
||||
contentTextView.setText(comment.optString("content"));
|
||||
|
||||
// Gravatar image
|
||||
String gravatarUrl = "http://www.gravatar.com/avatar/" + comment.optString("creator_gravatar") + "?s=128d=identicon";
|
||||
OkHttpUtil.picasso(context)
|
||||
.load(gravatarUrl)
|
||||
.into(gravatarImageView);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new comment.
|
||||
*
|
||||
* @param comment Comment
|
||||
*/
|
||||
public void add(JSONObject comment) {
|
||||
commentList.add(comment);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a comment.
|
||||
*
|
||||
* @param commentId Comment ID
|
||||
*/
|
||||
public void remove(String commentId) {
|
||||
for (JSONObject comment : commentList) {
|
||||
if (comment.optString("id").equals(commentId)) {
|
||||
commentList.remove(comment);
|
||||
notifyDataSetChanged();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.util.TagUtil;
|
||||
import com.sismics.docs.util.SpannableUtil;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
@@ -69,7 +69,7 @@ public class DocListAdapter extends RecyclerView.Adapter<DocListAdapter.ViewHold
|
||||
holder.titleTextView.setText(document.optString("title"));
|
||||
|
||||
JSONArray tags = document.optJSONArray("tags");
|
||||
holder.subtitleTextView.setText(TagUtil.buildSpannable(tags));
|
||||
holder.subtitleTextView.setText(SpannableUtil.buildSpannableTags(tags));
|
||||
|
||||
String date = DateFormat.getDateFormat(holder.dateTextView.getContext()).format(new Date(document.optLong("create_date")));
|
||||
holder.dateTextView.setText(date);
|
||||
|
||||
@@ -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,21 +31,11 @@ public class FilePagerAdapter extends PagerAdapter {
|
||||
*/
|
||||
private List<JSONObject> files;
|
||||
|
||||
/**
|
||||
* AQuery.
|
||||
*/
|
||||
private AQuery aq;
|
||||
|
||||
/**
|
||||
* Context.
|
||||
*/
|
||||
private Context context;
|
||||
|
||||
/**
|
||||
* Auth token used to download files.
|
||||
*/
|
||||
private String authToken;
|
||||
|
||||
/**
|
||||
* File pager adapter.
|
||||
*
|
||||
@@ -57,8 +48,6 @@ public class FilePagerAdapter extends PagerAdapter {
|
||||
files.add(filesArray.optJSONObject(i));
|
||||
}
|
||||
this.context = context;
|
||||
this.authToken = PreferenceUtil.getAuthToken(context);
|
||||
aq = new AQuery(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,15 +55,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 +103,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ public class LanguageAdapter extends BaseAdapter {
|
||||
}
|
||||
languageList.add(new Language("fra", R.string.language_french, R.drawable.fra));
|
||||
languageList.add(new Language("eng", R.string.language_english, R.drawable.eng));
|
||||
languageList.add(new Language("jpn", R.string.language_japanese, R.drawable.jpn));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -12,14 +12,13 @@ import com.sismics.docs.R;
|
||||
import com.sismics.docs.event.ShareDeleteEvent;
|
||||
import com.sismics.docs.event.ShareSendEvent;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Share list adapter.
|
||||
*
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.sismics.docs.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -17,9 +19,8 @@ import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Tag list adapter.
|
||||
@@ -30,7 +31,7 @@ public class TagListAdapter extends BaseAdapter {
|
||||
/**
|
||||
* Tags.
|
||||
*/
|
||||
private List<JSONObject> tags;
|
||||
private List<TagItem> tagItemList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Tag list adapter.
|
||||
@@ -38,33 +39,52 @@ public class TagListAdapter extends BaseAdapter {
|
||||
* @param tagsArray Tags
|
||||
*/
|
||||
public TagListAdapter(JSONArray tagsArray) {
|
||||
this.tags = new ArrayList<>();
|
||||
List<JSONObject> tags = new ArrayList<>();
|
||||
for (int i = 0; i < tagsArray.length(); i++) {
|
||||
tags.add(tagsArray.optJSONObject(i));
|
||||
}
|
||||
|
||||
// Sort tags by count desc
|
||||
Collections.sort(tags, new Comparator<JSONObject>() {
|
||||
@Override
|
||||
public int compare(JSONObject lhs, JSONObject rhs) {
|
||||
return lhs.optInt("count") < rhs.optInt("count") ? 1 : -1;
|
||||
// Reorder tags by parent/child relation and compute depth
|
||||
int depth = 0;
|
||||
initTags(tags, "", depth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init tags model recursively.
|
||||
*
|
||||
* @param tags All tags from server
|
||||
* @param parentId Parent ID
|
||||
* @param depth Depth
|
||||
*/
|
||||
private void initTags(List<JSONObject> tags, String parentId, int depth) {
|
||||
// Get all tags with this parent
|
||||
for (JSONObject tag : tags) {
|
||||
String tagParentId = tag.optString("parent");
|
||||
if (parentId.equals(tagParentId)) {
|
||||
TagItem tagItem = new TagItem();
|
||||
tagItem.id = tag.optString("id");
|
||||
tagItem.name = tag.optString("name");
|
||||
tagItem.color = tag.optString("color");
|
||||
tagItem.depth = depth;
|
||||
tagItemList.add(tagItem);
|
||||
initTags(tags, tagItem.id, depth + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return tags.size();
|
||||
return tagItemList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject getItem(int position) {
|
||||
return tags.get(position);
|
||||
public TagItem getItem(int position) {
|
||||
return tagItemList.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return getItem(position).optString("id").hashCode();
|
||||
return getItem(position).id.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,19 +95,38 @@ public class TagListAdapter extends BaseAdapter {
|
||||
}
|
||||
|
||||
// Fill the view
|
||||
JSONObject tag = getItem(position);
|
||||
TagItem tagItem = getItem(position);
|
||||
TextView tagTextView = (TextView) view.findViewById(R.id.tagTextView);
|
||||
tagTextView.setText(tag.optString("name"));
|
||||
TextView tagCountTextView = (TextView) view.findViewById(R.id.tagCountTextView);
|
||||
tagCountTextView.setText(tag.optString("count"));
|
||||
tagTextView.setText(tagItem.name);
|
||||
|
||||
// Label color filtering
|
||||
ImageView labelImageView = (ImageView) view.findViewById(R.id.labelImageView);
|
||||
Drawable labelDrawable = labelImageView.getDrawable().mutate();
|
||||
labelDrawable.setColorFilter(Color.parseColor(tag.optString("color")), PorterDuff.Mode.MULTIPLY);
|
||||
labelDrawable.setColorFilter(Color.parseColor(tagItem.color), PorterDuff.Mode.MULTIPLY);
|
||||
labelImageView.setImageDrawable(labelDrawable);
|
||||
labelImageView.invalidate();
|
||||
|
||||
// Offset according to depth
|
||||
Resources resources = parent.getContext().getResources();
|
||||
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) labelImageView.getLayoutParams();
|
||||
layoutParams.leftMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, tagItem.depth * 12, resources.getDisplayMetrics());
|
||||
labelImageView.setLayoutParams(layoutParams);
|
||||
labelImageView.requestLayout();
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* A tag item in the tags list.
|
||||
*/
|
||||
public static class TagItem {
|
||||
private String id;
|
||||
private String name;
|
||||
private String color;
|
||||
private int depth;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.sismics.docs.event;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Comment add event.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class CommentAddEvent {
|
||||
/**
|
||||
* Comment.
|
||||
*/
|
||||
private JSONObject comment;
|
||||
|
||||
/**
|
||||
* Create a comment add event.
|
||||
*
|
||||
* @param comment Comment
|
||||
*/
|
||||
public CommentAddEvent(JSONObject comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of comment.
|
||||
*
|
||||
* @return comment
|
||||
*/
|
||||
public JSONObject getComment() {
|
||||
return comment;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.sismics.docs.event;
|
||||
|
||||
/**
|
||||
* Comment delete event.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class CommentDeleteEvent {
|
||||
/**
|
||||
* Comment ID.
|
||||
*/
|
||||
private String commentId;
|
||||
|
||||
/**
|
||||
* Create a comment add event.
|
||||
*
|
||||
* @param commentId Comment ID
|
||||
*/
|
||||
public CommentDeleteEvent(String commentId) {
|
||||
this.commentId = commentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of commentId.
|
||||
*
|
||||
* @return commentId
|
||||
*/
|
||||
public String getCommentId() {
|
||||
return commentId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
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 android.widget.CheckBox;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.util.NetworkUtil;
|
||||
import com.sismics.docs.util.PreferenceUtil;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Export PDF dialog fragment.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class DocExportPdfFragment extends DialogFragment {
|
||||
/**
|
||||
* Export PDF dialog fragment.
|
||||
*
|
||||
* @param id Document ID
|
||||
* @param title Document title
|
||||
*/
|
||||
public static DocExportPdfFragment newInstance(String id, String title) {
|
||||
DocExportPdfFragment fragment = new DocExportPdfFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString("id", id);
|
||||
args.putString("title", title);
|
||||
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);
|
||||
final SeekBar marginSeekBar = (SeekBar) view.findViewById(R.id.marginSeekBar);
|
||||
final CheckBox exportMetadataCheckbox = (CheckBox) view.findViewById(R.id.exportMetadataCheckbox);
|
||||
final CheckBox exportCommentsCheckbox = (CheckBox) view.findViewById(R.id.exportCommentsCheckbox);
|
||||
final CheckBox fitToPageCheckbox = (CheckBox) view.findViewById(R.id.fitToPageCheckbox);
|
||||
final TextView marginValueText = (TextView) view.findViewById(R.id.marginValueText);
|
||||
|
||||
// Margin label follow seekbar value
|
||||
marginSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
marginValueText.setText(String.format(Locale.ENGLISH, "%d", progress));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// Build the dialog
|
||||
builder.setView(view)
|
||||
.setPositiveButton(R.string.download, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
// Download the PDF
|
||||
String pdfUrl = PreferenceUtil.getServerUrl(getActivity()) + "/api/document/" + getArguments().getString("id") + "/pdf?" +
|
||||
"metadata=" + exportMetadataCheckbox.isChecked() + "&comments=" + exportCommentsCheckbox.isChecked() + "&fitimagetopage=" + fitToPageCheckbox.isChecked() +
|
||||
"&margin=" + marginSeekBar.getProgress();
|
||||
String title = getArguments().getString("title");
|
||||
NetworkUtil.downloadFile(getActivity(), pdfUrl, title + ".pdf", title, getString(R.string.download_pdf_title));
|
||||
|
||||
getDialog().cancel();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
getDialog().cancel();
|
||||
}
|
||||
});
|
||||
return builder.create();
|
||||
}
|
||||
}
|
||||
@@ -21,17 +21,17 @@ 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.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* @author bgamard.
|
||||
*/
|
||||
@@ -100,7 +100,7 @@ public class DocListFragment extends Fragment {
|
||||
}));
|
||||
|
||||
// Infinite scrolling
|
||||
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||
super.onScrolled(recyclerView, dx, dy);
|
||||
@@ -150,6 +150,7 @@ public class DocListFragment extends Fragment {
|
||||
*
|
||||
* @param event Search event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(SearchEvent event) {
|
||||
query = event.getQuery();
|
||||
loadDocuments(getView(), true);
|
||||
@@ -160,6 +161,7 @@ public class DocListFragment extends Fragment {
|
||||
*
|
||||
* @param event Document edit event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(DocumentEditEvent event) {
|
||||
adapter.updateDocument(event.getDocument());
|
||||
}
|
||||
@@ -169,6 +171,7 @@ public class DocListFragment extends Fragment {
|
||||
*
|
||||
* @param event Document delete event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(DocumentDeleteEvent event) {
|
||||
adapter.deleteDocument(event.getDocumentId());
|
||||
}
|
||||
@@ -178,6 +181,7 @@ public class DocListFragment extends Fragment {
|
||||
*
|
||||
* @param event Document add event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(DocumentAddEvent event) {
|
||||
// Refresh the list, maybe the new document fit in it
|
||||
loadDocuments(getView(), true);
|
||||
@@ -218,16 +222,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);
|
||||
|
||||
|
||||
@@ -21,17 +21,17 @@ 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.JsonHttpResponseHandler;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
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.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Document sharing dialog fragment.
|
||||
*
|
||||
@@ -44,7 +44,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) {
|
||||
@@ -74,15 +75,15 @@ public class DocShareFragment extends DialogFragment {
|
||||
shareAddButton.setEnabled(false);
|
||||
|
||||
ShareResource.add(getActivity(), getArguments().getString("id"), shareNameEditText.getText().toString(),
|
||||
new JsonHttpResponseHandler() {
|
||||
new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||
public void onSuccess(JSONObject response) {
|
||||
shareNameEditText.setText("");
|
||||
loadShares(getDialog().getWindow().getDecorView());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
Toast.makeText(getActivity(), R.string.error_adding_share, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@@ -121,9 +122,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,27 +133,39 @@ 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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A share delete event has been fired.
|
||||
*
|
||||
* @param event Share delete event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(ShareDeleteEvent event) {
|
||||
ShareResource.delete(getActivity(), event.getId(), new JsonHttpResponseHandler() {
|
||||
ShareResource.delete(getActivity(), event.getId(), new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||
public void onSuccess(JSONObject response) {
|
||||
loadShares(getDialog().getWindow().getDecorView());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
Toast.makeText(getActivity(), R.string.error_deleting_share, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A share send event has been fired.
|
||||
*
|
||||
* @param event Share send event
|
||||
*/
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(ShareSendEvent event) {
|
||||
if (document == null) return;
|
||||
|
||||
|
||||
@@ -22,14 +22,13 @@ import com.sismics.docs.ui.view.TagsCompleteTextView;
|
||||
import com.sismics.docs.util.PreferenceUtil;
|
||||
import com.sismics.docs.util.SearchQueryBuilder;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Advanced search fragment.
|
||||
*
|
||||
@@ -56,6 +55,7 @@ public class SearchFragment extends DialogFragment {
|
||||
View view = inflater.inflate(R.layout.search_dialog, null);
|
||||
final EditText searchEditText = (EditText) view.findViewById(R.id.searchEditText);
|
||||
final EditText fulltextEditText = (EditText) view.findViewById(R.id.fulltextEditText);
|
||||
final EditText creatorEditText = (EditText) view.findViewById(R.id.creatorEditText);
|
||||
final CheckBox sharedCheckbox = (CheckBox) view.findViewById(R.id.sharedCheckbox);
|
||||
final Spinner languageSpinner = (Spinner) view.findViewById(R.id.languageSpinner);
|
||||
final DatePickerView beforeDatePicker = (DatePickerView) view.findViewById(R.id.beforeDatePicker);
|
||||
@@ -73,7 +73,7 @@ public class SearchFragment extends DialogFragment {
|
||||
dialog.cancel();
|
||||
return dialog;
|
||||
}
|
||||
JSONArray tagArray = tags.optJSONArray("stats");
|
||||
JSONArray tagArray = tags.optJSONArray("tags");
|
||||
|
||||
List<JSONObject> tagList = new ArrayList<>();
|
||||
for (int i = 0; i < tagArray.length(); i++) {
|
||||
@@ -90,6 +90,7 @@ public class SearchFragment extends DialogFragment {
|
||||
// Build the simple criterias
|
||||
SearchQueryBuilder queryBuilder = new SearchQueryBuilder()
|
||||
.simpleSearch(searchEditText.getText().toString())
|
||||
.creator(creatorEditText.getText().toString())
|
||||
.shared(sharedCheckbox.isChecked())
|
||||
.language(((LanguageAdapter.Language) languageSpinner.getSelectedItem()).getId())
|
||||
.before(beforeDatePicker.getDate())
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
/*
|
||||
Android Asynchronous Http Client
|
||||
Copyright (c) 2011 James Smith <james@loopj.com>
|
||||
http://loopj.com
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package com.sismics.docs.listener;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.loopj.android.http.TextHttpResponseHandler;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONTokener;
|
||||
|
||||
/**
|
||||
* Used to intercept and handle the responses from requests made using {@link com.loopj.android.http.AsyncHttpClient}, with
|
||||
* automatic parsing into a {@link JSONObject} or {@link JSONArray}. <p> </p> This class is
|
||||
* designed to be passed to get, post, put and delete requests with the {@link #onSuccess(int,
|
||||
* org.apache.http.Header[], org.json.JSONArray)} or {@link #onSuccess(int,
|
||||
* org.apache.http.Header[], org.json.JSONObject)} methods anonymously overridden. <p> </p>
|
||||
* Additionally, you can override the other event methods from the parent class.
|
||||
*/
|
||||
public class JsonHttpResponseHandler extends TextHttpResponseHandler {
|
||||
|
||||
private static final String LOG_TAG = "JsonHttpResponseHandler";
|
||||
|
||||
/**
|
||||
* Creates new JsonHttpResponseHandler, with JSON String encoding UTF-8
|
||||
*/
|
||||
public JsonHttpResponseHandler() {
|
||||
super(DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new JsonHttpRespnseHandler with given JSON String encoding
|
||||
*
|
||||
* @param encoding String encoding to be used when parsing JSON
|
||||
*/
|
||||
public JsonHttpResponseHandler(String encoding) {
|
||||
super(encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns when request succeeds
|
||||
*
|
||||
* @param statusCode http response status line
|
||||
* @param headers response headers if any
|
||||
* @param response parsed response if any
|
||||
*/
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||
Log.w(LOG_TAG, "onSuccess(int, Header[], JSONObject) was not overriden, but callback was received");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns when request succeeds
|
||||
*
|
||||
* @param statusCode http response status line
|
||||
* @param headers response headers if any
|
||||
* @param response parsed response if any
|
||||
*/
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
|
||||
Log.w(LOG_TAG, "onSuccess(int, Header[], JSONArray) was not overriden, but callback was received");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns when request failed
|
||||
*
|
||||
* @param statusCode http response status line
|
||||
* @param headers response headers if any
|
||||
* @param throwable throwable describing the way request failed
|
||||
* @param errorResponse parsed response if any
|
||||
*/
|
||||
public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
|
||||
Log.w(LOG_TAG, "onFailure(int, Header[], Throwable, JSONObject) was not overriden, but callback was received", throwable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns when request failed
|
||||
*
|
||||
* @param statusCode http response status line
|
||||
* @param headers response headers if any
|
||||
* @param throwable throwable describing the way request failed
|
||||
* @param errorResponse parsed response if any
|
||||
*/
|
||||
public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONArray errorResponse) {
|
||||
Log.w(LOG_TAG, "onFailure(int, Header[], Throwable, JSONArray) was not overriden, but callback was received", throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
|
||||
Log.w(LOG_TAG, "onFailure(int, Header[], String, Throwable) was not overriden, but callback was received", throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, String responseString) {
|
||||
Log.w(LOG_TAG, "onSuccess(int, Header[], String) was not overriden, but callback was received");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onSuccess(final int statusCode, final Header[] headers, final byte[] responseBytes) {
|
||||
if (statusCode != HttpStatus.SC_NO_CONTENT) {
|
||||
Runnable parser = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
final Object jsonResponse = parseResponse(responseBytes);
|
||||
postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (jsonResponse instanceof JSONObject) {
|
||||
onSuccess(statusCode, headers, (JSONObject) jsonResponse);
|
||||
} else if (jsonResponse instanceof JSONArray) {
|
||||
onSuccess(statusCode, headers, (JSONArray) jsonResponse);
|
||||
} else if (jsonResponse instanceof String) {
|
||||
onFailure(statusCode, headers, (String) jsonResponse, new JSONException("Response cannot be parsed as JSON data"));
|
||||
} else {
|
||||
onFailure(statusCode, headers, new JSONException("Unexpected response type " + jsonResponse.getClass().getName()), (JSONObject) null);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
} catch (final JSONException ex) {
|
||||
postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
onFailure(statusCode, headers, ex, (JSONObject) null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
if (!getUseSynchronousMode()) {
|
||||
new Thread(parser).start();
|
||||
} else {
|
||||
// In synchronous mode everything should be run on one thread
|
||||
parser.run();
|
||||
}
|
||||
} else {
|
||||
onSuccess(statusCode, headers, new JSONObject());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onFailure(final int statusCode, final Header[] headers, final byte[] responseBytes, final Throwable throwable) {
|
||||
if (responseBytes != null) {
|
||||
Runnable parser = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
final Object jsonResponse = parseResponse(responseBytes);
|
||||
postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (jsonResponse instanceof JSONObject) {
|
||||
onFailure(statusCode, headers, throwable, (JSONObject) jsonResponse);
|
||||
} else if (jsonResponse instanceof JSONArray) {
|
||||
onFailure(statusCode, headers, throwable, (JSONArray) jsonResponse);
|
||||
} else if (jsonResponse instanceof String) {
|
||||
onFailure(statusCode, headers, (String) jsonResponse, throwable);
|
||||
} else {
|
||||
onFailure(statusCode, headers, new JSONException("Unexpected response type " + jsonResponse.getClass().getName()), (JSONObject) null);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} catch (final JSONException ex) {
|
||||
postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
onFailure(statusCode, headers, ex, (JSONObject) null);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
if (!getUseSynchronousMode()) {
|
||||
new Thread(parser).start();
|
||||
} else {
|
||||
// In synchronous mode everything should be run on one thread
|
||||
parser.run();
|
||||
}
|
||||
} else {
|
||||
Log.v(LOG_TAG, "response body is null, calling onFailure(Throwable, JSONObject)");
|
||||
onFailure(statusCode, headers, throwable, (JSONObject) null);
|
||||
}
|
||||
|
||||
// In all cases, call the default failure listener
|
||||
onAllFailure(statusCode, headers, responseBytes, throwable);
|
||||
}
|
||||
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
// All failures go there
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Object of type {@link JSONObject}, {@link JSONArray}, String, Boolean, Integer, Long,
|
||||
* Double or {@link JSONObject#NULL}, see {@link org.json.JSONTokener#nextValue()}
|
||||
*
|
||||
* @param responseBody response bytes to be assembled in String and parsed as JSON
|
||||
* @return Object parsedResponse
|
||||
* @throws org.json.JSONException exception if thrown while parsing JSON
|
||||
*/
|
||||
protected Object parseResponse(byte[] responseBody) throws JSONException {
|
||||
if (null == responseBody)
|
||||
return null;
|
||||
Object result = null;
|
||||
//trim the string to prevent start with blank, and test if the string is valid JSON, because the parser don't do this :(. If JSON is not valid this will return null
|
||||
String jsonString = getResponseString(responseBody, getCharset());
|
||||
if (jsonString != null) {
|
||||
jsonString = jsonString.trim();
|
||||
if (jsonString.startsWith(UTF8_BOM)) {
|
||||
jsonString = jsonString.substring(1);
|
||||
}
|
||||
if (jsonString.startsWith("{") || jsonString.startsWith("[")) {
|
||||
result = new JSONTokener(jsonString).nextValue();
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
result = jsonString;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListen
|
||||
private OnItemClickListener mListener;
|
||||
|
||||
public interface OnItemClickListener {
|
||||
public void onItemClick(View view, int position);
|
||||
void onItemClick(View view, int position);
|
||||
}
|
||||
|
||||
GestureDetector mGestureDetector;
|
||||
@@ -25,13 +25,18 @@ public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListen
|
||||
});
|
||||
}
|
||||
|
||||
@Override public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
|
||||
View childView = view.findChildViewUnder(e.getX(), e.getY());
|
||||
if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
|
||||
mListener.onItemClick(childView, view.getChildPosition(childView));
|
||||
mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }
|
||||
@Override
|
||||
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }
|
||||
|
||||
@Override
|
||||
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { }
|
||||
}
|
||||
@@ -4,11 +4,10 @@ import android.app.Activity;
|
||||
import android.content.Context;
|
||||
|
||||
import com.sismics.docs.listener.CallbackListener;
|
||||
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.resource.UserResource;
|
||||
import com.sismics.docs.util.PreferenceUtil;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
@@ -80,9 +79,9 @@ public class ApplicationContext {
|
||||
* @param callbackListener CallbackListener
|
||||
*/
|
||||
public void fetchUserInfo(final Activity activity, final CallbackListener callbackListener) {
|
||||
UserResource.info(activity.getApplicationContext(), new JsonHttpResponseHandler() {
|
||||
UserResource.info(activity.getApplicationContext(), new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, final JSONObject json) {
|
||||
public void onSuccess(JSONObject json) {
|
||||
// Save data in application context
|
||||
if (!json.optBoolean("anonymous", true)) {
|
||||
setUserInfo(activity.getApplicationContext(), json);
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.sismics.docs.resource;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.util.OkHttpUtil;
|
||||
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Request;
|
||||
|
||||
/**
|
||||
* Access to /auditlog API.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class AuditLogResource extends BaseResource {
|
||||
/**
|
||||
* GET /auditlog.
|
||||
*
|
||||
* @param context Context
|
||||
* @param documentId Document ID
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void list(Context context, String documentId, HttpCallback callback) {
|
||||
HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(getApiUrl(context) + "/auditlog")
|
||||
.newBuilder();
|
||||
if (documentId != null) {
|
||||
httpUrlBuilder.addQueryParameter("document", documentId);
|
||||
}
|
||||
Request request = new Request.Builder()
|
||||
.url(httpUrlBuilder.build())
|
||||
.get()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
}
|
||||
@@ -1,125 +1,15 @@
|
||||
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;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
/**
|
||||
* Base class for API access.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class BaseResource {
|
||||
|
||||
/**
|
||||
* User-Agent to use.
|
||||
*/
|
||||
protected static String USER_AGENT = null;
|
||||
|
||||
/**
|
||||
* Accept-Language header.
|
||||
*/
|
||||
protected static String ACCEPT_LANGUAGE = null;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
|
||||
sf.setHostnameVerifier(MySSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
|
||||
client.setSSLSocketFactory(sf);
|
||||
AbstractAjaxCallback.setSSF(sf);
|
||||
} catch (Exception e) {
|
||||
// NOP
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resource initialization.
|
||||
*
|
||||
* @param context Context
|
||||
*/
|
||||
protected static void init(Context context) {
|
||||
client.setCookieStore(new PersistentCookieStore(context));
|
||||
|
||||
if (USER_AGENT == null) {
|
||||
USER_AGENT = "Sismics Docs Android " + ApplicationUtil.getVersionName(context) + "/Android " + Build.VERSION.RELEASE + "/" + Build.MODEL;
|
||||
client.setUserAgent(USER_AGENT);
|
||||
}
|
||||
|
||||
if (ACCEPT_LANGUAGE == null) {
|
||||
Locale locale = Locale.getDefault();
|
||||
ACCEPT_LANGUAGE = locale.getLanguage() + "_" + locale.getCountry();
|
||||
client.addHeader("Accept-Language", ACCEPT_LANGUAGE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Socket factory to allow self-signed certificates.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public static class MySSLSocketFactory extends SSLSocketFactory {
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
|
||||
public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
|
||||
super(truststore);
|
||||
|
||||
TrustManager tm = new X509TrustManager() {
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
sslContext.init(null, new TrustManager[] { tm }, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
|
||||
return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket() throws IOException {
|
||||
return sslContext.getSocketFactory().createSocket();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns cleaned API URL.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.sismics.docs.resource;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.util.OkHttpUtil;
|
||||
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Request;
|
||||
|
||||
|
||||
/**
|
||||
* Access to /comment API.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class CommentResource extends BaseResource {
|
||||
/**
|
||||
* GET /comment/id.
|
||||
*
|
||||
* @param context Context
|
||||
* @param documentId Document ID
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void list(Context context, String documentId, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/comment/" + documentId))
|
||||
.get()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT /comment.
|
||||
*
|
||||
* @param context Context
|
||||
* @param documentId Document ID
|
||||
* @param content Comment content
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void add(Context context, String documentId, String content, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/comment"))
|
||||
.put(new FormBody.Builder()
|
||||
.add("id", documentId)
|
||||
.add("content", content)
|
||||
.build())
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE /comment/id.
|
||||
*
|
||||
* @param context Context
|
||||
* @param commentId Comment ID
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void remove(Context context, String commentId, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/comment/" + commentId))
|
||||
.delete()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
}
|
||||
@@ -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,26 @@ 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<String> tagIdList, String language, long createDate, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
Set<String> 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));
|
||||
for( String tagId : tagIdList) {
|
||||
formBuilder.add("tags", tagId);
|
||||
}
|
||||
|
||||
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,27 +117,25 @@ 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<String> tagIdList, String language, long createDate, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
Set<String> 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));
|
||||
for( String tagId : tagIdList) {
|
||||
formBuilder.add("tags", tagId);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel pending requests.
|
||||
*
|
||||
* @param context Context
|
||||
*/
|
||||
public static void cancel(Context context) {
|
||||
client.cancelRequests(context, true);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,24 @@ package com.sismics.docs.resource;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.loopj.android.http.PersistentCookieStore;
|
||||
import com.loopj.android.http.RequestParams;
|
||||
import com.loopj.android.http.SyncHttpClient;
|
||||
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.util.OkHttpUtil;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.internal.Util;
|
||||
import okio.BufferedSink;
|
||||
import okio.Okio;
|
||||
import okio.Source;
|
||||
|
||||
|
||||
/**
|
||||
@@ -22,12 +33,19 @@ public class FileResource extends BaseResource {
|
||||
*
|
||||
* @param context Context
|
||||
* @param documentId Document ID
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void list(Context context, String documentId, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
|
||||
client.get(getApiUrl(context) + "/file/list?id=" + documentId, responseHandler);
|
||||
public static void list(Context context, String documentId, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/file/list")
|
||||
.newBuilder()
|
||||
.addQueryParameter("id", documentId)
|
||||
.build())
|
||||
.get()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,12 +53,16 @@ public class FileResource 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) + "/file/" + id, responseHandler);
|
||||
public static void delete(Context context, String id, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/file/" + id))
|
||||
.delete()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,34 +71,53 @@ public class FileResource extends BaseResource {
|
||||
* @param context Context
|
||||
* @param documentId Document ID
|
||||
* @param is Input stream
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void addSync(Context context, String documentId, InputStream is, JsonHttpResponseHandler responseHandler) throws Exception {
|
||||
init(context);
|
||||
public static void addSync(Context context, String documentId, final InputStream is, HttpCallback callback) throws Exception {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/file"))
|
||||
.put(new MultipartBody.Builder()
|
||||
.setType(MultipartBody.FORM)
|
||||
.addFormDataPart("id", documentId)
|
||||
.addFormDataPart("file", "file", new RequestBody() {
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return MediaType.parse("application/octet-stream");
|
||||
}
|
||||
|
||||
SyncHttpClient client = new SyncHttpClient();
|
||||
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
trustStore.load(null, null);
|
||||
MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
|
||||
sf.setHostnameVerifier(MySSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
|
||||
client.setSSLSocketFactory(sf);
|
||||
client.setCookieStore(new PersistentCookieStore(context));
|
||||
client.setUserAgent(USER_AGENT);
|
||||
client.addHeader("Accept-Language", ACCEPT_LANGUAGE);
|
||||
@Override
|
||||
public void writeTo(BufferedSink sink) throws IOException {
|
||||
Source source = Okio.source(is);
|
||||
try {
|
||||
sink.writeAll(source);
|
||||
} finally {
|
||||
Util.closeQuietly(source);
|
||||
}
|
||||
}
|
||||
})
|
||||
.build())
|
||||
.build();
|
||||
Response response = OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.execute();
|
||||
|
||||
RequestParams params = new RequestParams();
|
||||
params.put("id", documentId);
|
||||
params.put("file", is, "file", "application/octet-stream", true);
|
||||
client.put(getApiUrl(context) + "/file", params, responseHandler);
|
||||
}
|
||||
// Call the right callback
|
||||
final String body = response.body().string();
|
||||
if (response.isSuccessful()) {
|
||||
try {
|
||||
callback.onSuccess(new JSONObject(body));
|
||||
} catch (Exception e) {
|
||||
callback.onFailure(null, e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
callback.onFailure(new JSONObject(body), null);
|
||||
} catch (Exception e) {
|
||||
callback.onFailure(null, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel pending requests.
|
||||
*
|
||||
* @param context Context
|
||||
*/
|
||||
public static void cancel(Context context) {
|
||||
client.cancelRequests(context, true);
|
||||
callback.onFinish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,12 @@ 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 okhttp3.FormBody;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Request;
|
||||
|
||||
|
||||
/**
|
||||
@@ -18,15 +22,19 @@ public class ShareResource extends BaseResource {
|
||||
* @param context Context
|
||||
* @param documentId Document ID
|
||||
* @param name Name
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void add(Context context, String documentId, String name, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
|
||||
RequestParams params = new RequestParams();
|
||||
params.put("id", documentId);
|
||||
params.put("name", name);
|
||||
client.put(getApiUrl(context) + "/share", params, responseHandler);
|
||||
public static void add(Context context, String documentId, String name, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/share"))
|
||||
.put(new FormBody.Builder()
|
||||
.add("id", documentId)
|
||||
.add("name", name)
|
||||
.build())
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,11 +42,15 @@ public class ShareResource 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) + "/share/" + id, responseHandler);
|
||||
public static void delete(Context context, String id, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/share/" + id))
|
||||
.delete()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,11 @@ package com.sismics.docs.resource;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.util.OkHttpUtil;
|
||||
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Request;
|
||||
|
||||
|
||||
/**
|
||||
@@ -12,14 +16,18 @@ import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||
*/
|
||||
public class TagResource extends BaseResource {
|
||||
/**
|
||||
* GET /tag/stats.
|
||||
* GET /tag/list.
|
||||
*
|
||||
* @param context Context
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void stats(Context context, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
|
||||
client.get(getApiUrl(context) + "/tag/stats", responseHandler);
|
||||
public static void list(Context context, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/tag/list"))
|
||||
.get()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,12 @@ 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 okhttp3.FormBody;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Request;
|
||||
|
||||
/**
|
||||
* Access to /user API.
|
||||
@@ -18,41 +22,69 @@ public class UserResource extends BaseResource {
|
||||
* @param context Context
|
||||
* @param username Username
|
||||
* @param password Password
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void login(Context context, String username, String password, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
|
||||
RequestParams params = new RequestParams();
|
||||
params.put("username", username);
|
||||
params.put("password", password);
|
||||
params.put("remember", "true");
|
||||
client.post(getApiUrl(context) + "/user/login", params, responseHandler);
|
||||
public static void login(Context context, String username, String password, String code, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/user/login"))
|
||||
.post(new FormBody.Builder()
|
||||
.add("username", username)
|
||||
.add("password", password)
|
||||
.add("code", code)
|
||||
.add("remember", "true")
|
||||
.build())
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /user.
|
||||
*
|
||||
* @param context Context
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void info(Context context, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
|
||||
RequestParams params = new RequestParams();
|
||||
client.get(getApiUrl(context) + "/user", params, responseHandler);
|
||||
public static void info(Context context, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/user"))
|
||||
.get()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /user/username.
|
||||
*
|
||||
* @param context Context
|
||||
* param username Username
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void get(Context context, String username, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/user/" + username))
|
||||
.get()
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /user/logout.
|
||||
*
|
||||
* @param context Context
|
||||
* @param responseHandler Callback
|
||||
* @param callback Callback
|
||||
*/
|
||||
public static void logout(Context context, JsonHttpResponseHandler responseHandler) {
|
||||
init(context);
|
||||
|
||||
RequestParams params = new RequestParams();
|
||||
client.post(getApiUrl(context) + "/user/logout", params, responseHandler);
|
||||
public static void logout(Context context, HttpCallback callback) {
|
||||
Request request = new Request.Builder()
|
||||
.url(HttpUrl.parse(getApiUrl(context) + "/user/logout"))
|
||||
.post(new FormBody.Builder().build())
|
||||
.build();
|
||||
OkHttpUtil.buildClient(context)
|
||||
.newCall(request)
|
||||
.enqueue(HttpCallback.buildOkHttpCallback(callback));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String, ConcurrentHashMap<String, HttpCookie>> 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<String, ?> prefsMap = cookiePrefs.getAll();
|
||||
for (Map.Entry<String, ?> 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<String, HttpCookie>());
|
||||
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<String, HttpCookie>());
|
||||
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<HttpCookie> get(URI uri) {
|
||||
ArrayList<HttpCookie> 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<HttpCookie> getCookies() {
|
||||
ArrayList<HttpCookie> ret = new ArrayList<>();
|
||||
for (String key : cookies.keySet())
|
||||
ret.addAll(cookies.get(key).values());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<URI> getURIs() {
|
||||
ArrayList<URI> 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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -12,16 +12,16 @@ import android.util.Log;
|
||||
|
||||
import com.sismics.docs.R;
|
||||
import com.sismics.docs.event.FileAddEvent;
|
||||
import com.sismics.docs.listener.JsonHttpResponseHandler;
|
||||
import com.sismics.docs.listener.HttpCallback;
|
||||
import com.sismics.docs.resource.FileResource;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
import okhttp3.internal.Util;
|
||||
|
||||
/**
|
||||
* Service to upload a file to a document in the background.
|
||||
@@ -81,17 +81,22 @@ public class FileUploadService extends IntentService {
|
||||
*/
|
||||
private void handleFileUpload(final String documentId, final Uri uri) throws Exception {
|
||||
final InputStream is = getContentResolver().openInputStream(uri);
|
||||
FileResource.addSync(this, documentId, is, new JsonHttpResponseHandler() {
|
||||
FileResource.addSync(this, documentId, is, new HttpCallback() {
|
||||
@Override
|
||||
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
|
||||
public void onSuccess(JSONObject response) {
|
||||
EventBus.getDefault().post(new FileAddEvent(documentId, response.optString("id")));
|
||||
FileUploadService.this.onComplete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
|
||||
public void onFailure(JSONObject json, Exception e) {
|
||||
FileUploadService.this.onError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
Util.closeQuietly(is);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.sismics.docs.ui.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ListView;
|
||||
|
||||
/**
|
||||
* Non-scrollable ListView.
|
||||
* All items are visible from the start.
|
||||
*
|
||||
* @author http://stackoverflow.com/questions/18813296/non-scrollable-listview-inside-scrollview/24629341#24629341
|
||||
*/
|
||||
public class NonScrollListView extends ListView {
|
||||
|
||||
public NonScrollListView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
public NonScrollListView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
public NonScrollListView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
@Override
|
||||
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(
|
||||
Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
|
||||
ViewGroup.LayoutParams params = getLayoutParams();
|
||||
params.height = getMeasuredHeight();
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import android.content.pm.PackageManager.NameNotFoundException;
|
||||
* @author bgamard
|
||||
*/
|
||||
public class ApplicationUtil {
|
||||
|
||||
/**
|
||||
* Returns version name.
|
||||
*
|
||||
|
||||
@@ -12,7 +12,6 @@ import com.sismics.docs.R;
|
||||
* @author bgamard
|
||||
*/
|
||||
public class DialogUtil {
|
||||
|
||||
/**
|
||||
* Create a dialog with an OK button.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.sismics.docs.util;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.DownloadManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
||||
/**
|
||||
* Utility class for network actions.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class NetworkUtil {
|
||||
/**
|
||||
* Download a file using Android download manager.
|
||||
*
|
||||
* @param url URL to download
|
||||
* @param fileName Destination file name
|
||||
* @param title Notification title
|
||||
* @param description Notification description
|
||||
*/
|
||||
public static void downloadFile(Activity activity, String url, String fileName, String title, String description) {
|
||||
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(activity, new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
String authToken = PreferenceUtil.getAuthToken(activity);
|
||||
DownloadManager downloadManager = (DownloadManager) activity.getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
|
||||
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
||||
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
|
||||
request.addRequestHeader("Cookie", "auth_token=" + authToken);
|
||||
request.setTitle(title);
|
||||
request.setDescription(description);
|
||||
downloadManager.enqueue(request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
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.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();
|
||||
return chain.proceed(original.newBuilder()
|
||||
.header("Cache-Control", "max-age=" + (3600 * 24 * 365))
|
||||
.method(original.method(), original.body())
|
||||
.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);
|
||||
|
||||
// Runtime configuration
|
||||
return okHttpClient.newBuilder()
|
||||
.cookieJar(new JavaNetCookieJar(cookieManager))
|
||||
.addNetworkInterceptor(new Interceptor() {
|
||||
@Override
|
||||
public Response intercept(Chain chain) throws IOException {
|
||||
Request original = chain.request();
|
||||
return chain.proceed(original.newBuilder()
|
||||
.header("User-Agent", userAgent)
|
||||
.header("Accept-Language", acceptLanguage)
|
||||
.method(original.method(), original.body())
|
||||
.build());
|
||||
}
|
||||
})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,11 @@ import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import com.loopj.android.http.PersistentCookieStore;
|
||||
import com.sismics.docs.resource.cookie.PersistentCookieStore;
|
||||
|
||||
import org.apache.http.cookie.Cookie;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.HttpCookie;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -26,6 +26,7 @@ public class PreferenceUtil {
|
||||
|
||||
/**
|
||||
* Returns a preference of boolean type.
|
||||
*
|
||||
* @param context Context
|
||||
* @param key Shared preference key
|
||||
* @return Shared preference value
|
||||
@@ -37,6 +38,7 @@ public class PreferenceUtil {
|
||||
|
||||
/**
|
||||
* Returns a preference of string type.
|
||||
*
|
||||
* @param context Context
|
||||
* @param key Shared preference key
|
||||
* @return Shared preference value
|
||||
@@ -48,6 +50,7 @@ public class PreferenceUtil {
|
||||
|
||||
/**
|
||||
* Returns a preference of integer type.
|
||||
*
|
||||
* @param context Context
|
||||
* @param key Shared preference key
|
||||
* @return Shared preference value
|
||||
@@ -69,6 +72,7 @@ public class PreferenceUtil {
|
||||
|
||||
/**
|
||||
* Update JSON cache.
|
||||
*
|
||||
* @param context Context
|
||||
* @param key Shared preference key
|
||||
* @param json JSON data
|
||||
@@ -80,6 +84,7 @@ public class PreferenceUtil {
|
||||
|
||||
/**
|
||||
* Returns a JSON cache.
|
||||
*
|
||||
* @param context Context
|
||||
* @param key Shared preference key
|
||||
* @return JSON data
|
||||
@@ -96,6 +101,7 @@ public class PreferenceUtil {
|
||||
|
||||
/**
|
||||
* Update server URL.
|
||||
*
|
||||
* @param context Context
|
||||
*/
|
||||
public static void setServerUrl(Context context, String serverUrl) {
|
||||
@@ -105,6 +111,7 @@ public class PreferenceUtil {
|
||||
|
||||
/**
|
||||
* Empty user caches.
|
||||
*
|
||||
* @param context Context
|
||||
*/
|
||||
public static void resetUserCache(Context context) {
|
||||
@@ -118,12 +125,14 @@ public class PreferenceUtil {
|
||||
|
||||
/**
|
||||
* Returns auth token cookie from shared preferences.
|
||||
*
|
||||
* @param context Context
|
||||
* @return Auth token
|
||||
*/
|
||||
public static String getAuthToken(Context context) {
|
||||
PersistentCookieStore cookieStore = new PersistentCookieStore(context);
|
||||
List<Cookie> cookieList = cookieStore.getCookies();
|
||||
for (Cookie cookie : cookieList) {
|
||||
List<HttpCookie> cookieList = cookieStore.getCookies();
|
||||
for (HttpCookie cookie : cookieList) {
|
||||
if (cookie.getName().equals("auth_token")) {
|
||||
return cookie.getValue();
|
||||
}
|
||||
@@ -132,8 +141,19 @@ public class PreferenceUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all auth tokens.
|
||||
*
|
||||
* @param context Context
|
||||
*/
|
||||
public static void clearAuthToken(Context context) {
|
||||
PersistentCookieStore cookieStore = new PersistentCookieStore(context);
|
||||
cookieStore.removeAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns cleaned server URL.
|
||||
*
|
||||
* @param context Context
|
||||
* @return Server URL
|
||||
*/
|
||||
|
||||
@@ -59,6 +59,21 @@ public class SearchQueryBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a creator criteria.
|
||||
*
|
||||
* @param creator Creator criteria
|
||||
* @return The builder
|
||||
*/
|
||||
public SearchQueryBuilder creator(String creator) {
|
||||
if (isValid(creator)) {
|
||||
query.append(SEARCH_SEPARATOR)
|
||||
.append("by:")
|
||||
.append(creator);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a language criteria.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.sismics.docs.util;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.BackgroundColorSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Utility class for spannable.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class SpannableUtil {
|
||||
/**
|
||||
* Create a colored spannable from tags.
|
||||
*
|
||||
* @param tags Tags
|
||||
* @return Colored spannable
|
||||
*/
|
||||
public static Spannable buildSpannableTags(JSONArray tags) {
|
||||
return buildSpannable(tags, "name", "color");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a spannable for contributors.
|
||||
*
|
||||
* @param contributors Contributors
|
||||
* @return Spannable
|
||||
*/
|
||||
public static Spannable buildSpannableContributors(JSONArray contributors) {
|
||||
return buildSpannable(contributors, "username", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a spannable for relations.
|
||||
*
|
||||
* @param relations Relations
|
||||
* @return Spannable
|
||||
*/
|
||||
public static Spannable buildSpannableRelations(JSONArray relations) {
|
||||
return buildSpannable(relations, "title", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a spannable from a JSONArray.
|
||||
*
|
||||
* @param array JSONArray
|
||||
* @param valueName Name of the value part
|
||||
* @param colorName Name of the color part (optional)
|
||||
* @return Spannable
|
||||
*/
|
||||
private static Spannable buildSpannable(JSONArray array, String valueName, String colorName) {
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder();
|
||||
|
||||
for (int i = 0; i < array.length(); i++) {
|
||||
final JSONObject tag = array.optJSONObject(i);
|
||||
int start = builder.length();
|
||||
builder.append(" ").append(tag.optString(valueName)).append(" ");
|
||||
builder.setSpan(new ForegroundColorSpan(Color.WHITE), start, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
builder.setSpan(new BackgroundColorSpan(Color.parseColor(tag.optString(colorName, "#5bc0de"))), start, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
/*
|
||||
TODO : Make tags, relations and contributors clickable
|
||||
builder.setSpan(new ClickableSpan() {
|
||||
@Override
|
||||
public void onClick(View widget) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDrawState(TextPaint ds) {
|
||||
super.updateDrawState(ds);
|
||||
ds.setColor(Color.WHITE);
|
||||
ds.setUnderlineText(false);
|
||||
}
|
||||
}, start, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);*/
|
||||
builder.append(" ");
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.sismics.docs.util;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.BackgroundColorSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Utility class for tags.
|
||||
*
|
||||
* @author bgamard.
|
||||
*/
|
||||
public class TagUtil {
|
||||
/**
|
||||
* Create a colored spannable from tags.
|
||||
*
|
||||
* @param tags Tags
|
||||
* @return Colored spannable
|
||||
*/
|
||||
public static Spannable buildSpannable(JSONArray tags) {
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder();
|
||||
|
||||
for (int i = 0; i < tags.length(); i++) {
|
||||
JSONObject tag = tags.optJSONObject(i);
|
||||
int start = builder.length();
|
||||
builder.append(" ").append(tag.optString("name")).append(" ");
|
||||
builder.setSpan(new ForegroundColorSpan(Color.WHITE), start, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
builder.setSpan(new BackgroundColorSpan(Color.parseColor(tag.optString("color"))), start, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
builder.append(" ");
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 353 B |
|
After Width: | Height: | Size: 448 B |
|
Before Width: | Height: | Size: 558 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 461 B |
|
After Width: | Height: | Size: 565 B |
|
Before Width: | Height: | Size: 813 B |
30
docs-android/app/src/main/res/layout/auditlog_activity.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<android.support.v4.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ListView
|
||||
android:id="@+id/auditLogListView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:choiceMode="singleChoice"
|
||||
android:dividerHeight="0dp"
|
||||
android:visibility="gone">
|
||||
</ListView>
|
||||
</android.support.v4.widget.SwipeRefreshLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
android:layout_centerInParent="true"
|
||||
android:indeterminate="true" />
|
||||
|
||||
</RelativeLayout>
|
||||
60
docs-android/app/src/main/res/layout/auditlog_list_item.xml
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/assignImageView"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_assignment_grey600_48dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/usernameTextView"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toRightOf="@+id/assignImageView"
|
||||
android:layout_toEndOf="@+id/assignImageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:textColor="#212121"
|
||||
android:text="admin"
|
||||
android:textSize="16sp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/messageTextView"
|
||||
android:layout_below="@+id/usernameTextView"
|
||||
android:layout_toRightOf="@+id/assignImageView"
|
||||
android:layout_toEndOf="@+id/assignImageView"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:textColor="#777777"
|
||||
android:text="Document created : test doc 1"
|
||||
android:textSize="16sp"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dateTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="2014-12-02"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:textColor="#777777"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</RelativeLayout>
|
||||
57
docs-android/app/src/main/res/layout/comment_list_item.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/gravatarImageView"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginRight="12dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/creatorTextView"
|
||||
android:layout_toRightOf="@id/gravatarImageView"
|
||||
android:layout_toEndOf="@id/gravatarImageView"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#212121"
|
||||
android:text="Creator"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/contentTextView"
|
||||
android:layout_toRightOf="@id/gravatarImageView"
|
||||
android:layout_toEndOf="@id/gravatarImageView"
|
||||
android:layout_below="@id/creatorTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="6dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textColor="#212121"
|
||||
android:text="Comment content"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dateTextView"
|
||||
android:layout_toRightOf="@id/gravatarImageView"
|
||||
android:layout_toEndOf="@id/gravatarImageView"
|
||||
android:layout_below="@id/contentTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="6dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textColor="#888"
|
||||
android:text="2015-11-10"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -37,7 +37,7 @@
|
||||
android:textSize="16sp"
|
||||
android:layout_centerInParent="true"/>
|
||||
|
||||
<com.shamanland.fab.FloatingActionButton
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
android:id="@+id/addDocumentButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -48,6 +48,6 @@
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:src="@drawable/ic_add_white_24dp"
|
||||
app:floatingActionButtonColor="#263238"/>
|
||||
app:fabSize="normal"/>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -13,6 +13,7 @@
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:id="@+id/folderImageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -22,7 +23,9 @@
|
||||
android:id="@+id/titleTextView"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toRightOf="@+id/folderImageView"
|
||||
android:layout_toEndOf="@+id/folderImageView"
|
||||
android:layout_toLeftOf="@+id/dateTextView"
|
||||
android:layout_toStartOf="@+id/dateTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="12dp">
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="6dp"
|
||||
android:text="@string/export_metadata"
|
||||
android:id="@+id/exportMetadataCheckbox" />
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="6dp"
|
||||
android:text="@string/export_comments"
|
||||
android:id="@+id/exportCommentsCheckbox" />
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="6dp"
|
||||
android:checked="true"
|
||||
android:text="@string/fit_image_to_page"
|
||||
android:id="@+id/fitToPageCheckbox" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/margin"
|
||||
android:layout_weight="0"/>
|
||||
|
||||
<SeekBar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:id="@+id/marginSeekBar"
|
||||
android:progress="10"
|
||||
android:max="50"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/marginValueText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:text="10"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="0"
|
||||
android:text="@string/mm"/>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -37,200 +37,628 @@
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- Right drawer -->
|
||||
<!-- Left drawer -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/right_drawer"
|
||||
android:id="@+id/left_drawer"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end"
|
||||
android:layout_gravity="start"
|
||||
android:orientation="vertical"
|
||||
android:clickable="true"
|
||||
android:background="#fff"
|
||||
android:elevation="5dp">
|
||||
|
||||
<!-- Actions -->
|
||||
<!-- Comments -->
|
||||
|
||||
<TextView
|
||||
android:drawableStart="@drawable/ic_comment_grey600_24dp"
|
||||
android:drawableLeft="@drawable/ic_comment_grey600_24dp"
|
||||
android:drawablePadding="6dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:gravity="center"
|
||||
android:textColor="#de000000"
|
||||
android:text="@string/comments"
|
||||
android:layout_margin="12dp"/>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="#eee"/>
|
||||
|
||||
<ListView
|
||||
android:layout_weight="1"
|
||||
android:id="@+id/commentListView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:choiceMode="singleChoice"
|
||||
android:divider="@android:color/transparent"
|
||||
android:transcriptMode="normal"
|
||||
android:dividerHeight="0dp"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_weight="1"
|
||||
android:id="@+id/commentProgressView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<ProgressBar
|
||||
style="?android:progressBarStyle"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/commentEmptyView"
|
||||
android:visibility="gone"
|
||||
android:padding="12dp"
|
||||
android:gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:text="@string/no_comments"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="#eee"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:orientation="horizontal"
|
||||
android:padding="6dp"
|
||||
android:gravity="center">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
<EditText
|
||||
android:id="@+id/commentEditText"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
style="?android:buttonBarStyle">
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionEditDocument"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_create_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/edit_document"
|
||||
android:textColor="@color/button_material_dark"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionUploadFile"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_file_upload_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/upload_file"
|
||||
android:textColor="@color/button_material_dark"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionDownload"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_file_download_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/download_document"
|
||||
android:textColor="@color/button_material_dark"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
style="?android:buttonBarStyle">
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionSharing"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_share_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/share"
|
||||
android:textColor="@color/button_material_dark"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionDelete"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_delete_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/delete_document"
|
||||
android:textColor="@color/button_material_dark"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
android:lines="1"
|
||||
android:inputType="text"
|
||||
android:hint="@string/add_comment"
|
||||
android:maxLength="4000"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/addCommentBtn"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:src="@drawable/ic_send_grey600_24dp"
|
||||
android:contentDescription="@string/send"
|
||||
android:background="?android:selectableItemBackground"/>
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:background="#eee"/>
|
||||
|
||||
<!-- Document metadata -->
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/detailLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/createdDateLabel"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="24dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/created_date"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/createdDateTextView"
|
||||
android:layout_toRightOf="@id/createdDateLabel"
|
||||
android:layout_toEndOf="@id/createdDateLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_alignParentTop="true"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tagTextView"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/createdDateLabel"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:maxLines="1"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/descriptionTextView"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_below="@id/tagTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/sharedImageView"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/ic_folder_shared_grey600_24dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_toLeftOf="@+id/languageImageView"
|
||||
android:layout_toStartOf="@+id/languageImageView"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/languageImageView"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentTop="true"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:background="#eee"/>
|
||||
|
||||
<!-- ACLs -->
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/primary_text_default_material_light"
|
||||
android:text="@string/who_can_access"
|
||||
android:layout_margin="12dp"/>
|
||||
|
||||
<ListView
|
||||
android:id="@+id/aclListView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:choiceMode="singleChoice"
|
||||
android:divider="@android:color/transparent"
|
||||
android:dividerHeight="0dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Right drawer -->
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/right_drawer"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="match_parent"
|
||||
android:clickable="true"
|
||||
android:background="#fff"
|
||||
android:elevation="5dp"
|
||||
android:layout_gravity="end">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- Actions -->
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
style="?android:buttonBarStyle">
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionDownload"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_file_download_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/download_document"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionExportPdf"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_description_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/export_pdf"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionAuditLog"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_assignment_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/activity"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="8dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
style="?android:buttonBarStyle">
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionEditDocument"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_create_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/edit_document"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="0dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionUploadFile"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_file_upload_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/upload_file"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="0dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionSharing"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_share_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/share"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="0dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionDelete"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_delete_grey600_24dp"
|
||||
style="?android:buttonBarButtonStyle"
|
||||
android:text="@string/delete_document"
|
||||
android:textColor="#ff5a595b"
|
||||
android:textAllCaps="false"
|
||||
android:layout_margin="0dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:background="#eee"/>
|
||||
|
||||
<!-- Document metadata -->
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/detailLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/createdDateLabel"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="24dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/created_date"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/createdDateTextView"
|
||||
android:layout_toRightOf="@id/createdDateLabel"
|
||||
android:layout_toEndOf="@id/createdDateLabel"
|
||||
android:layout_toLeftOf="@id/sharedImageView"
|
||||
android:layout_toStartOf="@id/sharedImageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_alignParentTop="true"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/creatorLabel"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="24dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_below="@+id/createdDateLabel"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/creator"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/creatorTextView"
|
||||
android:layout_toRightOf="@id/creatorLabel"
|
||||
android:layout_toEndOf="@id/creatorLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_below="@+id/createdDateTextView"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tagTextView"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/creatorLabel"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:maxLines="1"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/descriptionTextView"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_below="@id/tagTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
<ImageView
|
||||
android:contentDescription="@string/shared"
|
||||
android:id="@+id/sharedImageView"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/ic_folder_shared_grey600_24dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_toLeftOf="@+id/languageImageView"
|
||||
android:layout_toStartOf="@+id/languageImageView"/>
|
||||
|
||||
<ImageView
|
||||
android:contentDescription="@string/language"
|
||||
android:id="@+id/languageImageView"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentTop="true"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- Additional dublincore metadata -->
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingRight="12dp"
|
||||
android:paddingBottom="12dp">
|
||||
|
||||
<!-- Subject -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/subjectLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_weight="0.33"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/subject"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subjectTextView"
|
||||
android:layout_weight="0.67"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Identifier -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/identifierLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_weight="0.33"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/identifier"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/identifierTextView"
|
||||
android:layout_weight="0.67"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Publisher -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/publisherLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_weight="0.33"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/publisher"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/publisherTextView"
|
||||
android:layout_weight="0.67"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Format -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/formatLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_weight="0.33"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/format"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/formatTextView"
|
||||
android:layout_weight="0.67"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Source -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/sourceLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_weight="0.33"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/source"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sourceTextView"
|
||||
android:layout_weight="0.67"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Type -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/typeLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_weight="0.33"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/type"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/typeTextView"
|
||||
android:layout_weight="0.67"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Coverage -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/coverageLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_weight="0.33"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/coverage"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/coverageTextView"
|
||||
android:layout_weight="0.67"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Rights -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/rightsLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_weight="0.33"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/rights"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rightsTextView"
|
||||
android:layout_weight="0.67"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Contributors -->
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_weight="0.33"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/contributors"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/contributorsTextView"
|
||||
android:layout_weight="0.67"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Relations -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/relationsLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="2dp">
|
||||
|
||||
<TextView
|
||||
android:layout_weight="0.33"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/relations"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/relationsTextView"
|
||||
android:layout_weight="0.67"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:background="#eee"/>
|
||||
|
||||
<!-- ACLs -->
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textColor="#de000000"
|
||||
android:text="@string/who_can_access"
|
||||
android:layout_margin="12dp"/>
|
||||
|
||||
<com.sismics.docs.ui.view.NonScrollListView
|
||||
android:id="@+id/aclListView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:choiceMode="singleChoice"
|
||||
android:divider="@android:color/transparent"
|
||||
android:dividerHeight="0dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</android.support.v4.widget.DrawerLayout>
|
||||
@@ -9,7 +9,7 @@
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="15dip"
|
||||
android:id="@+id/fileProgressBar"
|
||||
android:indeterminate="false"
|
||||
android:indeterminate="true"
|
||||
android:layout_centerInParent="true"/>
|
||||
|
||||
<it.sephiroth.android.library.imagezoom.ImageViewTouch
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/membersTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
android:layout_centerInParent="true"
|
||||
android:indeterminate="true" />
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -61,6 +61,17 @@
|
||||
android:inputType="textPassword">
|
||||
</EditText>
|
||||
|
||||
<EditText
|
||||
android:visibility="gone"
|
||||
android:id="@+id/txtValidationCode"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.5"
|
||||
android:ems="10"
|
||||
android:hint="@string/validation_code"
|
||||
android:inputType="number">
|
||||
</EditText>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnConnect"
|
||||
android:layout_width="fill_parent"
|
||||
|
||||
@@ -117,6 +117,40 @@
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- Audit log -->
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/auditLogLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
android:clickable="true"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/auditLogImageView"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_assignment_grey600_24dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toRightOf="@+id/auditLogImageView"
|
||||
android:layout_toEndOf="@+id/auditLogImageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textColor="#212121"
|
||||
android:text="@string/latest_activity"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- Separator -->
|
||||
|
||||
<View
|
||||
|
||||
@@ -27,6 +27,15 @@
|
||||
android:textSize="18sp"
|
||||
android:hint="@string/fulltext_search"/>
|
||||
|
||||
<!-- Creator -->
|
||||
<EditText
|
||||
android:id="@+id/creatorEditText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:textSize="18sp"
|
||||
android:hint="@string/creator"/>
|
||||
|
||||
<!-- Language -->
|
||||
<Spinner
|
||||
android:id="@+id/languageSpinner"
|
||||
|
||||
@@ -28,17 +28,5 @@
|
||||
android:textColor="#212121"
|
||||
android:text="Appartement"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tagCountTextView"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textColor="#888"
|
||||
android:text="5"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textStyle="bold"
|
||||
android:gravity="end"
|
||||
android:textSize="16sp"
|
||||
android:text="@string/email"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/emailTextView"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textSize="16sp"
|
||||
android:text="user1@sismicsdocs.com"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textStyle="bold"
|
||||
android:gravity="end"
|
||||
android:textSize="16sp"
|
||||
android:text="@string/storage_quota"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/quotaTextView"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textSize="16sp"
|
||||
android:text="35/500 MB"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
android:layout_centerInParent="true"
|
||||
android:indeterminate="true" />
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -9,6 +9,12 @@
|
||||
android:title="@string/toggle_informations">
|
||||
</item>
|
||||
|
||||
<item
|
||||
android:id="@+id/comments"
|
||||
app:showAsAction="collapseActionView"
|
||||
android:title="@string/comments">
|
||||
</item>
|
||||
|
||||
<item
|
||||
android:id="@+id/download_file"
|
||||
app:showAsAction="collapseActionView"
|
||||
|
||||
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 11 KiB |
144
docs-android/app/src/main/res/values-fr/strings.xml
Normal file
@@ -0,0 +1,144 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!-- Validation -->
|
||||
<string name="validate_error_email">Email invalide</string>
|
||||
<string name="validate_error_length_min">Trop court (min. %d)</string>
|
||||
<string name="validate_error_length_max">Trop long (max. %d)</string>
|
||||
<string name="validate_error_required">Requis</string>
|
||||
<string name="validate_error_alphanumeric">Seuls les lettres et les nombres sont autorisés</string>
|
||||
|
||||
<!-- App -->
|
||||
<string name="drawer_open">Ouvrir le menu de navigation</string>
|
||||
<string name="drawer_close">Fermer le menu de navigation</string>
|
||||
<string name="login_explain"><![CDATA[Pour commencer, vous devez télécharger et installer le serveur Sismics Docs sur <a href="https://github.com/sismics/docs">github.com/sismics/docs</a> et entrer son URL ci-dessous]]></string>
|
||||
<string name="server">Serveur</string>
|
||||
<string name="username">Nom d\'utilisateur</string>
|
||||
<string name="password">Mot de passe</string>
|
||||
<string name="login">Connexion</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="cancel">Annuler</string>
|
||||
<string name="login_fail_title">Echec de la connexion</string>
|
||||
<string name="login_fail">Mauvais nom d\'utilisateur ou de mot de passe</string>
|
||||
<string name="network_error_title">Erreur réseau</string>
|
||||
<string name="network_error">Erreur réseau, veuillez vérifier votre connexion internet et l\'URL du serveur</string>
|
||||
<string name="invalid_url_title">URL invalide</string>
|
||||
<string name="invalid_url">Veuillez vérifier l\URL du serveur et réessayer</string>
|
||||
<string name="crash_toast_text">Une erreur s\'est produite, un rapport a été envoyé afin de corriger ce problème</string>
|
||||
<string name="created_date">Date création</string>
|
||||
<string name="download_file">Télécharger ce fichier</string>
|
||||
<string name="download_document">Télécharger</string>
|
||||
<string name="action_search">Rechercher dans les documents</string>
|
||||
<string name="all_documents">Tous les documents</string>
|
||||
<string name="shared_documents">Documents partagés</string>
|
||||
<string name="all_tags">Tous les tags</string>
|
||||
<string name="no_tags">Aucun tag</string>
|
||||
<string name="error_loading_tags">Erreur de chargement des tags</string>
|
||||
<string name="no_documents">Aucun document</string>
|
||||
<string name="error_loading_documents">Erreur de chargement des documents</string>
|
||||
<string name="no_files">Aucun fichier</string>
|
||||
<string name="error_loading_files">Erreur de chargement des fichiers</string>
|
||||
<string name="new_document">Nouveau document</string>
|
||||
<string name="share">Partage</string>
|
||||
<string name="close">Fermer</string>
|
||||
<string name="add">Ajouter</string>
|
||||
<string name="add_share_hint">Nom du partage (facultatif)</string>
|
||||
<string name="document_not_shared">Ce document n\'est pas partagé</string>
|
||||
<string name="delete_share">Supprimer ce partage</string>
|
||||
<string name="send_share">Envoi ce lien de partage</string>
|
||||
<string name="error_loading_shares">Erreur de chargement des partages</string>
|
||||
<string name="error_adding_share">Erreur lors de l\'ajout du partage</string>
|
||||
<string name="share_default_name">Lien de partage</string>
|
||||
<string name="error_deleting_share">Erreur lors de la suppression de ce partage</string>
|
||||
<string name="send_share_to">Envoi ce lien de partage à</string>
|
||||
<string name="upload_file">Aj. fichier</string>
|
||||
<string name="upload_from">Envoyer un fichier depuis</string>
|
||||
<string name="settings">Paramètres</string>
|
||||
<string name="logout">Déconnexion</string>
|
||||
<string name="version">Version</string>
|
||||
<string name="build">Build</string>
|
||||
<string name="pref_advanced_category">Paramètres avancés</string>
|
||||
<string name="pref_about_category">A propors</string>
|
||||
<string name="pref_github">GitHub</string>
|
||||
<string name="pref_issue">Rapporter un bug</string>
|
||||
<string name="pref_clear_cache_title">Vider le cache</string>
|
||||
<string name="pref_clear_cache_summary">Nettoyer les fichiers en cache</string>
|
||||
<string name="pref_clear_cache_success">Cache vidé</string>
|
||||
<string name="pref_clear_history_title">Vider l\'historique de recherche</string>
|
||||
<string name="pref_clear_history_summary">Supprimer les recherches récentes</string>
|
||||
<string name="pref_clear_history_success">Historique de recherche vidé</string>
|
||||
<string name="pref_cache_size">Taille du cache</string>
|
||||
<string name="save">Enregistrer</string>
|
||||
<string name="edit_document">Modifier</string>
|
||||
<string name="error_editing_document">Erreur réseau, veuillez réessayer</string>
|
||||
<string name="please_wait">Veuillez patienter</string>
|
||||
<string name="document_editing_message">Envoi des données</string>
|
||||
<string name="delete_document">Suppr.</string>
|
||||
<string name="delete_document_title">Supprimer le document</string>
|
||||
<string name="delete_document_message">Etes-vous sûr de vouloir supprimer ce document et tous les fichiers associés ?</string>
|
||||
<string name="document_delete_failure">Erreur réseau lors de la suppression de ce document</string>
|
||||
<string name="document_deleting_message">Suppression du document</string>
|
||||
<string name="delete_file_title">Supprimer le fichier</string>
|
||||
<string name="delete_file_message">Etes-vous sûr de vouloir supprimer ce fichier ?</string>
|
||||
<string name="file_delete_failure">Erreur réseau lors de la suppression du fichier</string>
|
||||
<string name="file_deleting_message">Suppression du fichier</string>
|
||||
<string name="error_reading_file">Erreur lors de la lecture du fichier</string>
|
||||
<string name="upload_notification_title">Sismics Docs</string>
|
||||
<string name="upload_notification_message">Envoi du nouveau fichier</string>
|
||||
<string name="upload_notification_error">Erreur lors de l\'envoi du nouveau fichier</string>
|
||||
<string name="delete_file">Supprimer ce fichier</string>
|
||||
<string name="advanced_search">Recherche avancée</string>
|
||||
<string name="search">Rechercher</string>
|
||||
<string name="add_tags">Ajouter des tags</string>
|
||||
<string name="creation_date">Date de création</string>
|
||||
<string name="description">Description</string>
|
||||
<string name="title">Titre</string>
|
||||
<string name="simple_search">Recherche simple</string>
|
||||
<string name="fulltext_search">Recherche texte intégral</string>
|
||||
<string name="creator">Créateur</string>
|
||||
<string name="after_date">Après cette date</string>
|
||||
<string name="before_date">Avant cette date</string>
|
||||
<string name="search_tags">Rechercher dans les tags</string>
|
||||
<string name="all_languages">Toutes les langues</string>
|
||||
<string name="toggle_informations">Afficher/masquer les informations</string>
|
||||
<string name="who_can_access">Qui a accès</string>
|
||||
<string name="comments">Commentaires</string>
|
||||
<string name="no_comments">Aucun commentaire</string>
|
||||
<string name="error_loading_comments">Erreur de chargement des commentaires</string>
|
||||
<string name="send">Envoyer</string>
|
||||
<string name="add_comment">Ajouter un commentaire</string>
|
||||
<string name="comment_add_failure">Erreur lors de l\'ajout du commentaire</string>
|
||||
<string name="adding_comment">Ajout du commentaire</string>
|
||||
<string name="comment_delete">Supprimer le commentaire</string>
|
||||
<string name="deleting_comment">Suppression du commentaire</string>
|
||||
<string name="error_deleting_comment">Erreur lors de la suppression du commentaire</string>
|
||||
<string name="export_pdf">PDF</string>
|
||||
<string name="download">Télécharger</string>
|
||||
<string name="margin">Marge</string>
|
||||
<string name="fit_image_to_page">Ajuster les images à la page</string>
|
||||
<string name="export_comments">Exporter les commentaires</string>
|
||||
<string name="export_metadata">Exporter les métadonnées</string>
|
||||
<string name="mm">mm</string>
|
||||
<string name="download_file_title">Export de fichier Sismics Docs</string>
|
||||
<string name="download_document_title">Export de document Sismics Docs</string>
|
||||
<string name="download_pdf_title">Export PDF Sismics Docs</string>
|
||||
<string name="latest_activity">Activité récente</string>
|
||||
<string name="activity">Activité</string>
|
||||
<string name="email">E-mail</string>
|
||||
<string name="storage_quota">Quota de stockage</string>
|
||||
<string name="storage_display">%1$d/%2$d Mo</string>
|
||||
<string name="validation_code">Code de validation</string>
|
||||
<string name="shared">Partagé</string>
|
||||
<string name="language">Langue</string>
|
||||
<string name="coverage">Couverture</string>
|
||||
<string name="type">Type</string>
|
||||
<string name="source">Source</string>
|
||||
<string name="format">Format</string>
|
||||
<string name="publisher">Editeur</string>
|
||||
<string name="identifier">Identifiant</string>
|
||||
<string name="subject">Sujet</string>
|
||||
<string name="rights">Droits</string>
|
||||
<string name="contributors">Contributeurs</string>
|
||||
<string name="relations">Relations</string>
|
||||
|
||||
</resources>
|
||||
@@ -9,10 +9,10 @@
|
||||
<string name="validate_error_alphanumeric">Only letters and numbers</string>
|
||||
|
||||
<!-- App -->
|
||||
<string name="app_name">Sismics Docs</string>
|
||||
<string name="app_name" translatable="false">Sismics Docs</string>
|
||||
<string name="drawer_open">Open navigation drawer</string>
|
||||
<string name="drawer_close">Close navigation drawer</string>
|
||||
<string name="login_explain"><![CDATA[To start, you must download and install Sismics Docs Server on <a href="http://www.sismics.com/docs">www.sismics.com/docs</a> and enter your server URL below]]></string>
|
||||
<string name="login_explain"><![CDATA[To start, you must download and install Sismics Docs Server on <a href="https://github.com/sismics/docs">github.com/sismics/docs</a> and enter its below]]></string>
|
||||
<string name="server">Server</string>
|
||||
<string name="username">Username</string>
|
||||
<string name="password">Password</string>
|
||||
@@ -28,9 +28,7 @@
|
||||
<string name="crash_toast_text">A crash occurred, a report has been sent to resolve this problem</string>
|
||||
<string name="created_date">Created date</string>
|
||||
<string name="download_file">Download current file</string>
|
||||
<string name="downloading_file">Downloading file number %1s</string>
|
||||
<string name="download_document">Download</string>
|
||||
<string name="downloading_document">Downloading document</string>
|
||||
<string name="action_search">Search documents</string>
|
||||
<string name="all_documents">All documents</string>
|
||||
<string name="shared_documents">Shared documents</string>
|
||||
@@ -48,7 +46,7 @@
|
||||
<string name="add_share_hint">Name the share (optional)</string>
|
||||
<string name="document_not_shared">This document is not currently shared</string>
|
||||
<string name="delete_share">Delete this share</string>
|
||||
<string name="send_share">Send this share</string>
|
||||
<string name="send_share">Send this share link</string>
|
||||
<string name="error_loading_shares">Error loading shares</string>
|
||||
<string name="error_adding_share">Error adding share</string>
|
||||
<string name="share_default_name">Share link</string>
|
||||
@@ -71,9 +69,8 @@
|
||||
<string name="pref_clear_history_summary">Wipe the recent search suggestions</string>
|
||||
<string name="pref_clear_history_success">Search history cleared</string>
|
||||
<string name="pref_cache_size">Cache size</string>
|
||||
<string name="language_french">French</string>
|
||||
<string name="language_english">English</string>
|
||||
<string name="language_japanese">Japanese</string>
|
||||
<string name="language_french" translatable="false">Français</string>
|
||||
<string name="language_english" translatable="false">English</string>
|
||||
<string name="save">Save</string>
|
||||
<string name="edit_document">Edit</string>
|
||||
<string name="error_editing_document">Network error, please try again</string>
|
||||
@@ -101,12 +98,50 @@
|
||||
<string name="title">Title</string>
|
||||
<string name="simple_search">Simple search</string>
|
||||
<string name="fulltext_search">Fulltext search</string>
|
||||
<string name="creator">Creator</string>
|
||||
<string name="after_date">After date</string>
|
||||
<string name="before_date">Before date</string>
|
||||
<string name="search_tags">Search tags</string>
|
||||
<string name="all_languages">All languages</string>
|
||||
<string name="toggle_informations">Toggle informations</string>
|
||||
<string name="who_can_access">Who can access</string>
|
||||
|
||||
<string name="comments">Comments</string>
|
||||
<string name="no_comments">No comments</string>
|
||||
<string name="error_loading_comments">Error loading comments</string>
|
||||
<string name="send">Send</string>
|
||||
<string name="add_comment">Add a comment</string>
|
||||
<string name="comment_add_failure">Error adding a comment</string>
|
||||
<string name="adding_comment">Adding a comment</string>
|
||||
<string name="comment_delete">Delete comment</string>
|
||||
<string name="deleting_comment">Deleting comment</string>
|
||||
<string name="error_deleting_comment">Error deleting comment</string>
|
||||
<string name="export_pdf">PDF</string>
|
||||
<string name="download">Download</string>
|
||||
<string name="margin">Margin</string>
|
||||
<string name="fit_image_to_page">Fit image to page</string>
|
||||
<string name="export_comments">Export comments</string>
|
||||
<string name="export_metadata">Export metadata</string>
|
||||
<string name="mm">mm</string>
|
||||
<string name="download_file_title">Sismics Docs file export</string>
|
||||
<string name="download_document_title">Sismics Docs document export</string>
|
||||
<string name="download_pdf_title">Sismics Docs PDF export</string>
|
||||
<string name="latest_activity">Latest activity</string>
|
||||
<string name="activity">Activity</string>
|
||||
<string name="email">E-mail</string>
|
||||
<string name="storage_quota">Storage quota</string>
|
||||
<string name="storage_display">%1$d/%2$d MB</string>
|
||||
<string name="validation_code">Validation code</string>
|
||||
<string name="shared">Shared</string>
|
||||
<string name="language">Language</string>
|
||||
<string name="coverage">Coverage</string>
|
||||
<string name="type">Type</string>
|
||||
<string name="source">Source</string>
|
||||
<string name="format">Format</string>
|
||||
<string name="publisher">Publisher</string>
|
||||
<string name="identifier">Identifier</string>
|
||||
<string name="subject">Subject</string>
|
||||
<string name="rights">Rights</string>
|
||||
<string name="contributors">Contributors</string>
|
||||
<string name="relations">Relations</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
||||
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
|
||||
org.gradle.jvmargs=-Xmx3072m
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#Wed Nov 26 21:58:48 CET 2014
|
||||
#Tue Nov 14 23:55:56 CET 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<parent>
|
||||
<groupId>com.sismics.docs</groupId>
|
||||
<artifactId>docs-parent</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<relativePath>../docs-parent</relativePath>
|
||||
<version>1.5</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -26,6 +26,11 @@
|
||||
<artifactId>hibernate-entitymanager</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-c3p0</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Other external dependencies -->
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
@@ -46,7 +51,27 @@
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-email</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.freemarker</groupId>
|
||||
<artifactId>freemarker</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>javax.json</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
@@ -72,26 +97,6 @@
|
||||
<artifactId>jbcrypt</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-core-asl</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-mapper-asl</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.ccil.cowan.tagsoup</groupId>
|
||||
<artifactId>tagsoup</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.googlecode.owasp-java-html-sanitizer</groupId>
|
||||
<artifactId>owasp-java-html-sanitizer</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
<artifactId>lucene-core</artifactId>
|
||||
@@ -106,10 +111,16 @@
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
<artifactId>lucene-queryparser</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.sun.mail</groupId>
|
||||
<artifactId>javax.mail</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Only there to read old index and rebuild them -->
|
||||
<dependency>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
<artifactId>lucene-highlighter</artifactId>
|
||||
<artifactId>lucene-backward-codecs</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -127,15 +138,35 @@
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- OCR dependencies -->
|
||||
<dependency>
|
||||
<groupId>fr.opensagres.xdocreport</groupId>
|
||||
<artifactId>org.odftoolkit.odfdom.converter.pdf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>fr.opensagres.xdocreport</groupId>
|
||||
<artifactId>org.apache.poi.xwpf.converter.pdf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jna</groupId>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- ImageIO plugins -->
|
||||
<dependency>
|
||||
<groupId>com.levigo.jbig2</groupId>
|
||||
<artifactId>levigo-jbig2-imageio</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jai</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<groupId>com.github.jai-imageio</groupId>
|
||||
<artifactId>jai-imageio-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test dependencies -->
|
||||
@@ -146,8 +177,8 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hsqldb</groupId>
|
||||
<artifactId>hsqldb</artifactId>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
update T_CONFIG set CFG_VALUE_C = 'RAM' where CFG_ID_C = 'LUCENE_DIRECTORY_STORAGE';
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.sismics.docs.core.constant;
|
||||
|
||||
/**
|
||||
* ACL type.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public enum AclType {
|
||||
/**
|
||||
* User created ACL.
|
||||
*/
|
||||
USER,
|
||||
|
||||
/**
|
||||
* ACL created by the routing module.
|
||||
*/
|
||||
ROUTING
|
||||
}
|
||||
@@ -10,4 +10,37 @@ public enum ConfigType {
|
||||
* Lucene directory storage type.
|
||||
*/
|
||||
LUCENE_DIRECTORY_STORAGE,
|
||||
/**
|
||||
* Theme configuration.
|
||||
*/
|
||||
THEME,
|
||||
|
||||
/**
|
||||
* Guest login.
|
||||
*/
|
||||
GUEST_LOGIN,
|
||||
|
||||
/**
|
||||
* Default language.
|
||||
*/
|
||||
DEFAULT_LANGUAGE,
|
||||
|
||||
/**
|
||||
* SMTP server configuration.
|
||||
*/
|
||||
SMTP_HOSTNAME,
|
||||
SMTP_PORT,
|
||||
SMTP_FROM,
|
||||
SMTP_USERNAME,
|
||||
SMTP_PASSWORD,
|
||||
|
||||
/**
|
||||
* Inbox scanning configuration.
|
||||
*/
|
||||
INBOX_ENABLED,
|
||||
INBOX_HOSTNAME,
|
||||
INBOX_PORT,
|
||||
INBOX_USERNAME,
|
||||
INBOX_PASSWORD,
|
||||
INBOX_TAG
|
||||
}
|
||||
|
||||
@@ -1,30 +1,20 @@
|
||||
package com.sismics.docs.core.constant;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Application constants.
|
||||
*
|
||||
* @author jtremeaux
|
||||
*/
|
||||
public class Constants {
|
||||
/**
|
||||
* Default locale.
|
||||
*/
|
||||
public static final String DEFAULT_LOCALE_ID = "en";
|
||||
|
||||
/**
|
||||
* Default timezone ID.
|
||||
*/
|
||||
public static final String DEFAULT_TIMEZONE_ID = "Europe/London";
|
||||
|
||||
/**
|
||||
* Default theme ID.
|
||||
*/
|
||||
public static final String DEFAULT_THEME_ID = "default.less";
|
||||
|
||||
/**
|
||||
* Administrator's default password ("admin").
|
||||
*/
|
||||
@@ -39,7 +29,12 @@ public class Constants {
|
||||
* File Lucene directory storage.
|
||||
*/
|
||||
public static final String LUCENE_DIRECTORY_STORAGE_FILE = "FILE";
|
||||
|
||||
|
||||
/**
|
||||
* Guest user ID.
|
||||
*/
|
||||
public static final String GUEST_USER_ID = "guest";
|
||||
|
||||
/**
|
||||
* Default generic user role.
|
||||
*/
|
||||
@@ -48,5 +43,48 @@ public class Constants {
|
||||
/**
|
||||
* Supported document languages.
|
||||
*/
|
||||
public static final List<String> SUPPORTED_LANGUAGES = Lists.newArrayList("eng", "fra", "jpn");
|
||||
public static final List<String> SUPPORTED_LANGUAGES = Lists.newArrayList("eng", "fra", "ita", "deu", "spa", "por", "pol", "rus", "ukr", "ara", "hin", "chi_sim", "chi_tra", "jpn", "tha", "kor");
|
||||
|
||||
/**
|
||||
* Base URL environment variable.
|
||||
*/
|
||||
public static final String BASE_URL_ENV = "DOCS_BASE_URL";
|
||||
|
||||
/**
|
||||
* Default language environment variable.
|
||||
*/
|
||||
public static final String DEFAULT_LANGUAGE_ENV = "DOCS_DEFAULT_LANGUAGE";
|
||||
|
||||
/**
|
||||
* SMTP configuration environment variables.
|
||||
*/
|
||||
public static final String SMTP_HOSTNAME_ENV = "DOCS_SMTP_HOSTNAME";
|
||||
public static final String SMTP_PORT_ENV = "DOCS_SMTP_PORT";
|
||||
public static final String SMTP_USERNAME_ENV = "DOCS_SMTP_USERNAME";
|
||||
public static final String SMTP_PASSWORD_ENV = "DOCS_SMTP_PASSWORD";
|
||||
|
||||
/**
|
||||
* Global quota environment variable.
|
||||
*/
|
||||
public static final String GLOBAL_QUOTA_ENV = "DOCS_GLOBAL_QUOTA";
|
||||
|
||||
/**
|
||||
* Initial admin password environment variable.
|
||||
*/
|
||||
public static final String ADMIN_PASSWORD_INIT_ENV = "DOCS_ADMIN_PASSWORD_INIT";
|
||||
|
||||
/**
|
||||
* Expiration time of the password recovery in hours.
|
||||
*/
|
||||
public static final int PASSWORD_RECOVERY_EXPIRATION_HOUR = 2;
|
||||
|
||||
/**
|
||||
* Email template for password recovery.
|
||||
*/
|
||||
public static final String EMAIL_TEMPLATE_PASSWORD_RECOVERY = "password_recovery";
|
||||
|
||||
/**
|
||||
* Email template for route step validate.
|
||||
*/
|
||||
public static final String EMAIL_TEMPLATE_ROUTE_STEP_VALIDATE = "route_step_validate";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.sismics.docs.core.constant;
|
||||
|
||||
/**
|
||||
* Route step transitions.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public enum RouteStepTransition {
|
||||
/**
|
||||
* Route step approved.
|
||||
*/
|
||||
APPROVED,
|
||||
|
||||
/**
|
||||
* Route step rejected.
|
||||
*/
|
||||
REJECTED,
|
||||
|
||||
/**
|
||||
* Route step validated.
|
||||
*/
|
||||
VALIDATED
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.sismics.docs.core.constant;
|
||||
|
||||
/**
|
||||
* Route step types.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public enum RouteStepType {
|
||||
/**
|
||||
* Approval step with 2 choices.
|
||||
*/
|
||||
APPROVE,
|
||||
|
||||
/**
|
||||
* Simple validation step, no possible choice.
|
||||
*/
|
||||
VALIDATE
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package com.sismics.docs.core.dao.file.theme;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.sismics.docs.core.util.DirectoryUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Theme DAO.
|
||||
*
|
||||
* @author jtremeaux
|
||||
*/
|
||||
public class ThemeDao {
|
||||
private final static FilenameFilter CSS_FILTER = new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.endsWith(".css") || name.endsWith(".less");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the list of all themes.
|
||||
*
|
||||
* @return List of themes
|
||||
*/
|
||||
public List<String> findAll() {
|
||||
final File themeDirectory = DirectoryUtil.getThemeDirectory();
|
||||
if (themeDirectory != null) {
|
||||
return Lists.newArrayList(themeDirectory.list(CSS_FILTER));
|
||||
} else {
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +1,7 @@
|
||||
package com.sismics.docs.core.dao.jpa;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import com.sismics.docs.core.constant.AclTargetType;
|
||||
import com.sismics.docs.core.constant.AclType;
|
||||
import com.sismics.docs.core.constant.AuditLogType;
|
||||
import com.sismics.docs.core.constant.PermType;
|
||||
import com.sismics.docs.core.dao.jpa.dto.AclDto;
|
||||
@@ -16,36 +9,43 @@ import com.sismics.docs.core.model.jpa.Acl;
|
||||
import com.sismics.docs.core.util.AuditLogUtil;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* ACL DAO.
|
||||
*
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
public class AclDao {
|
||||
/**
|
||||
* Creates a new ACL.
|
||||
*
|
||||
*
|
||||
* @param acl ACL
|
||||
* @param userId User ID
|
||||
* @return New ID
|
||||
* @throws Exception
|
||||
*/
|
||||
public String create(Acl acl) {
|
||||
public String create(Acl acl, String userId) {
|
||||
// Create the UUID
|
||||
acl.setId(UUID.randomUUID().toString());
|
||||
|
||||
|
||||
// Create the ACL
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
em.persist(acl);
|
||||
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(acl, AuditLogType.CREATE);
|
||||
|
||||
AuditLogUtil.create(acl, AuditLogType.CREATE, userId);
|
||||
|
||||
return acl.getId();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Search ACLs by target ID.
|
||||
*
|
||||
*
|
||||
* @param targetId Target ID
|
||||
* @return ACL list
|
||||
*/
|
||||
@@ -54,32 +54,35 @@ public class AclDao {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select a from Acl a where a.targetId = :targetId and a.deleteDate is null");
|
||||
q.setParameter("targetId", targetId);
|
||||
|
||||
|
||||
return q.getResultList();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Search ACLs by source ID.
|
||||
*
|
||||
*
|
||||
* @param sourceId Source ID
|
||||
* @return ACL DTO list
|
||||
*/
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<AclDto> getBySourceId(String sourceId) {
|
||||
public List<AclDto> getBySourceId(String sourceId, AclType type) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
StringBuilder sb = new StringBuilder("select a.ACL_ID_C, a.ACL_PERM_C, a.ACL_TARGETID_C, u.USE_USERNAME_C, s.SHA_NAME_C");
|
||||
sb.append(" from T_ACL a ");
|
||||
sb.append(" left join T_USER u on u.USE_ID_C = a.ACL_TARGETID_C ");
|
||||
sb.append(" left join T_SHARE s on s.SHA_ID_C = a.ACL_TARGETID_C ");
|
||||
sb.append(" where a.ACL_DELETEDATE_D is null and a.ACL_SOURCEID_C = :sourceId ");
|
||||
|
||||
StringBuilder sb = new StringBuilder("select a.ACL_ID_C, a.ACL_PERM_C, a.ACL_TARGETID_C, ")
|
||||
.append(" u.USE_USERNAME_C, s.SHA_ID_C, s.SHA_NAME_C, g.GRP_NAME_C ")
|
||||
.append(" from T_ACL a ")
|
||||
.append(" left join T_USER u on u.USE_ID_C = a.ACL_TARGETID_C ")
|
||||
.append(" left join T_SHARE s on s.SHA_ID_C = a.ACL_TARGETID_C ")
|
||||
.append(" left join T_GROUP g on g.GRP_ID_C = a.ACL_TARGETID_C ")
|
||||
.append(" where a.ACL_DELETEDATE_D is null and a.ACL_SOURCEID_C = :sourceId and a.ACL_TYPE_C = :type ");
|
||||
|
||||
// Perform the query
|
||||
Query q = em.createNativeQuery(sb.toString());
|
||||
q.setParameter("sourceId", sourceId);
|
||||
q.setParameter("type", type.name());
|
||||
List<Object[]> l = q.getResultList();
|
||||
|
||||
|
||||
// Assemble results
|
||||
List<AclDto> aclDtoList = new ArrayList<AclDto>();
|
||||
List<AclDto> aclDtoList = new ArrayList<>();
|
||||
for (Object[] o : l) {
|
||||
int i = 0;
|
||||
AclDto aclDto = new AclDto();
|
||||
@@ -87,65 +90,82 @@ public class AclDao {
|
||||
aclDto.setPerm(PermType.valueOf((String) o[i++]));
|
||||
aclDto.setTargetId((String) o[i++]);
|
||||
String userName = (String) o[i++];
|
||||
String shareId = (String) o[i++];
|
||||
String shareName = (String) o[i++];
|
||||
aclDto.setTargetName(userName == null ? shareName : userName);
|
||||
aclDto.setTargetType(userName == null ?
|
||||
AclTargetType.SHARE.name() : AclTargetType.USER.name());
|
||||
String groupName = (String) o[i];
|
||||
if (userName != null) {
|
||||
aclDto.setTargetName(userName);
|
||||
aclDto.setTargetType(AclTargetType.USER.name());
|
||||
}
|
||||
if (shareId != null) { // Use ID because share name is nullable
|
||||
aclDto.setTargetName(shareName);
|
||||
aclDto.setTargetType(AclTargetType.SHARE.name());
|
||||
}
|
||||
if (groupName != null) {
|
||||
aclDto.setTargetName(groupName);
|
||||
aclDto.setTargetType(AclTargetType.GROUP.name());
|
||||
}
|
||||
aclDtoList.add(aclDto);
|
||||
}
|
||||
return aclDtoList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if a source is accessible to a target.
|
||||
*
|
||||
*
|
||||
* @param sourceId ACL source entity ID
|
||||
* @parm perm Necessary permission
|
||||
* @param targetId ACL target entity ID
|
||||
* @param perm Necessary permission
|
||||
* @param targetIdList List of targets
|
||||
* @return True if the document is accessible
|
||||
*/
|
||||
public boolean checkPermission(String sourceId, PermType perm, String targetId) {
|
||||
public boolean checkPermission(String sourceId, PermType perm, List<String> targetIdList) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query q = em.createQuery("select a from Acl a where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId and a.deleteDate is null");
|
||||
StringBuilder sb = new StringBuilder("select a.ACL_ID_C from T_ACL a ");
|
||||
sb.append(" where a.ACL_TARGETID_C in (:targetIdList) and a.ACL_SOURCEID_C = :sourceId and a.ACL_PERM_C = :perm and a.ACL_DELETEDATE_D is null ");
|
||||
sb.append(" union all ");
|
||||
sb.append(" select a.ACL_ID_C from T_ACL a, T_DOCUMENT_TAG dt ");
|
||||
sb.append(" where a.ACL_SOURCEID_C = dt.DOT_IDTAG_C and dt.DOT_IDDOCUMENT_C = :sourceId and dt.DOT_DELETEDATE_D is null ");
|
||||
sb.append(" and a.ACL_TARGETID_C in (:targetIdList) and a.ACL_PERM_C = :perm and a.ACL_DELETEDATE_D is null ");
|
||||
Query q = em.createNativeQuery(sb.toString());
|
||||
q.setParameter("sourceId", sourceId);
|
||||
q.setParameter("perm", perm);
|
||||
q.setParameter("targetId", targetId);
|
||||
|
||||
q.setParameter("perm", perm.name());
|
||||
q.setParameter("targetIdList", targetIdList);
|
||||
|
||||
// We have a matching permission
|
||||
if (q.getResultList().size() > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return q.getResultList().size() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an ACL.
|
||||
*
|
||||
*
|
||||
* @param sourceId Source ID
|
||||
* @param perm Permission
|
||||
* @param targetId Target ID
|
||||
* @param userId User ID
|
||||
* @param type Type
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void delete(String sourceId, PermType perm, String targetId) {
|
||||
public void delete(String sourceId, PermType perm, String targetId, String userId, AclType type) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
|
||||
// Create audit log
|
||||
Query q = em.createQuery("from Acl a where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId");
|
||||
Query q = em.createQuery("from Acl a where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId and a.type = :type and a.deleteDate is null");
|
||||
q.setParameter("sourceId", sourceId);
|
||||
q.setParameter("perm", perm);
|
||||
q.setParameter("targetId", targetId);
|
||||
q.setParameter("type", type);
|
||||
List<Acl> aclList = q.getResultList();
|
||||
for (Acl acl : aclList) {
|
||||
AuditLogUtil.create(acl, AuditLogType.DELETE);
|
||||
AuditLogUtil.create(acl, AuditLogType.DELETE, userId);
|
||||
}
|
||||
|
||||
|
||||
// Soft delete the ACLs
|
||||
q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId");
|
||||
q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId and a.type = :type and a.deleteDate is null");
|
||||
q.setParameter("sourceId", sourceId);
|
||||
q.setParameter("perm", perm);
|
||||
q.setParameter("targetId", targetId);
|
||||
q.setParameter("type", type);
|
||||
q.setParameter("dateNow", new Date());
|
||||
q.executeUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import java.util.UUID;
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.sismics.docs.core.constant.AuditLogType;
|
||||
import com.sismics.docs.core.dao.jpa.criteria.AuditLogCriteria;
|
||||
import com.sismics.docs.core.dao.jpa.dto.AuditLogDto;
|
||||
@@ -53,41 +54,33 @@ public class AuditLogDao {
|
||||
* @param criteria Search criteria
|
||||
* @param sortCriteria Sort criteria
|
||||
* @return List of audit logs
|
||||
* @throws Exception
|
||||
*/
|
||||
public void findByCriteria(PaginatedList<AuditLogDto> paginatedList, AuditLogCriteria criteria, SortCriteria sortCriteria) throws Exception {
|
||||
public void findByCriteria(PaginatedList<AuditLogDto> paginatedList, AuditLogCriteria criteria, SortCriteria sortCriteria) {
|
||||
Map<String, Object> parameterMap = new HashMap<String, Object>();
|
||||
List<String> criteriaList = new ArrayList<String>();
|
||||
|
||||
StringBuilder sb = new StringBuilder("select l.LOG_ID_C c0, l.LOG_CREATEDATE_D c1, l.LOG_IDENTITY_C c2, l.LOG_CLASSENTITY_C c3, l.LOG_TYPE_C c4, l.LOG_MESSAGE_C c5 ");
|
||||
sb.append(" from T_AUDIT_LOG l ");
|
||||
StringBuilder baseQuery = new StringBuilder("select l.LOG_ID_C c0, l.LOG_CREATEDATE_D c1, u.USE_USERNAME_C c2, l.LOG_IDENTITY_C c3, l.LOG_CLASSENTITY_C c4, l.LOG_TYPE_C c5, l.LOG_MESSAGE_C c6 from T_AUDIT_LOG l ");
|
||||
baseQuery.append(" join T_USER u on l.LOG_IDUSER_C = u.USE_ID_C ");
|
||||
List<String> queries = Lists.newArrayList();
|
||||
|
||||
// Adds search criteria
|
||||
if (criteria.getDocumentId() != null) {
|
||||
// ACL on document is not checked here, it's assumed
|
||||
StringBuilder sb0 = new StringBuilder(" (l.LOG_IDENTITY_C = :documentId and l.LOG_CLASSENTITY_C = 'Document' ");
|
||||
sb0.append(" or l.LOG_IDENTITY_C in (select f.FIL_ID_C from T_FILE f where f.FIL_IDDOC_C = :documentId) and l.LOG_CLASSENTITY_C = 'File' ");
|
||||
sb0.append(" or l.LOG_IDENTITY_C in (select a.ACL_ID_C from T_ACL a where a.ACL_SOURCEID_C = :documentId) and l.LOG_CLASSENTITY_C = 'Acl') ");
|
||||
criteriaList.add(sb0.toString());
|
||||
// ACL on document is not checked here, rights have been checked before
|
||||
queries.add(baseQuery + " where l.LOG_IDENTITY_C = :documentId ");
|
||||
queries.add(baseQuery + " where l.LOG_IDENTITY_C in (select f.FIL_ID_C from T_FILE f where f.FIL_IDDOC_C = :documentId) ");
|
||||
queries.add(baseQuery + " where l.LOG_IDENTITY_C in (select c.COM_ID_C from T_COMMENT c where c.COM_IDDOC_C = :documentId) ");
|
||||
queries.add(baseQuery + " where l.LOG_IDENTITY_C in (select a.ACL_ID_C from T_ACL a where a.ACL_SOURCEID_C = :documentId) ");
|
||||
parameterMap.put("documentId", criteria.getDocumentId());
|
||||
}
|
||||
|
||||
if (criteria.getUserId() != null) {
|
||||
StringBuilder sb0 = new StringBuilder(" (l.LOG_IDENTITY_C = :userId and l.LOG_CLASSENTITY_C = 'User' ");
|
||||
sb0.append(" or l.LOG_IDENTITY_C in (select t.TAG_ID_C from T_TAG t where t.TAG_IDUSER_C = :userId) and l.LOG_CLASSENTITY_C = 'Tag' ");
|
||||
// Show only logs from owned documents, ACL are lost on delete
|
||||
sb0.append(" or l.LOG_IDENTITY_C in (select d.DOC_ID_C from T_DOCUMENT d where d.DOC_IDUSER_C = :userId) and l.LOG_CLASSENTITY_C = 'Document') ");
|
||||
criteriaList.add(sb0.toString());
|
||||
// Get all logs originating from the user, not necessarly on owned items
|
||||
// Filter out ACL logs
|
||||
queries.add(baseQuery + " where l.LOG_IDUSER_C = :userId and l.LOG_CLASSENTITY_C != 'Acl' ");
|
||||
parameterMap.put("userId", criteria.getUserId());
|
||||
}
|
||||
|
||||
if (!criteriaList.isEmpty()) {
|
||||
sb.append(" where ");
|
||||
sb.append(Joiner.on(" and ").join(criteriaList));
|
||||
}
|
||||
|
||||
// Perform the search
|
||||
QueryParam queryParam = new QueryParam(sb.toString(), parameterMap);
|
||||
QueryParam queryParam = new QueryParam(Joiner.on(" union ").join(queries), parameterMap);
|
||||
List<Object[]> l = PaginatedLists.executePaginatedQuery(paginatedList, queryParam, sortCriteria);
|
||||
|
||||
// Assemble results
|
||||
@@ -97,6 +90,7 @@ public class AuditLogDao {
|
||||
AuditLogDto auditLogDto = new AuditLogDto();
|
||||
auditLogDto.setId((String) o[i++]);
|
||||
auditLogDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
|
||||
auditLogDto.setUsername((String) o[i++]);
|
||||
auditLogDto.setEntityId((String) o[i++]);
|
||||
auditLogDto.setEntityClass((String) o[i++]);
|
||||
auditLogDto.setType(AuditLogType.valueOf((String) o[i++]));
|
||||
|
||||