1
0
mirror of https://github.com/nerzhul/ownCloud-SMS-App.git synced 2025-06-10 17:36:16 +00:00

Compare commits

...

781 Commits

Author SHA1 Message Date
Nextcloud bot
bbb6f939ba
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-05-18 03:09:05 +00:00
Nextcloud bot
edbf7d74e9
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-05-04 03:09:07 +00:00
Nextcloud bot
a93d1ce4bc
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-05-02 03:09:49 +00:00
Nextcloud bot
733727c478
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-05-01 03:10:02 +00:00
Nextcloud bot
a2b4715f5c
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-04-30 03:10:07 +00:00
Nextcloud bot
8a6d42477e
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-04-29 03:10:15 +00:00
Nextcloud bot
7cd8d5f66f
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-04-25 03:08:27 +00:00
Nextcloud bot
57302f6689
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-04-19 03:10:01 +00:00
Nextcloud bot
d34c0474b8
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-04-04 03:10:58 +00:00
Nextcloud bot
2144f32b65
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-03-18 03:19:33 +00:00
Nextcloud bot
a83ffff1e0
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-03-17 03:09:25 +00:00
Nextcloud bot
3c37a8a22f
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-02-26 03:09:30 +00:00
Nextcloud bot
46f56742cd
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-02-25 03:18:13 +00:00
Nextcloud bot
33a1b5542e
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-02-24 03:04:37 +00:00
Nextcloud bot
8a0c8bb569
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-02-20 03:06:57 +00:00
Nextcloud bot
7b7e55853d
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-02-16 03:34:10 +00:00
Nextcloud bot
7c41ec2e42
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-02-13 03:07:17 +00:00
Nextcloud bot
e5f96a7a9e
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-02-09 03:08:10 +00:00
Nextcloud bot
e773058e6a
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-02-06 03:05:55 +00:00
Nextcloud bot
44773ffc75
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-01-29 03:02:25 +00:00
Nextcloud bot
edfbc5a77a
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-01-24 03:04:07 +00:00
Dmitriy Bogdanov
7d677a9fe2
Fix notifications not being shown on modern Androids (#221)
* Add notification channels support

* Update Travis CI config
2025-01-23 18:47:17 +01:00
Nextcloud bot
5ea03be8d6
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-01-21 03:05:12 +00:00
Nextcloud bot
4c7dd797f2
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-12-28 03:05:42 +00:00
Nextcloud bot
cd45e0ba95
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-11-18 03:01:21 +00:00
Nextcloud bot
679e39c019
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-11-11 02:59:30 +00:00
Nextcloud bot
5a975b2843
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-11-04 03:02:00 +00:00
Nextcloud bot
cdcb26b2de
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-10-24 03:21:30 +00:00
Nextcloud bot
3c889b8727
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-10-22 02:58:07 +00:00
Nextcloud bot
0fcd5e1b90
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-10-19 03:07:40 +00:00
Nextcloud bot
874444e8eb
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-10-18 03:02:38 +00:00
Nextcloud bot
b9a78a0262
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-10-13 02:58:53 +00:00
Nextcloud bot
a8479b59bf
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-10-12 02:52:12 +00:00
Nextcloud bot
fa56c85969
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-10-11 03:02:02 +00:00
Nextcloud bot
fd592fb39e
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-10-04 02:54:14 +00:00
Nextcloud bot
ba24fa7fed
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-09-18 02:55:21 +00:00
Nextcloud bot
d4cd898a05
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-09-11 03:00:37 +00:00
Nextcloud bot
cda7c306c8
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-09-02 02:57:18 +00:00
Nextcloud bot
989946263d
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-08-28 02:57:27 +00:00
Nextcloud bot
3233007191
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-08-02 02:46:55 +00:00
Nextcloud bot
6364bccfba
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-07-15 02:44:58 +00:00
Nextcloud bot
130b45a901
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-07-14 02:46:39 +00:00
Nextcloud bot
90358e07f7
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-06-02 02:44:16 +00:00
Nextcloud bot
2b18901a97
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-05-25 02:44:31 +00:00
Nextcloud bot
3834633d78
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-05-21 02:40:18 +00:00
Nextcloud bot
74b74d1265
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-05-17 02:39:43 +00:00
Nextcloud bot
18982eed14
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-04-25 02:48:04 +00:00
Nextcloud bot
8f7ce34a02
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-04-19 02:49:10 +00:00
Nextcloud bot
8f6cec4501
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-04-12 02:45:38 +00:00
Nextcloud bot
b5f144cb16
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-04-05 02:46:03 +00:00
Nextcloud bot
802fc1b68d
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-03-30 02:55:35 +00:00
Nextcloud bot
8765a0bbbf
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-03-24 02:48:37 +00:00
Nextcloud bot
e60e1d5805
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-02-25 02:46:18 +00:00
Nextcloud bot
904c9fe870
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-01-13 02:49:54 +00:00
Nextcloud bot
eb58db193d
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-01-07 02:47:48 +00:00
Nextcloud bot
cc49a6e828
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2024-01-04 02:45:46 +00:00
Nextcloud bot
c5cd46cdef
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-12-23 02:47:16 +00:00
Nextcloud bot
4da04c6cb3
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-11-29 02:47:21 +00:00
Nextcloud bot
f16702b7c2
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-11-15 02:55:26 +00:00
Nextcloud bot
c2a2afaa88
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-11-11 02:47:28 +00:00
Nextcloud bot
02967d3640
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-10-31 02:53:51 +00:00
Nextcloud bot
9be7af764d
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-10-06 02:51:33 +00:00
Nextcloud bot
14063ef66a
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-08-09 03:11:21 +00:00
Nextcloud bot
769425ab94
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-08-05 02:49:16 +00:00
Nextcloud bot
8566985222
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-08-03 02:48:09 +00:00
Nextcloud bot
8e91e061ef
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-07-30 02:41:06 +00:00
Nextcloud bot
fb19382fd2
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-07-26 03:17:01 +00:00
Nextcloud bot
68ea2f80a3
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-07-20 02:42:42 +00:00
Nextcloud bot
8d2a1bab74
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-07-19 02:42:26 +00:00
Nextcloud bot
a5a9c7967d
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-07-17 02:38:54 +00:00
Nextcloud bot
2bae3bcc93
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-07-13 02:47:05 +00:00
Nextcloud bot
f25b6ea1e3
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-07-04 02:42:24 +00:00
Nextcloud bot
c652d1a6e6
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-06-25 02:45:10 +00:00
Nextcloud bot
2450a74c32
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-06-20 02:42:07 +00:00
Nextcloud bot
10cbe87f51
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-06-19 02:36:07 +00:00
Nextcloud bot
c0a6f565c7
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-06-18 02:29:45 +00:00
Nextcloud bot
eac20f4094
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-06-17 02:30:57 +00:00
Nextcloud bot
873678d1ec
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-06-11 02:49:49 +00:00
Nextcloud bot
7a446f3243
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-06-04 02:27:47 +00:00
Nextcloud bot
c7605ede18
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-05-30 02:31:29 +00:00
Nextcloud bot
1a1a88bd44
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-05-21 02:30:43 +00:00
Nextcloud bot
010db03f37
Fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-05-12 04:44:33 +00:00
Nextcloud bot
24f3bba827
Fix(l10n): 🔠 Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-03-31 04:49:51 +00:00
Nextcloud bot
fbfb72acdf
Fix(l10n): 🔠 Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-03-16 04:39:30 +00:00
Nextcloud bot
237c695922
Fix(l10n): 🔠 Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-01-05 03:40:22 +00:00
Nextcloud bot
22218bb556
Fix(l10n): 🔠 Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2023-01-04 06:17:04 +00:00
Nextcloud bot
c25ebc819a
Fix(l10n): 🔠 Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-12-31 08:06:31 +00:00
Nextcloud bot
8d29a90222
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-11-08 03:24:46 +00:00
Nextcloud bot
a056d07cdc
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-11-05 03:28:27 +00:00
Nextcloud bot
b0353c3de9
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-27 03:23:00 +00:00
Nextcloud bot
577ac531ee
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-20 18:09:42 +00:00
Nextcloud bot
a373de0b5d
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-19 03:27:23 +00:00
Nextcloud bot
72ff4c0d21
[tx-robot] Update transifex configuration
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-19 03:27:05 +00:00
Nextcloud bot
18b3380cdc
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-18 03:24:20 +00:00
Nextcloud bot
c1aa168642
[tx-robot] Update transifex configuration
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-18 03:24:02 +00:00
Nextcloud bot
534d4b5a3f
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-17 03:22:53 +00:00
Nextcloud bot
9e8733e4f6
[tx-robot] Update transifex configuration
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-17 03:22:37 +00:00
Nextcloud bot
e000f06939
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-16 03:22:41 +00:00
Nextcloud bot
2658f9ca62
[tx-robot] Update transifex configuration
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-16 03:22:26 +00:00
Nextcloud bot
083fc202c7
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-15 03:22:49 +00:00
Nextcloud bot
0c36ff37ac
[tx-robot] Update transifex configuration
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-15 03:22:33 +00:00
Nextcloud bot
106b615201
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-14 03:27:19 +00:00
Nextcloud bot
8040bd9b43
[tx-robot] Update transifex configuration
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-14 03:27:02 +00:00
Nextcloud bot
ddc837e580
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-13 03:22:28 +00:00
Nextcloud bot
e19b1048c5
[tx-robot] Update transifex configuration
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-13 03:22:11 +00:00
Nextcloud bot
30cfebaf87
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-12 03:22:34 +00:00
Nextcloud bot
95e6c294ae
[tx-robot] Update transifex configuration
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-12 03:22:18 +00:00
Nextcloud bot
10acdd47f8
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-11 03:20:57 +00:00
Nextcloud bot
fe052f0fdc
[tx-robot] Update transifex configuration
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-11 03:20:40 +00:00
Nextcloud bot
63b8c9c512
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-10 03:27:33 +00:00
Nextcloud bot
98698068b1
[tx-robot] Update transifex configuration
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-10 03:27:17 +00:00
Nextcloud bot
7101e5e714
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-09 03:38:10 +00:00
Nextcloud bot
7d8867da60
[tx-robot] Update transifex configuration
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-09 03:37:54 +00:00
Nextcloud bot
df68852fd5
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-08 03:22:53 +00:00
Nextcloud bot
72bb1858a6
[tx-robot] Update transifex configuration
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-08 03:22:37 +00:00
Nextcloud bot
6f56028b00
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-07 03:23:50 +00:00
Nextcloud bot
7a851a1684
[tx-robot] Update transifex configuration
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-07 03:23:35 +00:00
Nextcloud bot
527c6f535c
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-06 03:24:41 +00:00
Nextcloud bot
1cb7a81a47
[tx-robot] Update transifex configuration
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-06 03:24:25 +00:00
Nextcloud bot
93f83c216a
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-05 03:18:02 +00:00
Nextcloud bot
80ec02b767
[tx-robot] Update transifex configuration
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-05 03:17:46 +00:00
Nextcloud bot
5309e46d58
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-04 03:18:39 +00:00
Nextcloud bot
b61d82fe1e
[tx-robot] Update transifex configuration
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-04 03:18:23 +00:00
Nextcloud bot
0c6d16063c
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-03 03:23:08 +00:00
Nextcloud bot
74dd4b33a4
[tx-robot] Update transifex configuration
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-03 03:22:52 +00:00
Nextcloud bot
f0d6588c07
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-02 03:21:01 +00:00
Nextcloud bot
8302493940
[tx-robot] Update transifex configuration
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-02 03:20:41 +00:00
Nextcloud bot
fc5241d6f9
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-20 04:37:23 +00:00
Nextcloud bot
65e3c246d7
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-08-07 04:17:59 +00:00
Nextcloud bot
5ddf363b20
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-05-05 04:16:54 +00:00
Nextcloud bot
dbe2543fcf
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-04-24 04:17:58 +00:00
Nextcloud bot
3dd9acddc7
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-27 03:56:13 +00:00
Nextcloud bot
0a9d1cc191
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-19 04:44:13 +00:00
Nextcloud bot
643203c874
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-13 03:56:21 +00:00
Nextcloud bot
5fe15716ee
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-12 03:51:59 +00:00
Nextcloud bot
599fe53515
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-10-20 04:15:44 +00:00
Nextcloud bot
8a87e4d63a
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-09-21 04:05:52 +00:00
Nextcloud bot
ffd5119c98
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-09-15 04:04:00 +00:00
Nextcloud bot
11409cbead
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-09-02 04:00:32 +00:00
Nextcloud bot
78f3d79630
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-08-06 04:08:44 +00:00
Nextcloud bot
6687f92346
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-06-28 04:02:46 +00:00
Nextcloud bot
2836ac6869
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-06-21 04:04:46 +00:00
Nextcloud bot
59acbd9ef5
[tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-06-03 04:12:36 +00:00
Nextcloud bot
fcbaf713f3
[tx-robot] updated from transifex 2021-05-14 04:00:30 +00:00
Nextcloud bot
e515419d63
[tx-robot] updated from transifex 2021-05-04 04:03:32 +00:00
Nextcloud bot
17374d2183
[tx-robot] updated from transifex 2021-05-02 04:00:24 +00:00
Nextcloud bot
915617d79a
[tx-robot] updated from transifex 2021-04-28 04:04:27 +00:00
Nextcloud bot
786ee2b842
[tx-robot] updated from transifex 2021-04-27 04:06:19 +00:00
Nextcloud bot
a3d804325f
[tx-robot] updated from transifex 2021-04-18 03:59:35 +00:00
Nextcloud bot
991bde7231
[tx-robot] updated from transifex 2021-04-17 04:01:43 +00:00
Nextcloud bot
58205cc06c
[tx-robot] updated from transifex 2021-04-15 04:02:45 +00:00
Nextcloud bot
c07dabf789
[tx-robot] updated from transifex 2021-04-13 04:02:11 +00:00
Nextcloud bot
bd8b23f1e0
[tx-robot] updated from transifex 2021-04-12 04:02:11 +00:00
Nextcloud bot
3b682e25d6
[tx-robot] updated from transifex 2021-04-08 04:02:01 +00:00
Nextcloud bot
ea17951c0a
[tx-robot] updated from transifex 2021-04-07 04:03:04 +00:00
Nextcloud bot
b7e5690a81
[tx-robot] updated from transifex 2021-04-04 03:59:48 +00:00
Nextcloud bot
eabab3be02
[tx-robot] updated from transifex 2021-03-23 03:58:37 +00:00
Nextcloud bot
710e471811
[tx-robot] updated from transifex 2021-03-22 03:57:59 +00:00
Nextcloud bot
7c2eef2e88
[tx-robot] updated from transifex 2021-03-20 03:56:27 +00:00
Nextcloud bot
477e3f8319
[tx-robot] updated from transifex 2021-03-16 03:55:53 +00:00
Nextcloud bot
f49f575971
[tx-robot] updated from transifex 2021-03-04 04:00:10 +00:00
Nextcloud bot
0ceb01b2bd
[tx-robot] updated from transifex 2021-02-27 03:57:20 +00:00
Nextcloud bot
a240c1cbef
[tx-robot] updated from transifex 2021-02-24 04:02:48 +00:00
Nextcloud bot
29ff6635f2
[tx-robot] updated from transifex 2021-02-18 03:59:03 +00:00
Nextcloud bot
a63d9599e1
[tx-robot] updated from transifex 2021-02-15 03:49:58 +00:00
Nextcloud bot
5261961673
[tx-robot] updated from transifex 2021-02-13 03:51:13 +00:00
Nextcloud bot
8adc71c413
[tx-robot] updated from transifex 2021-02-09 03:50:58 +00:00
Nextcloud bot
3a0601d217
[tx-robot] updated from transifex 2021-02-08 03:51:03 +00:00
Nextcloud bot
cb1c6fb71a
[tx-robot] updated from transifex 2021-02-06 03:54:45 +00:00
Nextcloud bot
2e287d4dcb
[tx-robot] updated from transifex 2021-02-05 03:55:03 +00:00
Nextcloud bot
64e26551a6
[tx-robot] updated from transifex 2021-01-27 03:54:31 +00:00
Nextcloud bot
4ecf711a65
[tx-robot] updated from transifex 2021-01-26 03:52:56 +00:00
Nextcloud bot
8e6e5b64e4
[tx-robot] updated from transifex 2021-01-19 03:51:52 +00:00
Nextcloud bot
86ac855e9a
[tx-robot] updated from transifex 2021-01-18 03:51:00 +00:00
Nextcloud bot
aa530a204c
[tx-robot] updated from transifex 2021-01-14 03:53:41 +00:00
Nextcloud bot
bd0c9c0c97
[tx-robot] updated from transifex 2021-01-02 03:59:33 +00:00
Nextcloud bot
00c2c6e8ea
[tx-robot] updated from transifex 2020-12-30 03:55:05 +00:00
Nextcloud bot
18650d4d7b
[tx-robot] updated from transifex 2020-12-19 03:52:53 +00:00
Nextcloud bot
3b23a320bc
[tx-robot] updated from transifex 2020-12-15 03:54:07 +00:00
Nextcloud bot
1a18eb48a1
[tx-robot] updated from transifex 2020-12-10 03:59:56 +00:00
Nextcloud bot
4d17c9c5af
[tx-robot] updated from transifex 2020-12-08 03:54:22 +00:00
Nextcloud bot
07fb0e7a87
[tx-robot] updated from transifex 2020-11-30 03:49:06 +00:00
Nextcloud bot
e2c8f185f5
[tx-robot] updated from transifex 2020-11-09 03:49:02 +00:00
Nextcloud bot
c88c9bb5be
[tx-robot] updated from transifex 2020-11-08 03:49:00 +00:00
Nextcloud bot
dc04f5997b
[tx-robot] updated from transifex 2020-11-05 03:47:24 +00:00
Nextcloud bot
5e9bcc88ca
[tx-robot] updated from transifex 2020-11-02 03:48:01 +00:00
Nextcloud bot
b99951c444
[tx-robot] updated from transifex 2020-11-01 03:47:16 +00:00
Nextcloud bot
353145693d
[tx-robot] updated from transifex 2020-10-31 03:48:53 +00:00
Nextcloud bot
c57f5237ed
[tx-robot] updated from transifex 2020-10-29 03:47:24 +00:00
Nextcloud bot
be3a183633
[tx-robot] updated from transifex 2020-10-27 03:48:38 +00:00
Nextcloud bot
511e11e454
[tx-robot] updated from transifex 2020-10-19 03:43:59 +00:00
Nextcloud bot
837a3effb1
[tx-robot] updated from transifex 2020-10-17 03:44:32 +00:00
Nextcloud bot
739ac51931
[tx-robot] updated from transifex 2020-10-15 03:43:43 +00:00
Nextcloud bot
1b58f45a9f
[tx-robot] updated from transifex 2020-10-14 03:47:57 +00:00
Nextcloud bot
e5f6719d22
[tx-robot] updated from transifex 2020-10-13 03:46:24 +00:00
Nextcloud bot
151b1488d4
[tx-robot] updated from transifex 2020-10-11 03:41:24 +00:00
Nextcloud bot
171890e507
[tx-robot] updated from transifex 2020-10-10 03:48:31 +00:00
Nextcloud bot
6a4c2363e7
[tx-robot] updated from transifex 2020-09-30 03:45:27 +00:00
Nextcloud bot
598f3413cf
[tx-robot] updated from transifex 2020-09-28 03:43:36 +00:00
Nextcloud bot
ec9729ebeb
[tx-robot] updated from transifex 2020-09-22 03:43:06 +00:00
Nextcloud bot
ab60df2369
[tx-robot] updated from transifex 2020-09-21 03:37:46 +00:00
Nextcloud bot
e11bed3f31
[tx-robot] updated from transifex 2020-09-20 03:37:19 +00:00
Nextcloud bot
02375e41c5
[tx-robot] updated from transifex 2020-09-18 03:36:44 +00:00
Nextcloud bot
05380b2d39
[tx-robot] updated from transifex 2020-09-15 03:27:54 +00:00
Nextcloud bot
c19248555d
[tx-robot] updated from transifex 2020-09-14 03:25:55 +00:00
Nextcloud bot
b0785093a9
[tx-robot] updated from transifex 2020-09-12 03:25:24 +00:00
Nextcloud bot
5d244ca55a
[tx-robot] updated from transifex 2020-09-10 03:32:13 +00:00
Nextcloud bot
46327119f8
[tx-robot] updated from transifex 2020-09-09 03:25:38 +00:00
Nextcloud bot
f682438980
[tx-robot] updated from transifex 2020-09-07 03:34:43 +00:00
Nextcloud bot
dd09e43818
[tx-robot] updated from transifex 2020-09-05 03:35:03 +00:00
Nextcloud bot
150284e27b
[tx-robot] updated from transifex 2020-09-04 03:35:18 +00:00
rakekniven
f8b9665963
l10n: Triple dot to ellipsis (#224)
Reported at Transifex.

Signed-off-by: rakekniven <mark.ziegler@rakekniven.de>
2020-09-03 13:26:46 +02:00
Nextcloud bot
2aacbb13b5
[tx-robot] updated from transifex 2020-08-26 03:36:10 +00:00
Nextcloud bot
e43a172079
[tx-robot] updated from transifex 2020-08-14 03:50:28 +00:00
Nextcloud bot
d96b8a7875
[tx-robot] updated from transifex 2020-08-06 03:37:22 +00:00
Nextcloud bot
478e3718fb
[tx-robot] updated from transifex 2020-08-04 03:36:16 +00:00
Nextcloud bot
3c19e328a2
[tx-robot] updated from transifex 2020-07-04 03:33:29 +00:00
Nextcloud bot
c9ed554c6d
[tx-robot] updated from transifex 2020-07-02 03:41:28 +00:00
Nextcloud bot
5bc84f9e7f
[tx-robot] updated from transifex 2020-07-01 03:31:43 +00:00
Nextcloud bot
a6ee12a195
[tx-robot] updated from transifex 2020-06-18 03:32:33 +00:00
Nextcloud bot
86d2dce73b
[tx-robot] updated from transifex 2020-06-06 03:34:09 +00:00
Nextcloud bot
236d348aba
[tx-robot] updated from transifex 2020-05-31 03:30:58 +00:00
Nextcloud bot
98daefe3df
[tx-robot] updated from transifex 2020-05-23 03:33:35 +00:00
Nextcloud bot
abce140b8f
[tx-robot] updated from transifex 2020-05-21 03:35:26 +00:00
Nextcloud bot
548e0d871f
[tx-robot] updated from transifex 2020-05-18 03:29:07 +00:00
Nextcloud bot
633901ea99
[tx-robot] updated from transifex 2020-05-17 03:29:47 +00:00
Nextcloud bot
889e365e03
[tx-robot] updated from transifex 2020-05-08 03:31:04 +00:00
e-alfred
b1688dbfed
Added screenshots (#231) 2020-05-02 08:17:22 +02:00
Nextcloud bot
93c954ec7c
[tx-robot] updated from transifex 2020-04-11 03:38:10 +00:00
Nextcloud bot
a7d74d4686
[tx-robot] updated from transifex 2020-03-30 03:38:08 +00:00
Nextcloud bot
961c731f60
[tx-robot] updated from transifex 2020-03-25 03:38:21 +00:00
Nextcloud bot
72702ab53e
[tx-robot] updated from transifex 2020-03-16 03:41:48 +00:00
Nextcloud bot
c199f6f60d
[tx-robot] updated from transifex 2020-02-25 03:42:19 +00:00
Nextcloud bot
dc41bf3798
[tx-robot] updated from transifex 2020-02-24 03:38:37 +00:00
Nextcloud bot
53d9431c15
[tx-robot] updated from transifex 2020-02-19 03:34:19 +00:00
Nextcloud bot
4d00647cc1
[tx-robot] updated from transifex 2020-02-12 03:33:27 +00:00
Nextcloud bot
8130af570f
[tx-robot] updated from transifex 2020-02-07 03:48:09 +00:00
Nextcloud bot
fee8902acb
[tx-robot] updated from transifex 2020-02-06 03:34:17 +00:00
Nextcloud bot
333a31b056
[tx-robot] updated from transifex 2020-02-05 03:35:49 +00:00
Nextcloud bot
48dca0fbb4
[tx-robot] updated from transifex 2020-02-01 03:31:48 +00:00
Nextcloud bot
6c63d8f89b
[tx-robot] updated from transifex 2020-01-28 03:37:13 +00:00
Maxime Sibellas
9bca7b4ca2 Fix typo in french translation for value title_activity_select_account (#222) 2020-01-27 17:07:13 +01:00
Nextcloud bot
79a56e2ada
[tx-robot] updated from transifex 2020-01-26 03:32:24 +00:00
Nextcloud bot
859147b69b
[tx-robot] updated from transifex 2020-01-24 03:31:38 +00:00
Nextcloud bot
00850485e9
[tx-robot] updated from transifex 2020-01-22 03:35:35 +00:00
Nextcloud bot
7bf6b842a0
[tx-robot] updated from transifex 2020-01-20 03:32:56 +00:00
Nextcloud bot
6ad684b650
[tx-robot] updated from transifex 2020-01-19 03:34:16 +00:00
Nextcloud bot
6554f343ae
[tx-robot] updated from transifex 2020-01-16 03:37:16 +00:00
Nextcloud bot
4fc26ff2d4
[tx-robot] updated from transifex 2020-01-14 03:34:11 +00:00
Nextcloud bot
d15945c1ca
[tx-robot] updated from transifex 2020-01-11 03:34:09 +00:00
Nextcloud bot
8bf4ab7c93
[tx-robot] updated from transifex 2020-01-09 04:29:15 +00:00
Niedermann IT-Dienstleistungen
600cb29c9c Update app icon background layer (#218)
see nextcloud/talk-android#695

Please do not forget to update the Play Store logo :)
2020-01-05 16:43:04 +01:00
Nextcloud bot
6840c982ca
[tx-robot] updated from transifex 2020-01-04 04:29:34 +00:00
Nextcloud bot
04b35c398d
[tx-robot] updated from transifex 2020-01-03 04:26:00 +00:00
Nextcloud bot
070a53167a
[tx-robot] updated from transifex 2020-01-02 04:26:26 +00:00
Nextcloud bot
badb5aaaa1
[tx-robot] updated from transifex 2020-01-01 04:26:08 +00:00
Nextcloud bot
7e73e2336b
[tx-robot] updated from transifex 2019-12-25 04:26:01 +00:00
Nextcloud bot
598f1fcf9a
[tx-robot] updated from transifex 2019-12-23 04:29:33 +00:00
Nextcloud bot
13f164b867
[tx-robot] updated from transifex 2019-12-22 04:32:42 +00:00
Nextcloud bot
0c5290118f
[tx-robot] updated from transifex 2019-12-06 03:20:24 +00:00
Nextcloud bot
58a4a7bc4e
[tx-robot] updated from transifex 2019-12-03 03:18:56 +00:00
Nextcloud bot
5bfbb005a5
[tx-robot] updated from transifex 2019-12-02 03:16:40 +00:00
Nextcloud bot
ad2f7c10f7
[tx-robot] updated from transifex 2019-11-28 03:19:20 +00:00
Nextcloud bot
82f43a885f
[tx-robot] updated from transifex 2019-11-22 03:15:02 +00:00
Nextcloud bot
76f2388f02
[tx-robot] updated from transifex 2019-11-21 03:14:32 +00:00
Nextcloud bot
e6e1f1a721
[tx-robot] updated from transifex 2019-11-20 03:15:31 +00:00
Nextcloud bot
961a760234
[tx-robot] updated from transifex 2019-11-03 03:17:31 +00:00
Nextcloud bot
5ea297104c
[tx-robot] updated from transifex 2019-10-28 03:12:20 +00:00
Nextcloud bot
855de0f86e
[tx-robot] updated from transifex 2019-10-25 03:13:09 +00:00
Nextcloud bot
191fabbca4
[tx-robot] updated from transifex 2019-09-24 03:14:06 +00:00
Nextcloud bot
d5149f729b
[tx-robot] updated from transifex 2019-09-21 03:09:17 +00:00
Nextcloud bot
5f5cc55259
[tx-robot] updated from transifex 2019-09-16 03:03:26 +00:00
Nextcloud bot
50d4de987e
[tx-robot] updated from transifex 2019-09-08 03:17:59 +00:00
Nextcloud bot
584524a1c9
[tx-robot] updated from transifex 2019-09-06 03:10:56 +00:00
Nextcloud bot
1730a48f23
[tx-robot] updated from transifex 2019-09-02 03:11:14 +00:00
Nextcloud bot
9f4219bd68
[tx-robot] updated from transifex 2019-08-30 03:14:03 +00:00
Nextcloud bot
3cdf1337f8
[tx-robot] updated from transifex 2019-08-28 03:18:15 +00:00
Nextcloud bot
9a1f187058
[tx-robot] updated from transifex 2019-08-20 03:10:47 +00:00
Nextcloud bot
b09f60cc4e
[tx-robot] updated from transifex 2019-08-18 03:15:36 +00:00
Nextcloud bot
9e5297f2aa
[tx-robot] updated from transifex 2019-08-12 03:10:29 +00:00
e-alfred
080ad35048 Remove dead Google Play links (#210) 2019-08-07 11:30:26 +02:00
Nextcloud bot
c64745d885
[tx-robot] updated from transifex 2019-07-19 03:22:56 +00:00
Nextcloud bot
f66e101e76
[tx-robot] updated from transifex 2019-06-26 03:03:11 +00:00
Nextcloud bot
33a62654cb
[tx-robot] updated from transifex 2019-06-18 03:02:18 +00:00
Nextcloud bot
dc13d4a21c
[tx-robot] updated from transifex 2019-06-08 02:53:19 +00:00
Nextcloud bot
bbef52e03a
[tx-robot] updated from transifex 2019-06-01 02:53:10 +00:00
Nextcloud bot
812c801042
[tx-robot] updated from transifex 2019-05-28 03:07:10 +00:00
Nextcloud bot
926182f997
[tx-robot] updated from transifex 2019-05-22 03:02:36 +00:00
Nextcloud bot
f75a6d40f1
[tx-robot] updated from transifex 2019-05-21 07:15:29 +00:00
Nextcloud bot
b3aca1c6b3
[tx-robot] updated from transifex 2019-05-13 00:53:15 +00:00
Nextcloud bot
5a02dfa045
[tx-robot] updated from transifex 2019-05-08 01:11:40 +00:00
Nextcloud bot
76f901c81f
[tx-robot] updated from transifex 2019-05-07 00:53:48 +00:00
Nextcloud bot
6a13d7ac83
[tx-robot] updated from transifex 2019-05-01 00:53:07 +00:00
Nextcloud bot
f1e5df9e02
[tx-robot] updated from transifex 2019-04-30 00:54:06 +00:00
Nextcloud bot
6bbd5da44f
[tx-robot] updated from transifex 2019-04-28 00:55:09 +00:00
Nextcloud bot
523603c1e3
[tx-robot] updated from transifex 2019-04-26 00:52:07 +00:00
Nextcloud bot
f953e6cd09
[tx-robot] updated from transifex 2019-04-20 00:53:01 +00:00
Nextcloud bot
123ce0adb3
[tx-robot] updated from transifex 2019-04-19 00:54:31 +00:00
Nextcloud bot
7c14bff700
[tx-robot] updated from transifex 2019-04-18 00:54:49 +00:00
Loïc Blot
d92ed7d4b1
Make gp_translation_version non translatable 2019-04-17 09:51:42 +02:00
Nextcloud bot
4f265773b8
[tx-robot] updated from transifex 2019-04-17 00:55:18 +00:00
Nextcloud bot
da0082ccac
[tx-robot] updated from transifex 2019-04-16 01:05:57 +00:00
Nextcloud bot
363248f580
[tx-robot] updated from transifex 2019-04-15 00:54:35 +00:00
Nextcloud bot
13a44da126
[tx-robot] updated from transifex 2019-04-13 00:56:52 +00:00
Nextcloud bot
9409ca64e2
[tx-robot] updated from transifex 2019-04-12 00:54:52 +00:00
Nextcloud bot
27942004b5
[tx-robot] updated from transifex 2019-04-11 00:54:39 +00:00
rakekniven
15aa29910d Update google_playstore_strings.xml (#204)
Changed project names.

Reported at Transifex
2019-04-10 11:20:57 +02:00
Nextcloud bot
6ceee7b012
[tx-robot] updated from transifex 2019-04-09 00:54:46 +00:00
Nextcloud bot
b5957f0c06
[tx-robot] updated from transifex 2019-04-01 00:57:54 +00:00
Nextcloud bot
d4d7fce3e8
[tx-robot] updated from transifex 2019-03-31 01:57:04 +00:00
Nextcloud bot
a7b33e58ac
[tx-robot] updated from transifex 2019-03-29 01:56:44 +00:00
Nextcloud bot
7d8db76067
[tx-robot] updated from transifex 2019-03-25 01:50:12 +00:00
Nextcloud bot
5106432c2b
[tx-robot] updated from transifex 2019-03-24 01:50:33 +00:00
Nextcloud bot
42a2a55cae
[tx-robot] updated from transifex 2019-03-22 01:50:19 +00:00
Nextcloud bot
a9cf377063
[tx-robot] updated from transifex 2019-03-19 01:58:13 +00:00
Loïc Blot
bccfbae82c
Mark pref_lastmsgdate as non translatable
Fix #202
2019-03-18 11:28:09 +01:00
Nextcloud bot
cfb605928d
[tx-robot] updated from transifex 2019-03-18 01:55:44 +00:00
Nextcloud bot
5cae31082b
[tx-robot] updated from transifex 2019-03-15 01:58:43 +00:00
Nextcloud bot
0cda521540
[tx-robot] updated from transifex 2019-03-14 01:51:18 +00:00
Nextcloud bot
d26712cd89
[tx-robot] updated from transifex 2019-03-12 01:50:08 +00:00
Nextcloud bot
a05a30b0e2
[tx-robot] updated from transifex 2019-03-05 02:01:04 +00:00
Nextcloud bot
d9ca696db0
[tx-robot] updated from transifex 2019-03-04 01:47:38 +00:00
Nextcloud bot
a81c5ad464
[tx-robot] updated from transifex 2019-03-03 01:48:24 +00:00
Nextcloud bot
dc593807d4
[tx-robot] updated from transifex 2019-03-02 01:48:18 +00:00
Nextcloud bot
3e5f5705d2
[tx-robot] updated from transifex 2019-03-01 01:55:28 +00:00
Nextcloud bot
8a10e28a1a
[tx-robot] updated from transifex 2019-02-24 01:57:16 +00:00
Nextcloud bot
2e0ce0706d
[tx-robot] updated from transifex 2019-02-17 01:55:59 +00:00
Nextcloud bot
484c25dabe
[tx-robot] updated from transifex 2019-02-16 01:53:12 +00:00
Nextcloud bot
5fc47f03e8
[tx-robot] updated from transifex 2019-02-14 01:53:18 +00:00
Nextcloud bot
b012971a8c
[tx-robot] updated from transifex 2019-02-10 01:56:16 +00:00
Nextcloud bot
816e975ca0
[tx-robot] updated from transifex 2019-02-09 01:54:53 +00:00
Nextcloud bot
649c97d31e
[tx-robot] updated from transifex 2019-02-07 01:57:15 +00:00
Nextcloud bot
4f139906a5
[tx-robot] updated from transifex 2019-02-06 01:48:33 +00:00
Nextcloud bot
1e206b55c8
[tx-robot] updated from transifex 2019-02-03 01:49:09 +00:00
Nextcloud bot
5b184b0490
[tx-robot] updated from transifex 2019-02-02 01:48:21 +00:00
Nextcloud bot
9847559732
[tx-robot] updated from transifex 2019-01-30 01:50:07 +00:00
Nextcloud bot
ceea85925f
[tx-robot] updated from transifex 2019-01-29 01:47:51 +00:00
Nextcloud bot
e81ed347e7
[tx-robot] updated from transifex 2019-01-28 01:48:48 +00:00
Nextcloud bot
32d6864e7c
[tx-robot] updated from transifex 2019-01-27 01:51:11 +00:00
Nextcloud bot
50683049fd
[tx-robot] updated from transifex 2019-01-26 01:52:45 +00:00
Nextcloud bot
edb117a020
[tx-robot] updated from transifex 2019-01-23 01:53:54 +00:00
Nextcloud bot
1a89b1d5f1
[tx-robot] updated from transifex 2019-01-21 01:52:57 +00:00
Nextcloud bot
67f60015ec
[tx-robot] updated from transifex 2019-01-19 01:55:08 +00:00
Nextcloud bot
90b651f3f1
[tx-robot] updated from transifex 2019-01-18 01:57:14 +00:00
Nextcloud bot
55e17986a9
[tx-robot] updated from transifex 2019-01-17 01:54:26 +00:00
Loïc Blot
2b4a1ecf9a
Update PRIVACY_POLICY.md 2019-01-16 17:23:21 +01:00
Loïc Blot
77ab787db7 Add privacy policy inside application and repository
This should permit to prevent Google play removal due to this missing information.
2019-01-16 16:56:53 +01:00
Loïc Blot
d88836e7d2
Update README.md links 2019-01-16 13:59:58 +01:00
Nextcloud bot
f5147e1bc0
[tx-robot] updated from transifex 2019-01-15 01:57:23 +00:00
Loic Blot
6bd1ce5ae9
Version 2.0.6 - ncsmsgo updated to latest golang runtime (1.11.4) 2019-01-14 19:54:10 +01:00
Nextcloud bot
58a753c921
[tx-robot] updated from transifex 2019-01-14 01:54:42 +00:00
Nextcloud bot
3668bbff51
[tx-robot] updated from transifex 2019-01-09 01:55:43 +00:00
Loic Blot
990de7450b
Version fixes 2018-12-22 18:38:14 +01:00
Loic Blot
6b9c825ed9
Version 2.0.5 2018-12-22 18:27:56 +01:00
Loic Blot
c4598208ac
Update maxSdkVersion 2018-12-22 18:24:31 +01:00
Loic Blot
03d18ae1e2
Update target version & dependencies 2018-12-22 18:23:40 +01:00
Loic Blot
3bb087663a
Gradle updates 2018-12-22 18:23:40 +01:00
Nextcloud bot
a43e3f410c
[tx-robot] updated from transifex 2018-12-01 01:50:21 +00:00
Nextcloud bot
a836408cae
[tx-robot] updated from transifex 2018-11-29 01:53:15 +00:00
Nextcloud bot
e2e7ff783d
[tx-robot] updated from transifex 2018-11-13 01:55:21 +00:00
Nextcloud bot
201da17578
[tx-robot] updated from transifex 2018-11-10 01:52:17 +00:00
Nextcloud bot
6e8a2d10b9
[tx-robot] updated from transifex 2018-11-06 01:52:33 +00:00
Nextcloud bot
dfc9f81695
[tx-robot] updated from transifex 2018-10-13 00:44:57 +00:00
Nextcloud bot
8d6b6b20e8
[tx-robot] updated from transifex 2018-09-27 00:54:37 +00:00
Nextcloud bot
261da28edb
[tx-robot] updated from transifex 2018-09-26 00:54:46 +00:00
Nextcloud bot
7d00089898
[tx-robot] updated from transifex 2018-09-25 00:54:42 +00:00
Nextcloud bot
ceefd6c5d7
[tx-robot] updated from transifex 2018-09-22 00:54:44 +00:00
Nextcloud bot
54ba895d1d
[tx-robot] updated from transifex 2018-09-21 01:01:52 +00:00
Nextcloud bot
75eb21fb4a
[tx-robot] updated from transifex 2018-09-17 00:55:16 +00:00
Nextcloud bot
3243292a66
[tx-robot] updated from transifex 2018-09-13 00:53:33 +00:00
Nextcloud bot
bc7d23970e
[tx-robot] updated from transifex 2018-09-08 00:53:13 +00:00
Nextcloud bot
28cfd0ce2a
[tx-robot] updated from transifex 2018-08-31 00:51:23 +00:00
Nextcloud bot
c50ce0e89c
[tx-robot] updated from transifex 2018-08-08 00:53:12 +00:00
Nextcloud bot
6594fee930
[tx-robot] updated from transifex 2018-07-23 00:49:25 +00:00
Nextcloud bot
753505141f
[tx-robot] updated from transifex 2018-07-20 00:49:11 +00:00
Nextcloud bot
c2cbee5cef
[tx-robot] updated from transifex 2018-07-17 00:48:35 +00:00
Nextcloud bot
cf3c7da645
[tx-robot] updated from transifex 2018-07-16 00:47:35 +00:00
Nextcloud bot
b500d02695
[tx-robot] updated from transifex 2018-07-14 00:47:55 +00:00
Nextcloud bot
e5573e7f56
[tx-robot] updated from transifex 2018-07-13 00:50:40 +00:00
Nextcloud bot
397a766663
[tx-robot] updated from transifex 2018-07-10 09:15:10 +00:00
Nextcloud bot
b06b50a8be
[tx-robot] updated from transifex 2018-07-06 00:44:19 +00:00
Nextcloud bot
8a628abb29
[tx-robot] updated from transifex 2018-06-21 00:53:11 +00:00
Nextcloud bot
cb2a8835d3
[tx-robot] updated from transifex 2018-06-15 00:52:29 +00:00
Loic Blot
7b9f035cd0
Update gradle tool 2018-06-10 16:16:25 +02:00
Loic Blot
96cdeaa2f0
Update deps & gradle 2018-06-10 16:08:42 +02:00
Nextcloud bot
92ef7cf592
[tx-robot] updated from transifex 2018-05-31 00:51:28 +00:00
Nextcloud bot
3b8f63d419
[tx-robot] updated from transifex 2018-05-20 00:51:48 +00:00
Nextcloud bot
c1fc5571bf
[tx-robot] updated from transifex 2018-05-18 00:56:43 +00:00
Nextcloud bot
f76798cb9e
[tx-robot] updated from transifex 2018-05-12 00:50:50 +00:00
Nextcloud bot
9ff749182b
[tx-robot] updated from transifex 2018-04-25 00:47:27 +00:00
Nextcloud bot
94818906ae
[tx-robot] updated from transifex 2018-04-22 00:46:31 +00:00
Niedermann IT-Dienstleistungen
9e597fb58c Adaptive icons and drawer adjustments (#188)
* Added adaptive icon

* Adjust drawer header to Notes app
2018-04-10 22:51:03 +02:00
Nextcloud bot
4a526730fa
[tx-robot] updated from transifex 2018-04-10 00:46:16 +00:00
Nextcloud bot
0bed763021
[tx-robot] updated from transifex 2018-04-09 00:51:35 +00:00
Nextcloud bot
5c8ba4b370
[tx-robot] updated from transifex 2018-03-27 00:44:02 +00:00
Nextcloud bot
77dd567593
[tx-robot] updated from transifex 2018-03-25 01:41:43 +00:00
Nextcloud bot
7d017c9919
[tx-robot] updated from transifex 2018-03-23 01:43:48 +00:00
Nextcloud bot
79cb5983a2
[tx-robot] updated from transifex 2018-03-20 01:43:37 +00:00
Loic Blot
e1fad991a8
Version 2.0.4 2018-03-18 12:26:16 +01:00
Loic Blot
160688d06f
Update android sdk & build tools 2018-03-18 12:25:31 +01:00
Loic Blot
449a6b2a81
Message date is a long, not a int 2018-03-18 12:16:25 +01:00
Loic Blot
15c9617a2b
Fix iteration on restore + add a log to understand where we are in restoration process 2018-03-18 11:37:36 +01:00
Loic Blot
7af398786e
Fix some restore issue (not complete yet) 2018-03-18 11:37:33 +01:00
Nextcloud bot
99560474fb
[tx-robot] updated from transifex 2018-03-17 01:44:00 +00:00
Nextcloud bot
9a1b28cb6d
[tx-robot] updated from transifex 2018-03-02 01:47:46 +00:00
Nextcloud bot
ae910f363b
[tx-robot] updated from transifex 2018-02-27 01:53:01 +00:00
Nextcloud bot
dd9be98ac8
[tx-robot] updated from transifex 2018-02-22 01:45:08 +00:00
Nextcloud bot
b1ab14f2b0
[tx-robot] updated from transifex 2018-02-21 01:45:41 +00:00
Nextcloud bot
cf7611999c
[tx-robot] updated from transifex 2018-02-20 01:48:56 +00:00
Loic Blot
60ac0afbea
Fix aar date issues due to overflow in 32b golang int in the JNI interface.
Use int64
2018-02-17 18:37:10 +01:00
Nextcloud bot
a1db1d3c03
[tx-robot] updated from transifex 2018-02-17 01:45:49 +00:00
Nextcloud bot
1451943e68
[tx-robot] updated from transifex 2018-02-14 01:43:43 +00:00
Nextcloud bot
6d82702a8d
[tx-robot] updated from transifex 2018-02-12 23:01:39 +00:00
Loic Blot
f2348d0a46
Version 2.0.2: fetch last http error issue properly from low level go client 2018-02-12 22:57:43 +01:00
Loic Blot
95d2bfe8f9
Bump version 2.0.1 2018-02-10 22:48:39 +01:00
Loïc Blot
61ed3d2791
SMS restore now used GoLang HTTP client. (#180)
This permits to remove the whole android HTTP client from source
2018-02-10 15:59:46 +01:00
Loic Blot
8ad3b251b0
Migrate getAllSmsIds to golang implementation 2018-02-10 11:23:46 +01:00
Loic Blot
db2cc05a3a
OCHttpClient::getPhoneList now used GoLang aar
* add handleEarlyHTTPStatus to properly handle http return codes in java part
* cleanup dead code
2018-02-10 10:44:53 +01:00
Loic Blot
1f26787983
Improve ASyncContactLoad performance by using addAll instead of looping + add 2018-02-10 09:49:41 +01:00
Loic Blot
5d0b5d377b
Prepare 2.0.0 version & add Go mobile mention 2018-02-10 00:10:01 +01:00
Loic Blot
d57810b5d8 Replace C++ with Cmake with GoLang gomobile
* Add ncsmsgo.arr which is the GoLang gomobile part
See: https://gitlab.com/nerzhul/ncsmsgo
* Android-NDK is not needed anymore
* Android app getVersion() now uses the Golang part
* Use the next getLastHTTPStatus() call in getVersion()
* Android app pushSms() now uses the Golang part
* JNI SmsBuffer has been replaced with GoLang aar bindings
-> This permits to remove some java code & increase performance
* Prepare a insecure switch flag to the Golang client
2018-02-09 23:59:01 +01:00
Nextcloud bot
5317dc4fd9
[tx-robot] updated from transifex 2018-02-09 01:43:46 +00:00
Loic Blot
62747ae33e
Update gradle version 2018-02-08 22:31:43 +01:00
Loic Blot
a8fad0bd3f
Fix the build 2018-02-08 22:31:30 +01:00
Nextcloud bot
f099ca95d2
[tx-robot] updated from transifex 2018-01-24 01:42:37 +00:00
Nextcloud bot
314016b5ca
[tx-robot] updated from transifex 2018-01-15 01:42:39 +00:00
Nextcloud bot
a7917c0b45
[tx-robot] updated from transifex 2018-01-05 01:43:58 +00:00
Nextcloud bot
da2a42ba91
[tx-robot] updated from transifex 2018-01-04 01:36:44 +00:00
Nextcloud bot
108c619fe7
[tx-robot] updated from transifex 2017-12-29 01:31:45 +00:00
Tobias Kaminsky
59db354042 Fix transifex
Transifex is using values-es_419 for spanish "latin america", but this is not supported on android. This changes "values-es_419" to "values-es-rUS", which works.
2017-12-28 16:20:43 +01:00
Nextcloud bot
92930ec758
[tx-robot] updated from transifex 2017-12-21 01:31:49 +00:00
Nextcloud bot
b877807f76
[tx-robot] updated from transifex 2017-12-20 01:32:20 +00:00
Nextcloud bot
8acf838b88
[tx-robot] updated from transifex 2017-12-19 01:33:19 +00:00
Nextcloud bot
3d27756f02
[tx-robot] updated from transifex 2017-12-18 01:31:22 +00:00
Nextcloud bot
c90d3c0fc2
[tx-robot] updated from transifex 2017-12-17 01:31:47 +00:00
Nextcloud bot
5b2c22e4ba
[tx-robot] updated from transifex 2017-12-11 01:40:45 +00:00
Nextcloud bot
14916aa092
[tx-robot] updated from transifex 2017-12-09 01:40:12 +00:00
Nextcloud bot
1553bc2e0e
[tx-robot] updated from transifex 2017-12-08 14:00:40 +00:00
Nextcloud bot
98f24bdf78
[tx-robot] updated from transifex 2017-12-08 01:38:58 +00:00
Nextcloud bot
3a04abe2d7
[tx-robot] updated from transifex 2017-12-07 01:39:40 +00:00
Loic Blot
89bf53de39
Release 1.3.4 2017-12-06 21:12:26 +01:00
Loic Blot
d12bd8b835
Add a new connection error type & fix periodic sync 2017-12-06 21:11:42 +01:00
Loic Blot
56857c16d4
Fix a wrong pref_slow_sync_frequency_titles in values-en 2017-12-06 19:44:26 +01:00
Loic Blot
011b78ba30
Update sdk to v27 & build tools to 27.0.2 2017-12-06 19:04:52 +01:00
Nextcloud bot
7ede7bb350
[tx-robot] updated from transifex 2017-12-06 01:40:18 +00:00
Nextcloud bot
f5b605c5f6
[tx-robot] updated from transifex 2017-12-05 01:40:02 +00:00
Nextcloud bot
33349000d4
[tx-robot] updated from transifex 2017-12-04 01:39:30 +00:00
Nextcloud bot
5f337bd8b0
[tx-robot] updated from transifex 2017-12-03 01:40:00 +00:00
Nextcloud bot
290312e4c9
[tx-robot] updated from transifex 2017-12-02 01:40:04 +00:00
Loic Blot
2fbb0e6ca5
Release 1.3.3
Help diagnose error + better HTTP options
2017-12-01 22:46:34 +01:00
Loic Blot
580d4c1588
Enhance connection error messages
Handle each exception in OCHttpClient::execute separately to help diagnose
2017-12-01 22:45:58 +01:00
Loic Blot
141dfa40ed
Add Transfer-Encoding option chunked in requests 2017-12-01 19:45:16 +01:00
Loic Blot
ff845bc079
update tools in travis 2017-12-01 19:44:59 +01:00
Loic Blot
05d1355b76
Fix build with NDK 16 2017-12-01 19:34:13 +01:00
Loic Blot
32ab866e55
update gradle build version 2017-12-01 19:07:45 +01:00
Nextcloud bot
3514305b60
[tx-robot] updated from transifex 2017-11-28 01:37:06 +00:00
Loic Blot
f4b20b5873
Release 1.3.2 2017-11-25 12:10:42 +01:00
Nextcloud bot
84134671be
[tx-robot] updated from transifex 2017-11-14 01:36:42 +00:00
Nextcloud bot
7ca2dbe089
[tx-robot] updated from transifex 2017-11-11 01:41:44 +00:00
Nextcloud bot
3ff9208004
[tx-robot] updated from transifex 2017-11-10 08:38:34 +00:00
Nextcloud bot
94856096ed
[tx-robot] updated from transifex 2017-11-10 01:40:33 +00:00
Nextcloud bot
395142b6b0
[tx-robot] updated from transifex 2017-11-09 01:36:37 +00:00
Loic Blot
2efc7d1436
Bump version code for play store (strange...) 2017-11-08 23:48:57 +01:00
Loic Blot
9f31d3ae53
Bump to 1.3.1 2017-11-08 23:41:13 +01:00
Loic Blot
08aba120e8
Translation fix 2017-11-08 23:40:35 +01:00
Loic Blot
fe2a27f4cb
Fix some HTTP client errors, especially closing write output
This fixes issue #162
2017-11-08 23:39:35 +01:00
Nextcloud bot
37c8f29f1e
[tx-robot] updated from transifex 2017-11-08 01:38:03 +00:00
Nextcloud bot
cda995fb57
[tx-robot] updated from transifex 2017-11-07 01:34:35 +00:00
Nextcloud bot
abe6d6b40a
[tx-robot] updated from transifex 2017-11-04 01:33:04 +00:00
Loic Blot
62df914e95
Bump to 1.3.0 2017-11-03 12:21:43 +01:00
Loic Blot
a17bffb68c
Re-initiate the SSL trust manager and remove org.apache.commons.httpclient
+ fix a wrong string
2017-11-03 12:21:16 +01:00
Loic Blot
5cb6c63f16
Refactor the whole OCHttpClient to use modern android http client 2017-11-03 12:16:54 +01:00
Nextcloud bot
605fbf5ad7
[tx-robot] updated from transifex 2017-11-03 01:38:04 +00:00
Nextcloud bot
d2e7621b88
[tx-robot] updated from transifex 2017-10-31 01:30:49 +00:00
Loic Blot
c08e0fbd51
use java.net.URL instead of URI 2017-10-30 09:17:02 +01:00
Loic Blot
5c0eb4ca14
Release 1.2.5 2017-10-30 08:43:06 +01:00
Loic Blot
1701065643
Revert "removed wrongly existing launcher icon"
This reverts commit 87538e351813f41b45a852b4bdf2b27c210cd960.
2017-10-30 08:40:10 +01:00
Loic Blot
d8855fd2c6
json::escape_string: if str is null return empty string 2017-10-30 08:38:20 +01:00
Loic Blot
8e15e18b09
Code style fixes 2017-10-30 08:37:08 +01:00
Loic Blot
8d0ba298ed
Update tools 2017-10-30 08:34:31 +01:00
Loic Blot
eae84dadfb
Fix a NullPointerException reported by Play store 2017-10-30 08:34:17 +01:00
Loic Blot
a72583894e
Translation fix 2017-10-30 08:33:53 +01:00
Nextcloud bot
ad70ad4100
[tx-robot] updated from transifex 2017-10-24 00:33:21 +00:00
Nextcloud bot
2da06bb1a9
[tx-robot] updated from transifex 2017-10-23 00:30:48 +00:00
AndyScherzinger
46650c4660 use mimap instead of drawable 2017-10-22 15:58:19 +02:00
AndyScherzinger
87538e3518 removed wrongly existing launcher icon 2017-10-22 15:58:19 +02:00
Nextcloud bot
94a57138a4
[tx-robot] updated from transifex 2017-10-20 00:31:13 +00:00
Nextcloud bot
81b60c4bd8
[tx-robot] updated from transifex 2017-10-11 00:30:18 +00:00
Nextcloud bot
7ddca6ee33
[tx-robot] updated from transifex 2017-10-10 00:32:17 +00:00
Nextcloud bot
1a57f78b5c
[tx-robot] updated from transifex 2017-09-29 00:31:11 +00:00
Nextcloud bot
35bc4f0017
[tx-robot] updated from transifex 2017-09-27 00:30:03 +00:00
Nextcloud bot
395f368052
[tx-robot] updated from transifex 2017-09-22 00:30:01 +00:00
Nextcloud bot
db86583571
[tx-robot] updated from transifex 2017-09-19 00:30:01 +00:00
Nextcloud bot
6ea0b5d317
[tx-robot] updated from transifex 2017-09-16 00:29:15 +00:00
Nextcloud bot
ee5929e9eb
[tx-robot] updated from transifex 2017-09-15 00:28:33 +00:00
Nextcloud bot
70c2d6ba34
[tx-robot] updated from transifex 2017-09-14 00:28:25 +00:00
Nextcloud bot
8bb64db9ed
[tx-robot] updated from transifex 2017-09-13 00:28:49 +00:00
Loic Blot
587099dba7
Fullsync: add a complete sync message when sync is done instead of telling nothing to sync 2017-09-12 08:10:58 +02:00
Loic Blot
47abfac7c3
Fix wrong Thread created Toast
This fixes #153
2017-09-12 08:03:06 +02:00
Loic Blot
b47a36dd99
Bump 1.2.3 2017-09-11 21:57:42 +02:00
Loic Blot
3edd898d7d
Full sync is now fixed
Full sync was previously not really good, Sms were ordered by date DESC, then the last date was good and when multiple loop iterations occurs, only 1 iteration happened
SmsBuffer now owns LastMessageDate instead of AndroidSmsFetcher
Full sync is now owned by AsyncsmsSync instead of MainActivity
Last message date is more accurate and less error prone
2017-09-11 21:56:34 +02:00
Loic Blot
bd2e70c7e7
Increase versioncode too 2017-09-04 22:37:43 +02:00
Loic Blot
3986e12313
Bump to 1.2.1 2017-09-04 22:37:31 +02:00
Loic Blot
fe67a6e012
Fix date type problem on some platforms 2017-09-04 22:36:22 +02:00
Loic Blot
56eecb98bc
Remove a useless try/catch 2017-09-04 22:28:42 +02:00
Loic Blot
0407f719ef
Add SmsEntry object to remove a JsonObject unoptimized storage object 2017-09-04 22:27:36 +02:00
Loic Blot
ebfd1ccfbf
SmsBuffer: add push helper function to factorize code 2017-09-04 20:26:25 +02:00
Nextcloud bot
d950fc73ed
[tx-robot] updated from transifex 2017-09-03 00:28:55 +00:00
Loic Blot
d092ba4a4d Fix JNI implementation, don't use static methods in Java 2017-08-24 08:27:24 +02:00
Loic Blot
38e529e3f6 Drop SDK cmake 2017-08-24 08:27:24 +02:00
Loic Blot
e037a1c9b2 try cmake like this 2017-08-24 08:27:24 +02:00
Loic Blot
139e5644eb Add cmake component to travis 2017-08-24 08:27:24 +02:00
Loic Blot
b216dc3ebb Try to accept cmake license 2017-08-24 08:27:24 +02:00
Loic Blot
11bbed02b8 Don't let Java choose when cleaning SmsBuffer, do it ourself 2017-08-24 08:27:24 +02:00
Loic Blot
5e6a1fc28e SmsBuffer is now working, replace json objects with the SmsBuffer on sync 2017-08-24 08:27:24 +02:00
Loic Blot
47c2923d0e Update tools versions & prepare JNI work to reduce memory & CPU usage 2017-08-24 08:27:24 +02:00
Nextcloud bot
c52168c939
[tx-robot] updated from transifex 2017-08-24 00:30:50 +00:00
Loïc Blot
c1abafc72c SmsDataProvider: factorize field names 2017-08-22 16:50:16 +02:00
Loïc Blot
c716107c3b Add more files to .gitignore 2017-08-22 15:39:45 +02:00
Loïc Blot
1986b941a6 Update build tools to 26.0.1 2017-08-22 14:39:01 +02:00
Loïc Blot
0a785692b4 Manage versionCode and versionString in gradle directly 2017-08-22 12:27:12 +02:00
Loïc Blot
7303534911 Update travis.yml components 2017-08-22 11:38:30 +02:00
Loic Blot
7c38679287
Re-bump version (play store problem) 2017-08-22 10:19:25 +02:00
Loic Blot
4aa0d0a500
Bump versioncode 2017-08-22 10:18:39 +02:00
Loic Blot
cd3b6bb02b
Bump android versioncode 2017-08-22 10:17:46 +02:00
Loic Blot
6c0de1ddef
Prepare for Android O 2017-08-22 10:12:56 +02:00
Nextcloud bot
70ed3a30c6
[tx-robot] updated from transifex 2017-08-22 00:29:49 +00:00
Nextcloud bot
8e27201e7f
[tx-robot] updated from transifex 2017-08-21 00:29:18 +00:00
Nextcloud bot
941654684b
[tx-robot] updated from transifex 2017-08-19 00:30:13 +00:00
Nextcloud bot
33152d3152
[tx-robot] updated from transifex 2017-08-18 00:29:29 +00:00
Nextcloud bot
94fffc2af3
[tx-robot] updated from transifex 2017-08-17 00:29:14 +00:00
Nextcloud bot
b0f97b3243
[tx-robot] updated from transifex 2017-08-16 00:29:28 +00:00
Nextcloud bot
c12acea373
[tx-robot] updated from transifex 2017-08-02 00:29:06 +00:00
Loic Blot
923a10b137
Update gradle version 2017-07-29 11:41:11 +02:00
Nextcloud bot
155da3c368
[tx-robot] updated from transifex 2017-07-28 00:27:55 +00:00
Nextcloud bot
8b944550e0
[tx-robot] updated from transifex 2017-07-22 00:27:49 +00:00
Nextcloud bot
c5e6bf4f15
[tx-robot] updated from transifex 2017-07-14 00:29:25 +00:00
Nextcloud bot
23d2aae40f
[tx-robot] updated from transifex 2017-07-13 00:29:02 +00:00
Nextcloud bot
e20de7ef54
[tx-robot] updated from transifex 2017-07-12 00:28:57 +00:00
Nextcloud bot
2bef4c1576
[tx-robot] updated from transifex 2017-07-11 00:30:01 +00:00
Nextcloud bot
4f56b9249f
[tx-robot] updated from transifex 2017-07-10 00:28:53 +00:00
Nextcloud bot
f665aea164
[tx-robot] updated from transifex 2017-07-08 00:28:26 +00:00
Nextcloud bot
e76d92daa8
[tx-robot] updated from transifex 2017-07-03 00:28:12 +00:00
Nextcloud bot
4a04a506e9
[tx-robot] updated from transifex 2017-06-30 00:28:30 +00:00
Nextcloud bot
7cd99f04b8
[tx-robot] updated from transifex 2017-06-26 00:28:38 +00:00
Nextcloud bot
179774d34c
[tx-robot] updated from transifex 2017-06-20 00:28:37 +00:00
Nextcloud bot
e1bc4d443c
[tx-robot] updated from transifex 2017-06-16 00:30:02 +00:00
Nextcloud bot
5c2194e8cb
[tx-robot] updated from transifex 2017-06-15 00:29:19 +00:00
Nextcloud bot
8b9dc8204a
[tx-robot] updated from transifex 2017-06-14 00:28:04 +00:00
Nextcloud bot
9701637007
[tx-robot] updated from transifex 2017-06-11 00:27:46 +00:00
Nextcloud bot
21db9c38f3
[tx-robot] updated from transifex 2017-06-10 00:28:07 +00:00
Nextcloud bot
97eb626795
[tx-robot] updated from transifex 2017-06-09 00:28:24 +00:00
Nextcloud bot
11361dfac9
[tx-robot] updated from transifex 2017-06-08 00:28:40 +00:00
Nextcloud bot
8f79d11e53
[tx-robot] updated from transifex 2017-06-07 00:30:32 +00:00
Loic Blot
c125201c4b
Code style fixes 2017-06-07 00:26:05 +02:00
Loic Blot
4f3a5d85dd
Version 1.1.0 2017-06-07 00:01:13 +02:00
Loic Blot
0eb1af2da8
BindObjectPref now takes resId and resolve setting directly 2017-06-07 00:00:43 +02:00
Loic Blot
1d45d0a318
Add an option to disable sync notification
Fix #150
2017-06-06 23:50:20 +02:00
Loic Blot
ceba0324e1
Fix TLS context create, TLSv1.2 > 1.1 > 1.0 > SSL 2017-06-06 23:35:04 +02:00
Nextcloud bot
697a02bfed
[tx-robot] updated from transifex 2017-06-05 00:27:29 +00:00
Nextcloud bot
35297c3d2a
[tx-robot] updated from transifex 2017-06-03 00:26:35 +00:00
Nextcloud bot
c03e7f3f4b
[tx-robot] updated from transifex 2017-06-02 00:28:31 +00:00
Nextcloud bot
24ce187e95
[tx-robot] updated from transifex 2017-06-01 00:26:32 +00:00
Nextcloud bot
af15291bcd
[tx-robot] updated from transifex 2017-05-29 00:28:36 +00:00
Nextcloud bot
75c3f70dbc
[tx-robot] updated from transifex 2017-05-28 00:27:10 +00:00
Nextcloud bot
53472e8a05
[tx-robot] updated from transifex 2017-05-26 00:26:54 +00:00
Nextcloud bot
ebba06ce79
[tx-robot] updated from transifex 2017-05-25 00:26:30 +00:00
Nextcloud bot
ec7dea1c3c
[tx-robot] updated from transifex 2017-05-23 00:27:18 +00:00
Loïc Blot
d4904797f7 Contactload code cleanup 2017-05-22 14:25:31 +02:00
Loïc Blot
a5a03dbfcd More code cleanup 2017-05-22 14:10:36 +02:00
Loïc Blot
e2c65cf867 Code cleanups 2017-05-22 13:26:04 +02:00
Loïc Blot
e46a7bccf2 Add codebeat badge 2017-05-22 12:40:02 +02:00
Loic Blot
ffa033ee95
Update android build tools & gradle 2017-05-21 15:16:01 +02:00
Nextcloud bot
4b02caf75a
[tx-robot] updated from transifex 2017-05-20 00:25:43 +00:00
Nextcloud bot
55db7a3340
[tx-robot] updated from transifex 2017-05-19 00:26:56 +00:00
Nextcloud bot
f67174b55a
[tx-robot] updated from transifex 2017-05-18 00:25:17 +00:00
Nextcloud bot
559026ca82
[tx-robot] updated from transifex 2017-05-08 00:22:38 +00:00
Nextcloud bot
349f87a888
[tx-robot] updated from transifex 2017-05-07 00:22:29 +00:00
Loic Blot
d617dade24
Release 1.0.1 2017-05-03 23:11:07 +02:00
Loic Blot
4c5a43c336
Update android support library version 2017-05-03 23:09:00 +02:00
Loïc Blot
f48c09f5ec Update gradle wrapper version 2017-05-03 16:02:32 +02:00
Loïc Blot
b681967b6a Add .idea to git ignore 2017-05-03 15:47:42 +02:00
Loïc Blot
c682d7fd0d Properly handle InvalidArgumentException when URI is not valid in HTTP connector
This fixes issue #149
2017-05-03 15:47:29 +02:00
Nextcloud bot
bc9f36d80b
[tx-robot] updated from transifex 2017-05-01 00:22:30 +00:00
Nextcloud bot
fddef1167b
[tx-robot] updated from transifex 2017-04-27 00:22:11 +00:00
Loic Blot
fafb8c7388
Update gradle tools 2017-04-26 07:27:34 +02:00
Nextcloud bot
c67370ac17
[tx-robot] updated from transifex 2017-04-26 00:22:56 +00:00
Nextcloud bot
201b463904
[tx-robot] updated from transifex 2017-04-24 00:21:03 +00:00
Nextcloud bot
d20292a137
[tx-robot] updated from transifex 2017-04-23 00:20:53 +00:00
Nextcloud bot
ed592a187b
[tx-robot] updated from transifex 2017-04-21 00:21:19 +00:00
Nextcloud bot
dabc11bb75
[tx-robot] updated from transifex 2017-04-20 00:21:40 +00:00
Nextcloud bot
3def96b8bb
[tx-robot] updated from transifex 2017-04-18 00:21:30 +00:00
Nextcloud bot
aaf64a7eb9
[tx-robot] updated from transifex 2017-04-17 00:21:07 +00:00
Nextcloud bot
e943ed86d2
[tx-robot] updated from transifex 2017-04-16 00:21:10 +00:00
Nextcloud bot
a6bb55450b
[tx-robot] updated from transifex 2017-04-15 00:22:53 +00:00
Nextcloud bot
3781d2f029
[tx-robot] updated from transifex 2017-04-14 00:21:59 +00:00
Nextcloud bot
5fbc7d2664
[tx-robot] updated from transifex 2017-04-13 00:22:24 +00:00
Nextcloud bot
49933a65cd
[tx-robot] updated from transifex 2017-04-06 00:22:14 +00:00
Nextcloud bot
5d7fbb6d8a
[tx-robot] updated from transifex 2017-04-01 00:22:10 +00:00
Nextcloud bot
6ed75c90f3
[tx-robot] updated from transifex 2017-03-31 00:22:29 +00:00
Nextcloud bot
f250ebb4a8
[tx-robot] updated from transifex 2017-03-29 00:21:03 +00:00
Nextcloud bot
349fbe0f97
[tx-robot] updated from transifex 2017-03-28 00:21:23 +00:00
Nextcloud bot
f072484ea5
[tx-robot] updated from transifex 2017-03-27 00:21:29 +00:00
Nextcloud bot
f89ebdc70e
[tx-robot] updated from transifex 2017-03-26 01:21:45 +00:00
Nextcloud bot
e891f70f79
[tx-robot] updated from transifex 2017-03-25 01:22:17 +00:00
Nextcloud bot
3303942782
[tx-robot] updated from transifex 2017-03-24 01:20:42 +00:00
Loic Blot
bd5824395e
Travis build fix 2017-03-23 21:17:43 +01:00
Loic Blot
15e16237f1
Release 1.0.0
Application is stable, reliable and new design has been done by a great contributors.
2017-03-23 20:52:47 +01:00
Loic Blot
afa0fc2fac
Add a feature to reinitialize synchronization cursor 2017-03-23 20:43:32 +01:00
Andy Scherzinger
f83eebe4dd Drawer, Toolbar and minor UI optimizations (#145)
* initial add of toolbar and hamburger menu to main activity
* toolbar added to preferences and accounts screens
* fixed original string (name)
* Layout fix for content
* initial add of toolbar and hamburger menu to main activity
* remove unnecessary padding bottom
* fix styling and paint login activity in blue
* toolbar added to preferences and accounts screens
* Layout fix for content
* remove unnecessary padding bottom
* fix styling and paint login activity in blue
* fixes after rebase
* consolidate style since minVersion=16, make drawer to be behind the systembar
* optimize drawer header
* add back navigation to login activity, fix spinner background
* add back navigation
* more launcher icon to mipmap folders
* fix identation
* fix license header
* revert code style deletion, change identation
* proper notification icon
* proper resolution for login logo
* fix large notification icon
* removed unused Eclair switch since support is v16+
* only use small notification icon
* spaces->tabs
* fix toolbar style for Android 4.x
2017-03-23 20:16:31 +01:00
Loic Blot
0bc671901e
Update libraries and tools 2017-03-23 20:12:49 +01:00
Nextcloud bot
9351c7e65c
[tx-robot] updated from transifex 2017-03-23 01:20:22 +00:00
Nextcloud bot
40c4ea4543
[tx-robot] updated from transifex 2017-03-15 01:19:46 +00:00
Nextcloud bot
6214c0f92d
[tx-robot] updated from transifex 2017-03-12 01:21:49 +00:00
Nextcloud bot
a72175252a
[tx-robot] updated from transifex 2017-03-11 01:20:55 +00:00
Nextcloud bot
cc62030de2
[tx-robot] updated from transifex 2017-03-10 01:19:59 +00:00
Nextcloud bot
d3985b1c92
[tx-robot] updated from transifex 2017-03-04 01:19:00 +00:00
Nextcloud bot
f2ed7e0558
[tx-robot] updated from transifex 2017-02-28 01:19:33 +00:00
Nextcloud bot
f6684138eb
[tx-robot] updated from transifex 2017-02-27 01:32:33 +00:00
Nextcloud bot
0bcefdc30f
[tx-robot] updated from transifex 2017-02-24 01:34:52 +00:00
Nextcloud bot
f2f5597b63
[tx-robot] updated from transifex 2017-02-23 01:31:48 +00:00
Nextcloud bot
9f71d7fe9e
[tx-robot] updated from transifex 2017-02-22 01:24:03 +00:00
Nextcloud bot
2495c97a35
[tx-robot] updated from transifex 2017-02-15 01:18:37 +00:00
Nextcloud bot
4a62335d52
[tx-robot] updated from transifex 2017-02-14 01:19:07 +00:00
Loïc Blot
6c90218d69 Translation fixes (#144)
* Make some translations at not translatable
* Remove them from all translation files

This fixes #143
2017-02-13 10:49:52 +01:00
Daniel Hansson
181fa7f46a Update issue_template.md (#142) 2017-02-13 10:21:30 +01:00
Nextcloud bot
5a15fea9bf
[tx-robot] updated from transifex 2017-02-13 01:18:17 +00:00
Nextcloud bot
0e152ae380
[tx-robot] updated from transifex 2017-02-11 01:18:35 +00:00
Nextcloud bot
d8aff2dd92
[tx-robot] updated from transifex 2017-02-10 01:17:44 +00:00
Nextcloud bot
5d55417531
[tx-robot] updated from transifex 2017-02-09 01:18:06 +00:00
Nextcloud bot
1981c44988
[tx-robot] updated from transifex 2017-02-08 01:18:24 +00:00
Nextcloud bot
96cc3d6bdd
[tx-robot] updated from transifex 2017-02-07 01:20:23 +00:00
Nextcloud bot
b7b07c3bbe
[tx-robot] updated from transifex 2017-02-06 01:17:31 +00:00
Nextcloud bot
f7718a9343
[tx-robot] updated from transifex 2017-02-05 01:17:53 +00:00
Nextcloud bot
77589f9ae5
[tx-robot] updated from transifex 2017-02-04 01:19:04 +00:00
Nextcloud bot
c9c2701355
[tx-robot] updated from transifex 2017-02-03 19:00:37 +00:00
Nextcloud Bot
e76b459f75 Merge pull request #140 from MorrisJobke/transifex
add transifex config
2017-02-03 12:49:46 -06:00
Morris Jobke
3afa30cb79
add transifex config
Signed-off-by: Morris Jobke <hey@morrisjobke.de>
2017-02-03 12:47:54 -06:00
Daniel Hansson
86b909472f Translation too long, and get's cut off (#138)
A possible solution for cut off text. But, would be better to fix in the code somehow.
2017-01-31 07:59:29 +01:00
Loic Blot
60ad4f5fa5 Bump version to 0.24.3
Minor build fixes
2017-01-26 07:43:05 +01:00
Loic Blot
2da14a0bdd Bump version to 0.24.2 2017-01-26 07:40:30 +01:00
Daniel Hansson
c79faeb279 Small fix (#137)
Same as 7fd6e874eb/src/main/res/values/strings.xml
2017-01-23 23:29:03 +01:00
Daniel Hansson
f9d4025193 Update Swedish strings (#136) 2017-01-23 23:20:27 +01:00
Andy Scherzinger
32475e72e6 set new drawer menu icons and also ship them (#133) 2017-01-23 22:36:11 +01:00
Daniel Hansson
d64b702fbb Update to Nextcloud (#134) 2017-01-23 22:34:52 +01:00
Loïc Blot
5785c44484 Bump 0.24.1 2017-01-20 11:35:25 +01:00
Loïc Blot
8b482f51c4 HTTPClient: add redirection follower 2017-01-20 11:35:19 +01:00
Loïc Blot
82d1de3463 HTTPClient: send application name & version code in User Agent
Also ignore cookies like owncloud android lib & set http version to 1.1
2017-01-20 11:35:11 +01:00
Loic Blot
dccfee4377 Bump version to 0.24.0 2017-01-18 23:05:31 +01:00
Loic Blot
90d758bb4a documentation fixes 2017-01-18 23:03:32 +01:00
Loic Blot
ef1922494b Fix a layout problem with nav view in main activity 2017-01-18 23:00:24 +01:00
Loic Blot
bcefc1fdf2 HTTPClient: Permit circular redirection.
This should fixes #128
2017-01-18 23:00:18 +01:00
Ner'zhul
f1188819f2 Add travis build support (#126)
Also update build tools to 25.0.1
2016-12-24 12:52:31 +01:00
Ner'zhul
e831b91f44 README.md: drop dead link 2016-12-24 11:24:04 +01:00
Niedermann IT-Dienstleistungen
28a28907a8 Update README.md (#124)
- Restructured
- Emojis
- ownCloud -> Nextcloud
- Typos
2016-12-24 11:23:20 +01:00
Loic Blot
6b5e809f55 Notify we didn't need owncloud Android library anymore 2016-12-21 20:58:20 +01:00
Ner'zhul
14a3cb1a3b Typo fix 2016-12-21 20:56:29 +01:00
Niedermann IT-Dienstleistungen
c3232dca60 Move to Nextcloud (#118)
* Move to Nextcloud (#116)

- Changed colors to Nextcloud Theme

* Move to Nextcloud (#116)

- Adjusted strings and some translations

* Move to Nextcloud (#116)

- Adjust icon color
2016-12-21 20:55:17 +01:00
Loic Blot
1257de1a0a Bump version to 0.23.2 2016-12-20 22:18:22 +01:00
Loic Blot
62ad5b1d34 Import some sources from legacy HTTP Client to permit using self signed certificates like before
It's not the perfect solution but this works

Rename HTTPRequestBuilder to OCHttpClient & make it child of HttpClient
2016-12-20 22:17:44 +01:00
Loic Blot
f0ec0f8b9f Use commons-httpclient from maven instead of embedded 2016-12-20 21:54:49 +01:00
Loic Blot
25e2d13409 Rewrite our HTTP Client to remove the owncloud library dependency and doing authentication ourselves
* Fixes issue #122
* Cleanup the code
* Drop owncloud library support, this will help the unofficial F-Droid build
2016-12-20 21:34:52 +01:00
Loic Blot
f2082e5215 Gradle update 2016-12-20 21:23:26 +01:00
Ner'zhul
03d7ac2b3e Update README.md 2016-12-14 23:34:27 +01:00
Ner'zhul
87fa2b70bb Remove outdated mention to jenkins 2016-12-14 23:33:29 +01:00
Boris Kraut
54f67e5200 Fix classpath dependencies (#115)
com.android.tools.build:gradle:2.2.2 is not in mavenCentral, but only in jcenter.
2016-12-11 00:48:14 +01:00
Loic Blot
4de4230dbb Release 0.23.0 2016-12-09 08:35:57 +01:00
Loic Blot
1bb93a5068 Verify connection when restoring messages 2016-12-08 22:14:29 +01:00
Loic Blot
819fa4543d Don't restore already existing messages 2016-12-08 21:43:32 +01:00
Loic Blot
9bb0a7d2f2 Add a label for progression, show it to user and fix sms restauration.
This is almost finished but needs some polish
2016-12-08 21:15:58 +01:00
Loic Blot
13ad6237fe Restore the default SMS app at the end 2016-12-08 01:18:28 +01:00
Loic Blot
184d2e12fd Add some preparations for progress 2016-12-08 01:14:12 +01:00
Loic Blot
b13e98e2dc Feedback when restore is finished 2016-12-08 00:58:27 +01:00
Loic Blot
09ab64044d Now require Android 4.1 or greater. Also better feed back on restore messages for Android 4.3 or lesser versions 2016-12-08 00:33:41 +01:00
Loic Blot
e06377d64b Permit to restore messages, but it's not finished yet
Remaining problems:
* date is not properly retrieved on the phone
* no feedback when connectivity errors
* no feedback when processing
* no feedback when finishing
2016-12-08 00:23:16 +01:00
Loic Blot
45d7352c32 Require Android 4.4 to restore messages 2016-12-07 08:41:31 +01:00
Loic Blot
52e7fa109b Prepare some SMS permissions 2016-12-07 08:34:37 +01:00
Loic Blot
f05eabe94b Add a loop which retrieve all messages without doing anything 2016-12-07 00:13:10 +01:00
Loic Blot
80a91635dd RestoreMessagesActivity: find account to load
+ some fixes
2016-12-06 23:17:46 +01:00
Loic Blot
f76de90a6e Better OCSMSOwncloudClient object creation 2016-12-06 23:04:51 +01:00
Loic Blot
a3088e7718 Move request building outside of OCSMSOwncloudClient, adding HTTPRequestBuilder 2016-12-06 22:50:39 +01:00
Loic Blot
65c2b9b99f Rename SmsFetcher to AndroidSmsFetcher 2016-12-06 20:37:22 +01:00
Loic Blot
ce88024258 Update android support lib 2016-12-06 20:00:05 +01:00
Loic Blot
36fb1e8164 Tag version 0.22.2 2016-11-02 08:12:12 +01:00
Loic Blot
8395ac5bb4 SmsFetcher: do the cursor checks directly in SmsDataProviderAPI 2016-11-02 08:09:06 +01:00
Loic Blot
8212ef63aa SmsFetcher: improve robustness of the synchronization 2016-11-02 08:03:51 +01:00
Loic Blot
e8ce83a232 Gradle update 2016-11-02 07:43:15 +01:00
Loic Blot
fbe817b0f3 Version 0.22.1 is now out and published 2016-10-22 19:45:53 +02:00
Loic Blot
3a4ea1713f Update tools versions for building project 2016-10-22 19:24:48 +02:00
Loic Blot
fda38cf855 when using bulk limit order by date DESC not _id ASC.
this permit to sync recent messages instead of older when doing sync now action
2016-10-22 19:20:04 +02:00
Loic Blot
92713ded76 MailboxID enum now provided directly the URI 2016-10-22 19:11:37 +02:00
Loic Blot
792b6137fd Refactor SMSFetcher 2016-10-22 18:44:23 +02:00
Loic Blot
19e564213b Add binding preferences 2016-10-22 17:31:33 +02:00
Loic Blot
b0d6c830e3 Add minimum sync chars configuration 2016-10-22 17:28:57 +02:00
Loic Blot
6f26634744 Update gradle version 2016-10-22 17:27:20 +02:00
Loic Blot
a4f016f069 set logging to info not debug 2016-10-07 09:08:43 +02:00
Loic Blot
d598602cba set default minimumCharsForSync to zero atm to restore app sync function 2016-10-07 08:43:23 +02:00
Ruslan
523a78c029 Update Russian localization (#111) 2016-09-27 17:37:37 +02:00
Loic Blot
4f509d5419 Gradle update + visibility fix on SmsFetcher 2016-09-23 00:09:43 +02:00
Loic Blot
929c4d2f76 Notify that the feature is not already implement for restore_messages & prepare bump version (not ready yet) 2016-09-07 23:56:54 +02:00
Loic Blot
c934f0deeb Sync now: add a toast to give feedback about nothing to sync
SmsSyncAdapter: make android studio happy

Drop .observers.SmsObserverService which is unused
2016-08-29 00:31:44 +02:00
Loic Blot
08b54f8aa0 Fix null cursor on lastmessage and prevent a double cursor close in same func 2016-08-29 00:16:17 +02:00
Loic Blot
783ed1dc00 Fix inconsistent array in french & spanish 2016-08-29 00:09:50 +02:00
Loic Blot
00000e84c6 Add some useful logging to permit better troubleshoot, and various fixes
* Properly close cursors for database
* Implement filtering for sender number size
* Remove systemd strings for chinese translation
2016-08-29 00:07:52 +02:00
Loic Blot
0e71ca5fa1 Cleanup a little bit SmsDataProvider 2016-08-28 22:31:03 +02:00
Loic Blot
58ef25883c Prepare new preference to manage minimum length senders to allow for sync 2016-08-28 14:19:05 +02:00
Aaron LI
ba672a2ab9 Add zh-rCN translation (#104)
* Add zh-rCN translation

* Fix minor typos in strings.xml

* Update zh-rCN translations against latest master.

* Minor adjustments to zh-rCN translations.
2016-08-28 08:48:50 +02:00
Ner'zhul
72f6c8d6b1 Merge pull request #99 from enoch85/patch-1
Fix a typo in template
2016-08-27 21:02:39 +02:00
Loic Blot
2acfb6655d Refactor a little bit LoginActitivy error handlers 2016-08-27 21:01:00 +02:00
Loic Blot
678fe0b720 Properly handle 404 NOT FOUND at account creation 2016-08-27 20:41:25 +02:00
Loic Blot
38c933477f Gradle, SDK & android support library update 2016-08-27 20:17:04 +02:00
Loic Blot
6203b8b989 Change a little bit account menu to show actions to do on an account & do some code cleanups 2016-07-21 23:34:27 +02:00
Ner'zhul
c27a57eda4 Merge pull request #105 from svetlemodry/master
czech translation update
2016-06-20 10:02:54 +02:00
Jaroslav Lichtblau
77e16e67c1 czech translation update
for owncloud sms app
2016-06-19 11:13:37 +02:00
Ner'zhul
8da4611b37 Merge pull request #103 from thfree/patch-5
update RU strings.xml
2016-05-17 10:40:34 +02:00
Ruslan
50b045e01a update RU strings.xml 2016-05-17 11:21:43 +03:00
Loic Blot
7c75034e84 Update libraries 2016-05-13 00:27:53 +02:00
Loic Blot
c5519c1dd0 Add another new future call and update build tools 2016-05-12 23:34:48 +02:00
Loic Blot
8da282db98 drop useless createGetSmsIdListWithStateRequest call & api v2 things
* rename OC_GET_PHONELIST to OC_V2_GET_PHONELIST
* declare OC_V2_GET_MESSAGES
* declare OC_V2_GET_MESSAGES_PHONE
2016-05-12 23:13:20 +02:00
Loic Blot
eb7786de0b Fix some project warnings 2016-04-30 01:20:42 +02:00
Loic Blot
3f3fe0d239 Fix a gradle warning for AndroidManifest.xml 2016-04-30 00:53:25 +02:00
Loic Blot
3978801374 Upgrade Android compat libraries to 23.3.0 2016-04-30 00:49:11 +02:00
Loic Blot
b3be3130b3 Forget files for Android 6.0
Fixes #102
2016-04-09 18:12:29 +02:00
Loic Blot
6759409188 0.21.2: Android 6.0 permissions fixes 2016-04-08 08:55:18 +02:00
Loic Blot
d7e1ec729f Crashfix -> 0.21.1 2016-04-06 09:13:03 +02:00
Loic Blot
1fbb1525c6 Merge branch 'master' of https://github.com/nerzhul/ownCloud-SMS-App 2016-04-04 23:33:08 +02:00
Loic Blot
7cf4bc8920 Release 0.21.0 2016-04-04 23:32:41 +02:00
Ner'zhul
8805869542 Merge pull request #100 from est31/master
Fix gradle build, remove unused library
2016-03-18 14:37:25 +01:00
est31
c5549e735f Fix gradle build and use jitpack 2016-03-18 12:26:41 +01:00
Loic Blot
e994dc1026 SDK v24 is not released yet (Android N) 2016-03-18 11:48:32 +01:00
Loic Blot
ea27e85d94 Fix mandatory constructor 2016-03-18 11:44:49 +01:00
Loic Blot
1cced8b2e4 Re-import nrzAndroidLib, this lib will be deprecated 2016-03-18 11:39:50 +01:00
Loic Blot
42e12a2081 Code cleanup & perf improvements by using members instead of object return 2016-03-18 11:13:43 +01:00
Loic Blot
114cc84ce6 Compile with newer build tools 2016-03-18 11:13:43 +01:00
Daniel Hansson
1cdeac34e8 typo 2016-03-17 20:11:17 +01:00
Ner'zhul
09ada641e5 Merge pull request #96 from enoch85/patch-2
no need for issue template anymore in README.md
2016-03-15 10:57:51 +01:00
Ner'zhul
f88dc16fa4 Merge pull request #95 from enoch85/patch-1
Create issue_template.md
2016-03-15 10:57:26 +01:00
Daniel Hansson
d0802a0cce no need for issue template
as it's automatically generated
2016-03-14 20:36:13 +01:00
Daniel Hansson
db8ddfaac6 Create issue_template.md 2016-03-14 20:33:05 +01:00
Loic Blot
dd34248aa5 Indent fix 2016-03-10 10:55:37 +01:00
Loic Blot
533c7ae3c0 Background sync: cancel notifications at the right moment 2016-03-10 10:53:31 +01:00
Loic Blot
c01df1b64a Drop unused SmsSlowSyncService 2016-03-10 10:53:31 +01:00
Ner'zhul
6239f8b2d4 Merge pull request #93 from thfree/patch-4
Patch 4
2016-03-09 16:03:30 +01:00
Ruslan
4e2b10cbb0 Update strings.xml
upgrade
2016-03-09 15:31:59 +03:00
Ruslan
c3152ae6b0 Update google_playstore_strings.xml
upgrade
2016-03-09 15:29:19 +03:00
Ner'zhul
cfced904b7 Merge pull request #91 from thfree/patch-3
Rename src/main/values-ru/strings.xml to src/main/res/values-ru/strin…
2016-03-08 23:09:40 +01:00
Ner'zhul
39992b8bf0 Merge pull request #92 from thfree/patch-2
Rename src/main/values-ru/google_playstore_strings.xml to src/main/re…
2016-03-08 23:09:29 +01:00
Loic Blot
d11e149814 Don't vibrate or sound on our notifications 2016-03-07 21:38:25 +01:00
Loic Blot
eebe9c5917 little french translation 2016-03-07 21:27:03 +01:00
Loic Blot
c49a098961 Rewrite application navigation to a better user experience 2016-03-07 21:25:36 +01:00
Ruslan
5d60cf0157 Rename src/main/values-ru/strings.xml to src/main/res/values-ru/strings.xml 2016-03-05 22:31:38 +03:00
Ruslan
253ad880d8 Rename src/main/values-ru/google_playstore_strings.xml to src/main/res/values-ru/google_playstore_strings.xml 2016-03-05 22:30:43 +03:00
Loic Blot
c8f37561cf Drop our notification manage & use native Android Studio providen and better class 2016-03-05 19:03:17 +01:00
Loic Blot
4ea1875dd5 Bump 0.20.1 for Russian translations 2016-03-05 18:22:58 +01:00
Loic Blot
f3e7258f9a Google playstore android version fix 2016-03-05 18:21:44 +01:00
Ner'zhul
6b184406ee Merge pull request #90 from thfree/patch-1
Russian translations
2016-03-05 18:13:58 +01:00
Ruslan
a022533543 Create google_playstore_strings.xml 2016-03-04 15:04:08 +03:00
Ruslan
b5ab5feac3 Create strings.xml 2016-03-04 15:00:54 +03:00
Ner'zhul
bb1c01cf41 Merge pull request #85 from stefan-niedermann/patch-1
Add App-Store buttons and fix some typos
2016-02-09 17:27:40 +01:00
Niedermann IT-Dienstleistungen
7566419b9d Add App-Store buttons and fix some typos 2016-02-09 11:08:02 +01:00
Loic Blot
700ce423b8 v0.20.0 2016-02-06 12:21:49 +01:00
Loic Blot
af2f8d86e0 Redesign app with material design 2016-02-06 10:44:37 +01:00
Ner'zhul
8b52f814ed Merge pull request #84 from stefan-niedermann/master
New icon for android client app
2016-02-06 09:43:44 +01:00
stefan-niedermann
b42595c888 New Icon
See nerzhul/ownCloud-SMS-App#83
2016-02-05 13:20:16 +01:00
Ner'zhul
ff3a957bda Merge pull request #83 from stefan-niedermann/master
New ic_launcher
2016-02-05 09:00:13 +01:00
Stefan Niedermann
a7ad4f9bfb New ic_launcher based on https://github.com/nerzhul/ocsms/blob/master/img/app.svg 2016-02-04 19:19:38 +01:00
Loic Blot
bee1163c44 Implement some material design 2016-01-31 01:46:44 +01:00
Loic Blot
7a0ff98658 Gradle script cleanups 2016-01-31 01:32:11 +01:00
Loic Blot
6405e23d08 Improve .gitignore 2016-01-31 01:05:59 +01:00
Loic Blot
b98758cff3 Add a more stylized button for creating account 2016-01-31 01:05:25 +01:00
Loic Blot
7e87253d5f Add build.gradle, as requested by #82 2016-01-18 07:04:45 +01:00
Ner'zhul
b1d2093563 Merge pull request #79 from enoch85/patch-1
change domain name for @enoch85
2015-12-19 19:22:57 +01:00
Ner'zhul
bbfeb66a02 Merge pull request #80 from enoch85/patch-2
change @enoch85  domain name
2015-12-19 08:17:43 +01:00
Daniel Hansson
717e97f321 change domain name 2015-12-18 21:12:47 +01:00
Daniel Hansson
b8c67f4c19 change domain name 2015-12-18 21:09:59 +01:00
Ner'zhul
651cde0441 Crashfix
This fixes issue #73
existingMessages could be null because _existingInboxMessages and other variables could be null when no messages are found in the upcoming JSON file.
2015-12-01 15:13:54 +01:00
Ner'zhul
e6e0a97bef Merge pull request #74 from svetlemodry/master
Czech translation update
2015-11-29 18:43:19 +01:00
Jaroslav Lichtblau
05b267a498 Czech translation update 2015-11-26 20:57:31 +01:00
Loic Blot
c132837aac Improve restore activity a little, not perfect but better 2015-11-23 23:28:17 +01:00
Loic Blot
3895b8cf0b Remote account interface improvements 2015-11-23 21:55:29 +01:00
Loic Blot
b3f43ce1f5 Cleanup AndroidManifest 2015-11-22 14:28:54 +01:00
302 changed files with 19794 additions and 2914 deletions

8
.gitignore vendored
View File

@ -1 +1,9 @@
/bin/
ownCloudSMS.iml
/build/
ownCloudSMS-release.apk
lint.xml
.gradle/
.idea/
local.properties
ownCloud-SMS-App.iml

231
.idea/codeStyleSettings.xml generated Normal file
View File

@ -0,0 +1,231 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectCodeStyleSettingsManager">
<option name="PER_PROJECT_SETTINGS">
<value>
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
<value />
</option>
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="android" withSubpackages="true" static="false" />
<emptyLine />
<package name="com" withSubpackages="true" static="false" />
<emptyLine />
<package name="junit" withSubpackages="true" static="false" />
<emptyLine />
<package name="net" withSubpackages="true" static="false" />
<emptyLine />
<package name="org" withSubpackages="true" static="false" />
<emptyLine />
<package name="java" withSubpackages="true" static="false" />
<emptyLine />
<package name="javax" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="true" />
<emptyLine />
</value>
</option>
<option name="RIGHT_MARGIN" value="100" />
<AndroidXmlCodeStyleSettings>
<option name="USE_CUSTOM_SETTINGS" value="true" />
</AndroidXmlCodeStyleSettings>
<Objective-C-extensions>
<option name="GENERATE_INSTANCE_VARIABLES_FOR_PROPERTIES" value="ASK" />
<option name="RELEASE_STYLE" value="IVAR" />
<option name="TYPE_QUALIFIERS_PLACEMENT" value="BEFORE" />
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" />
<pair source="c" header="h" />
</extensions>
</Objective-C-extensions>
<XML>
<option name="XML_KEEP_LINE_BREAKS" value="false" />
<option name="XML_ALIGN_ATTRIBUTES" value="false" />
<option name="XML_SPACE_INSIDE_EMPTY_TAG" value="true" />
</XML>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_width</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_height</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:width</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:height</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</value>
</option>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default (1)" />
</component>
</project>

28
.travis.yml Normal file
View File

@ -0,0 +1,28 @@
---
language: android
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
jdk:
- oraclejdk8
before_install:
- yes | sdkmanager "platforms;android-28"
android:
components:
- tools
- platform-tools
- extra
- build-tools-28.0.3
- android-28
licenses:
- 'android-sdk-license-.+'
- 'google-gdk-license-.+'
- 'android-sdk-preview-license-.+'
- 'android-.*'

17
.tx/config Normal file
View File

@ -0,0 +1,17 @@
[main]
host = https://www.transifex.com
[o:nextcloud:p:nextcloud:r:android-sms]
file_filter = src/main/res/values-<lang>/strings.xml
source_file = src/main/res/values/strings.xml
source_lang = en
type = ANDROID
lang_map = ar_EG: ar-rEG, ar_KW: ar-rKW, da_DK: da-rDK, hu_HU: hu-rHU, lb_LU: lb-rLU, tzm_DZ: tzm-rDZ, ar_DZ: ar-rDZ, ar_SA: ar-rSA, arn_CL: arn-rCL, de_LI: de-rLI, fr_MC: fr-rMC, se_FI: se-rFI, ta_LK: ta-rLK, tt_RU: tt-rRU, xh_ZA: xh-rZA, co_FR: co-rFR, cy_GB: cy-rGB, en_SG: en-rSG, es_PA: es-rPA, es_UY: es-rUY, ku_IQ: ku-rIQ, rm_CH: rm-rCH, smj_SE: smj-rSE, sq_AL: sq-rAL, tr_TR: tr-rTR, uz_UZ: uz-rUZ, en_AU: en-rAU, ar_OM: ar-rOM, el_GR: el-rGR, es_EC: es-rEC, ha_NG: ha-rNG, hr_HR: hr-rHR, or_IN: or-rIN, pt_BR: pt-rBR, se_NO: se-rNO, am_ET: am-rET, en_US: en-rUS, es_PE: es-rPE, fa_IR: fa-rIR, fr_CA: fr-rCA, fy_NL: fy-rNL, hr_BA: hr-rBA, hy_AM: hy-rAM, lt_LT: lt-rLT, ar_SY: ar-rSY, ca_ES: ca-rES, en_JM: en-rJM, es_AR: es-rAR, es_PY: es-rPY, it_CH: it-rCH, kk_KZ: kk-rKZ, vi_VN: vi-rVN, es_MX: es-rMX, pt_PT: pt-rPT, uk_UA: uk-rUA, zh_CN.GB2312: zh-rBG, zu_ZA: zu-rZA, bs_BA: bs-rBA, is_IS: is-rIS, my_MM: my, quz_PE: quz-rPE, ur_PK: ur-rPK, ar_AE: ar-rAE, ar_LY: ar-rLY, ar_QA: ar-rQA, ja_JP: ja-rJP, nl_BE: nl-rBE, nso_ZA: nso-rZA, rw_RW: rw-rRW, sr_BA: sr-rBA, te_IN: te-rIN, de_AT: de-rAT, dv_MV: dv-rMV, ro_RO: ro-rRO, sv_SE: sv-rSE, fr_FR: fr-rFR, he_IL: he-rIL, ne_NP: ne-rNP, sms_FI: sms-rFI, ar_TN: ar-rTN, az_AZ: az-rAZ, de_LU: de-rLU, es_CO: es-rCO, es_NI: es-rNI, id_ID: id-rID, quz_BO: quz-rBO, sr@latin: sr-rSP, en_GB: en-rGB, es_PR: es-rPR, es_SV: es-rSV, kn_IN: kn-rIN, ar_MA: ar-rMA, bo_CN: bo-rCN, dsb_DE: dsb-rDE, ig_NG: ig-rNG, mn_CN: mn-rCN, moh_CA: moh-rCA, pa_IN: pa-rIN, ps_AF: ps-rAF, smn_FI: smn-rFI, zh_MO: zh-rMO, en@pirate: en-rpirate, gl_ES: gl-rES, th_TH: th-rTH, fr_BE: fr-rBE, nb_NO: nb-rNO, prs_AF: prs-rAF, qut_GT: qut-rGT, en_ZW: en-rZW, eu_ES: eu-rES, hsb_DE: hsb-rDE, lo_LA: lo-rLA, mk_MK: mk-rMK, sk_SK: sk-rSK, sma_SE: sma-rSE, zh_HK: zh-rHK, as_IN: as-rIN, en_MY: en-rMY, en_NZ: en-rNZ, es_GT: es-rGT, es_HN: es-rHN, hi_IN: hi-rIN, mt_MT: mt-rMT, oc_FR: oc-rFR, sa_IN: sa-rIN, tk_TM: tk-rTM, ba_RU: ba-rRU, be_BY: be-rBY, kl_GL: kl-rGL, lv_LV: lv-rLV, sah_RU: sah-rRU, yo_NG: yo-rNG, de_DE: de-rDE, es_VE: es-rVE, gd_GB: gd-rGB, ko_KR: ko-rKR, sl_SI: sl-rSI, ug_CN: ug-rCN, ar_YE: ar-rYE, en_IN: en-rIN, es_BO: es-rBO, fr_LU: fr-rLU, bn_BD: bn-rBD, bn_IN: bn-rIN, gu_IN: gu-rIN, mr_IN: mr-rIN, ar_IQ: ar-rIQ, en_CA: en-rCA, es_CR: es-rCR, es_ES: es-rES, ga_IE: ga-rIE, gsw_FR: gsw-rFR, mn_MN: mn-rMN, ru_RU: ru-rRU, sr_CS: sr-rCS, tg_TJ: tg-rTJ, bg_BG: bg-rBG, iu_CA: iu-rCA, nl_NL: nl-rNL, quz_EC: quz-rEC, sma_NO: sma-rNO, sv_FI: sv-rFI, en_IE: en-rIE, fr_CH: fr-rCH, zh_TW: zh-rTW, ar_LB: ar-rLB, br_FR: br-rFR, cs_CZ: cs-rCZ, en_BZ: en-rBZ, en_TT: en-rTT, et_EE: et-rEE, fi_FI: fi-rFI, ii_CN: ii-rCN, km_KH: km-rKH, kok_IN: kok-rIN, ml_IN: ml-rIN, se_SE: se-rSE, syr_SY: syr-rSY, af_ZA: af-rZA, en_ZA: en-rZA, es_CL: es-rCL, mi_NZ: mi-rNZ, smj_NO: smj-rNO, wo_SN: wo-rSN, ar_BH: ar-rBH, fo_FO: fo-rFO, ky_KG: ky-rKG, ms_BN: ms-rBN, nn_NO: nn-rNO, zh_SG: zh-rSG, ar_JO: ar-rJO, en_PH: en-rPH, es_DO: es-rDO, ms_MY: ms-rMY, pl_PL: pl-rPL, sr_RS: sr-rRS, zh_CN: zh-rCN, es_419: es-rUS, it_IT: it-rIT, ka_GE: ka-rGE, si_LK: si-rLK, tn_ZA: tn-rZA, de_CH: de-rCH, fil_PH: fil-rPH, sr_ME: sr-rME, sw_KE: sw-rKE, ta_IN: ta-rIN
[o:nextcloud:p:nextcloud:r:android-sms-playstore]
file_filter = src/main/res/values-<lang>/google_playstore_strings.xml
source_file = src/main/res/values/google_playstore_strings.xml
source_lang = en
type = ANDROID
lang_map = ka_GE: ka-rGE, kn_IN: kn-rIN, sr_CS: sr-rCS, tr_TR: tr-rTR, bs_BA: bs-rBA, es_VE: es-rVE, gd_GB: gd-rGB, es_BO: es-rBO, es_HN: es-rHN, es_PE: es-rPE, eu_ES: eu-rES, rw_RW: rw-rRW, de_CH: de-rCH, de_DE: de-rDE, en_GB: en-rGB, sk_SK: sk-rSK, smj_NO: smj-rNO, zu_ZA: zu-rZA, es_ES: es-rES, mn_MN: mn-rMN, sa_IN: sa-rIN, ta_LK: ta-rLK, uk_UA: uk-rUA, fil_PH: fil-rPH, hr_BA: hr-rBA, ml_IN: ml-rIN, tg_TJ: tg-rTJ, uz_UZ: uz-rUZ, ar_LY: ar-rLY, ar_YE: ar-rYE, my_MM: my, ar_KW: ar-rKW, en_ZW: en-rZW, es_PA: es-rPA, it_CH: it-rCH, ku_IQ: ku-rIQ, ar_AE: ar-rAE, ar_BH: ar-rBH, ar_DZ: ar-rDZ, prs_AF: prs-rAF, pt_BR: pt-rBR, ug_CN: ug-rCN, oc_FR: oc-rFR, sma_NO: sma-rNO, es_PY: es-rPY, ms_MY: ms-rMY, mt_MT: mt-rMT, es_CR: es-rCR, fr_BE: fr-rBE, or_IN: or-rIN, quz_EC: quz-rEC, sr@latin: sr-rSP, zh_HK: zh-rHK, ar_JO: ar-rJO, ar_MA: ar-rMA, bn_BD: bn-rBD, en_ZA: en-rZA, it_IT: it-rIT, ko_KR: ko-rKR, mk_MK: mk-rMK, bo_CN: bo-rCN, co_FR: co-rFR, dsb_DE: dsb-rDE, nl_NL: nl-rNL, sah_RU: sah-rRU, se_SE: se-rSE, zh_CN.GB2312: zh-rBG, zh_TW: zh-rTW, az_AZ: az-rAZ, cy_GB: cy-rGB, nb_NO: nb-rNO, es_UY: es-rUY, fo_FO: fo-rFO, ig_NG: ig-rNG, lo_LA: lo-rLA, mi_NZ: mi-rNZ, en_MY: en-rMY, en_TT: en-rTT, es_SV: es-rSV, nso_ZA: nso-rZA, th_TH: th-rTH, ms_BN: ms-rBN, en_SG: en-rSG, es_EC: es-rEC, id_ID: id-rID, nl_BE: nl-rBE, fr_CH: fr-rCH, hi_IN: hi-rIN, is_IS: is-rIS, en_AU: en-rAU, et_EE: et-rEE, pt_PT: pt-rPT, hy_AM: hy-rAM, lv_LV: lv-rLV, tk_TM: tk-rTM, ur_PK: ur-rPK, en_NZ: en-rNZ, es_DO: es-rDO, es_GT: es-rGT, sv_FI: sv-rFI, tzm_DZ: tzm-rDZ, vi_VN: vi-rVN, ar_OM: ar-rOM, fa_IR: fa-rIR, hu_HU: hu-rHU, de_AT: de-rAT, en_IN: en-rIN, iu_CA: iu-rCA, qut_GT: qut-rGT, smj_SE: smj-rSE, ar_SY: ar-rSY, ar_TN: ar-rTN, cs_CZ: cs-rCZ, te_IN: te-rIN, zh_MO: zh-rMO, quz_BO: quz-rBO, ta_IN: ta-rIN, zh_CN: zh-rCN, de_LI: de-rLI, en_BZ: en-rBZ, ga_IE: ga-rIE, fy_NL: fy-rNL, ha_NG: ha-rNG, kk_KZ: kk-rKZ, rm_CH: rm-rCH, ro_RO: ro-rRO, ar_SA: ar-rSA, en_PH: en-rPH, es_419: es-rUS, hr_HR: hr-rHR, pa_IN: pa-rIN, pl_PL: pl-rPL, sma_SE: sma-rSE, xh_ZA: xh-rZA, dv_MV: dv-rMV, fi_FI: fi-rFI, fr_FR: fr-rFR, zh_SG: zh-rSG, ne_NP: ne-rNP, da_DK: da-rDK, gu_IN: gu-rIN, km_KH: km-rKH, mr_IN: mr-rIN, smn_FI: smn-rFI, el_GR: el-rGR, es_CL: es-rCL, ky_KG: ky-rKG, ps_AF: ps-rAF, ru_RU: ru-rRU, sms_FI: sms-rFI, sq_AL: sq-rAL, arn_CL: arn-rCL, es_AR: es-rAR, fr_LU: fr-rLU, sr_ME: sr-rME, sr_RS: sr-rRS, sw_KE: sw-rKE, yo_NG: yo-rNG, be_BY: be-rBY, es_MX: es-rMX, es_NI: es-rNI, ii_CN: ii-rCN, si_LK: si-rLK, sl_SI: sl-rSI, tn_ZA: tn-rZA, ar_EG: ar-rEG, ar_IQ: ar-rIQ, fr_MC: fr-rMC, moh_CA: moh-rCA, se_NO: se-rNO, en_CA: en-rCA, fr_CA: fr-rCA, kl_GL: kl-rGL, en_IE: en-rIE, en_JM: en-rJM, gl_ES: gl-rES, hsb_DE: hsb-rDE, lb_LU: lb-rLU, af_ZA: af-rZA, ba_RU: ba-rRU, bn_IN: bn-rIN, sv_SE: sv-rSE, wo_SN: wo-rSN, es_CO: es-rCO, br_FR: br-rFR, he_IL: he-rIL, ja_JP: ja-rJP, lt_LT: lt-rLT, tt_RU: tt-rRU, ar_LB: ar-rLB, ar_QA: ar-rQA, as_IN: as-rIN, de_LU: de-rLU, es_PR: es-rPR, gsw_FR: gsw-rFR, kok_IN: kok-rIN, mn_CN: mn-rCN, am_ET: am-rET, bg_BG: bg-rBG, ca_ES: ca-rES, quz_PE: quz-rPE, se_FI: se-rFI, sr_BA: sr-rBA, syr_SY: syr-rSY, en_US: en-rUS, en@pirate: en-rpirate, nn_NO: nn-rNO

20
PRIVACY_POLICY.md Normal file
View File

@ -0,0 +1,20 @@
# Overview
Nextcloud SMS is a free software developed by its contributors. This privacy policy
is intended to inform you about data gathered by this application."
# Information we collect
Only SMS and call log are collected by the application.
# Where information is sent.
Information is neither sent to Nextcloud team servers nor Nextcloud SMS team servers nor
any government nor another entity you don't want.
When you configure a Nextcloud account in the application, you agree with the Nextcloud
instance owner that your SMS and call log data will be stored in his infrastructure
under his responsibility.
We __don't__ recommend to use a public or a company Nextcloud instance account. Your privacy
must be under your control on your own Nextcloud instance.

View File

@ -1,65 +1,45 @@
# ownCloud SMS Android Application Offical Repository
# Nextcloud SMS (Android)
## Introduction
[![codebeat badge](https://codebeat.co/badges/df05cef7-6724-4a2f-b170-96ed1ab793f6)](https://codebeat.co/projects/github-com-nerzhul-owncloud-sms-app-master)
ownCloud SMS app push your Android devices conversation into your ownCloud instance, using ocsms app.
Nextcloud SMS app pushes your Android devices conversation into your Nextcloud instance, using [ocsms app](https://github.com/nerzhul/ocsms).
Android download link: https://play.google.com/store/apps/details?id=fr.unix_experience.owncloud_sms
## :arrow_forward: Access
ocsms app sources are available here: https://github.com/nerzhul/ocsms/
[![Nextcloud Notes App on fdroid.org](https://camo.githubusercontent.com/7df0eafa4433fa4919a56f87c3d99cf81b68d01c/68747470733a2f2f662d64726f69642e6f72672f77696b692f696d616765732f632f63342f462d44726f69642d627574746f6e5f617661696c61626c652d6f6e2e706e67)](https://f-droid.org/repository/browse/?fdid=fr.unix_experience.owncloud_sms)
## Application documentation
ocsms app sources are available here: https://github.com/nextcloud/ocsms
You can found application documentation here: https://github.com/nerzhul/ownCloud-SMS-App/wiki
## :notebook: Application documentation
## Licence
You can find the application documentation here: https://github.com/nerzhul/ncsms-android/wiki
ownCloud SMS Android Application licence is in reflexion, then sources are partial.
## :link: Requirements
- [Nextcloud](https://nextcloud.com/) instance running
- [ocsms](https://github.com/nextcloud/ocsms) app enabled
- App locales and layouts are under BSD 2 clause licence
- App DataProviders are under AGPLv3
## :exclamation: Reporting issues
## Contributions
- **Client:** https://github.com/nerzhul/ncsms-android/issues
- **Server:** https://github.com/nextcloud/ocsms/issues
We are searching for translations in others langs
## :rocket: Contributions
To contribute please download res/values/strings.xml and res/values/google_playstore_strings.xml and give us a translated version !
- We are searching for **translations** into others languages. To contribute please download `res/values/strings.xml` and `res/values/google_playstore_strings.xml` and provide a Pull Request with a translated version!
- You can also contribute by adding **patches** in Java code or cleanups.
- Application uses a [GoMobile AAR](https://gitlab.com/nerzhul/ncsmsgo) to have the best performance on phones with modern technologies like HTTP/2.0
You can also contribute by adding patches in Java code or cleanups.
## Requirements
- An ownCloud instance with ocsms app
## Build requirements
- nrz-androidlib (last version)
- Owncloud-Android-Library v1.0
## Issue template
Server
- ownCloud version: X.X.X
- PHP version: X.X
- HTTPd server: <apache|nginx...>
- HTTPS: <yes|no>
Client
- Android version: X.X.X
- Phone: <phone-model>
- ownCloud SMS app version: X.X.X
Please create your issues for the client here:
https://github.com/nerzhul/ownCloud-SMS-App/issues
And for the server app here:
https://github.com/nerzhul/ocsms/issues
## Developpers
You can found our continuous integration here: http://jenkins.unix-experience.fr/job/ownCloud%20SMS%20%28Android%29/
### Build requirements
- gradle
### Coding guidelines
- No empty lines at EOF
- No trailing whitespaces
## :notebook: License
Nextcloud SMS Android Application license is in reflexion, then sources are partial.
- App locales and layouts are under BSD 2 clause license
- App DataProviders are under AGPLv3

71
build.gradle Normal file
View File

@ -0,0 +1,71 @@
buildscript {
repositories {
mavenCentral()
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
}
}
allprojects {
repositories {
jcenter()
google()
}
}
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
buildToolsVersion "28.0.3"
lintOptions {
abortOnError false
}
defaultConfig {
applicationId "fr.unix_experience.owncloud_sms"
versionCode 70
versionName "2.0.6"
minSdkVersion 16
targetSdkVersion 28
maxSdkVersion 28
ndk {
// Specifies the ABI configurations of your native
// libraries Gradle should build and package with your APK.
abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
'arm64-v8a', 'mips', 'mips64'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
packagingOptions {
exclude 'META-INF/LICENSE.txt'
}
}
repositories {
maven {
url "https://jitpack.io"
}
mavenCentral()
google()
}
dependencies {
implementation 'com.android.support:support-v13:28.0.0'
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:design:28.0.0'
implementation 'in.srain.cube:ultra-ptr:1.0.11'
implementation 'com.github.dmytrodanylyk.android-process-button:library:1.0.4'
implementation 'com.android.support:support-v4:28.0.0'
implementation project(':ncsmsgo')
}

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,6 @@
#Sat Dec 22 18:14:31 CET 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip

164
gradlew vendored Executable file
View File

@ -0,0 +1,164 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

90
gradlew.bat vendored Normal file
View File

@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

56
ic_launcher.svg Normal file
View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
enable-background="new 0 0 595.275 311.111"
xml:space="preserve"
height="512"
width="512"
version="1.1"
y="0px"
x="0px"
id="svg2"
viewBox="0 0 512 512"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_launcher.svg"><metadata
id="metadata10"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs8" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1148"
id="namedview6"
showgrid="false"
inkscape:zoom="0.73698733"
inkscape:cx="8.4791339"
inkscape:cy="91.823077"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" /><rect
rx="70"
ry="70"
height="448"
width="448"
y="32"
x="32"
id="rect4"
style="fill:#0082C9" /><path
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.25007507;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0"
d="m 122.44396,89.125042 c -18.45887,0 -33.318923,14.115008 -33.318923,31.647818 l 0,143.74017 c 0,17.53283 14.860053,31.64784 33.318923,31.64784 l 38.24891,0 152.52844,126.43415 -49.39316,-126.43415 125.72788,0 c 18.45889,0 33.31893,-14.11501 33.31893,-31.64784 l 0,-143.74017 c 0,-17.53281 -14.86004,-31.647818 -33.31893,-31.647818 z m 9.05472,27.207518 249.00265,0 c 9.08866,0 16.40549,5.85418 16.40549,13.12558 l 0,8.53918 c 0,7.27142 -7.31683,13.12389 -16.40549,13.12389 l -249.00265,0 c -9.08867,0 -16.40551,-5.85247 -16.40551,-13.12389 l 0,-8.53918 c 0,-7.2714 7.31684,-13.12558 16.40551,-13.12558 z m 0,60.50813 249.00265,0 c 9.08866,0 16.40549,5.85418 16.40549,13.12559 l 0,8.53748 c 0,7.27141 -7.31683,13.12559 -16.40549,13.12559 l -249.00265,0 c -9.08867,0 -16.40551,-5.85418 -16.40551,-13.12559 l 0,-8.53748 c 0,-7.27141 7.31684,-13.12559 16.40551,-13.12559 z m 0,60.01043 89.95439,0 c 9.08866,0 16.40548,7.9877 16.40548,15.25911 l 0,6.40397 c 0,7.27139 -7.31682,13.12558 -16.40548,13.12558 l -89.95439,0 c -9.08867,0 -16.40551,-5.85419 -16.40551,-13.12558 l 0,-6.40397 c 0,-7.27141 7.31684,-15.25911 16.40551,-15.25911 z"
id="rect3350"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssscccssssssssssssssssssssssssssssssss" /></svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

45
issue_template.md Normal file
View File

@ -0,0 +1,45 @@
<!--
Thanks for reporting issues back to us!
To make it possible for us to help you please fill out below information carefully.
-->
### Steps to reproduce
1.
2.
3.
### Expected behaviour
Tell us what should happen
### Actual behaviour
Tell us what happens instead
### Server configuration
**Nextcloud version:**
**PHP version:**
**Webserver:**
**HTTPS:**
### Client configuration
**Android version:**
**Phone:**
**Nextcloud SMS app version:**
### Logs
```
Insert your log here
```
## Screenshots
<!--
Upload your screenshots here if any
-->

Binary file not shown.

2
ncsmsgo/build.gradle Normal file
View File

@ -0,0 +1,2 @@
configurations.maybeCreate("default")
artifacts.add("default", file('ncsmsgo.aar'))

BIN
ncsmsgo/ncsmsgo.aar Normal file

Binary file not shown.

25
ncsmsgo/ncsmsgo.iml Normal file
View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":ncsmsgo" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":ncsmsgo" />
</configuration>
</facet>
<facet type="java-gradle" name="Java-Gradle">
<configuration>
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
<option name="BUILDABLE" value="false" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

51
notification_icon.svg Normal file
View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
enable-background="new 0 0 595.275 311.111"
xml:space="preserve"
height="546.13336"
width="546.13336"
version="1.1"
y="0px"
x="0px"
id="svg2"
viewBox="0 0 512 512"
inkscape:version="0.92.1 r15371"
sodipodi:docname="notification_icon.svg"
inkscape:export-filename="C:\DEV\src\Android\Nextcloud\ownCloud-SMS-App\src\main\res\drawable-xxxhdpi\notification_icon.png"
inkscape:export-xdpi="16.879999"
inkscape:export-ydpi="16.879999"><metadata
id="metadata10"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs8" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1005"
id="namedview6"
showgrid="false"
inkscape:zoom="1.4739747"
inkscape:cx="140.31712"
inkscape:cy="253.09893"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" /><path
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.30745259;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0"
d="m 76.647972,90.431718 c -24.788364,0 -44.743887,15.887432 -44.743887,35.621852 v 161.78969 c 0,19.73444 19.955523,35.62188 44.743887,35.62188 H 128.01232 L 332.84234,465.77569 266.5124,323.46514 h 168.83961 c 24.7884,0 44.7439,-15.88744 44.7439,-35.62188 V 126.05357 c 0,-19.73442 -19.9555,-35.621852 -44.7439,-35.621852 z M 88.80753,121.0557 h 334.38495 c 12.20514,0 22.03089,6.58929 22.03089,14.77376 v 9.61145 c 0,8.1845 -9.82575,14.77187 -22.03089,14.77187 H 88.80753 c -12.205149,0 -22.030913,-6.58737 -22.030913,-14.77187 v -9.61145 c 0,-8.18447 9.825764,-14.77376 22.030913,-14.77376 z m 0,68.10616 h 334.38495 c 12.20514,0 22.03089,6.5893 22.03089,14.77378 v 9.60954 c 0,8.18448 -9.82575,14.77377 -22.03089,14.77377 H 88.80753 c -12.205149,0 -22.030913,-6.58929 -22.030913,-14.77377 v -9.60954 c 0,-8.18448 9.825764,-14.77378 22.030913,-14.77378 z m 0,67.54597 h 120.79949 c 12.20514,0 22.03088,8.99072 22.03088,17.17521 v 7.20812 c 0,8.18446 -9.82574,14.77376 -22.03088,14.77376 H 88.80753 c -12.205149,0 -22.030913,-6.5893 -22.030913,-14.77376 v -7.20812 c 0,-8.18449 9.825764,-17.17521 22.030913,-17.17521 z"
id="rect3350"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssscccssssssssssssssssssssssssssssssss" /></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
screenshots/login.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
screenshots/settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
screenshots/settings2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

1
settings.gradle Normal file
View File

@ -0,0 +1 @@
include ':ncsmsgo'

View File

@ -1,14 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="fr.unix_experience.owncloud_sms"
android:versionCode="30"
android:versionName="0.19.1"> <!-- From Android 4.0 to 5.2 -->
<uses-sdk
android:maxSdkVersion="22"
android:minSdkVersion="14"
android:targetSdkVersion="23" />
xmlns:tools="http://schemas.android.com/tools"
package="fr.unix_experience.owncloud_sms"> <!-- From Android 4.1 to O -->
<uses-sdk android:maxSdkVersion="26" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS" />
<!-- For SMS Restore & Sending -->
<uses-permission android:name="android.permission.SEND_SMS" />
<!-- For SMS Broadcaster -->
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.INTERNET" />
@ -29,21 +30,21 @@
<!-- For backup restauration -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/OcSmsTheme" >
android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/OcSmsTheme">
<!-- Related to periodic sync -->
<service
android:name=".observers.SmsObserverService"
android:exported="false" />
<service
android:name=".sync_adapters.SmsSyncService"
android:exported="true"
android:process=":sync" >
android:process=":sync">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
@ -56,33 +57,10 @@
<provider
android:name=".providers.SmsDataProvider"
android:authorities="@string/account_authority"
android:label="@string/pref_title_sync" >
</provider>
<!--
<service
android:name=".sync_adapters.SmsSlowSyncService"
android:exported="true"
android:process=":sync">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/slow_sync_adapter" />
</service>
<provider
android:name=".providers.SmsDataProvider"
android:label="@string/pref_title_slow_sync"
android:authorities="@string/slowsync_account_authority">
</provider>
-->
android:label="@string/pref_title_sync"></provider>
<!-- Related to Login -->
<service android:name=".authenticators.OwnCloudAuthenticatorService" >
<service android:name=".authenticators.OwnCloudAuthenticatorService">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
@ -92,12 +70,60 @@
android:resource="@xml/owncloud_account_authenticator" />
</service>
<receiver android:name=".broadcast_receivers.IncomingSms" >
<receiver
android:name=".broadcast_receivers.IncomingSms"
android:permission="android.permission.BROADCAST_SMS">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
<action android:name="android.provider.Telephony.SMS_DELIVER" />
</intent-filter>
</receiver>
<receiver android:name=".broadcast_receivers.ConnectivityChanged" >
<!-- BroadcastReceiver that listens for incoming MMS messages. Note: useless class, used only for restoring SMS -->
<receiver
android:name=".misc.MmsReceiver"
android:permission="android.permission.BROADCAST_WAP_PUSH">
<intent-filter>
<action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
<data android:mimeType="application/vnd.wap.mms-message" />
</intent-filter>
</receiver>
<!-- Activity that allows the user to send new SMS/MMS messages Note: useless class, used only for restoring SMS -->
<activity android:name=".misc.ComposeSmsActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.SENDTO" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="sms" />
<data android:scheme="smsto" />
<data android:scheme="mms" />
<data android:scheme="mmsto" />
</intent-filter>
</activity>
<!-- Service that delivers messages from the phone "quick response" Note: useless class, used only for restoring SMS -->
<service
android:name=".misc.HeadlessSmsSendService"
android:exported="true"
android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE">
<intent-filter>
<action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="sms" />
<data android:scheme="smsto" />
<data android:scheme="mms" />
<data android:scheme="mmsto" />
</intent-filter>
</service>
<receiver android:name=".broadcast_receivers.ConnectivityChanged">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
@ -105,35 +131,37 @@
<activity
android:name=".activities.LoginActivity"
android:label="@string/title_activity_login" >
</activity>
android:label="@string/title_activity_login"
android:theme="@style/OcSmsTheme.Login"></activity>
<activity
android:name=".activities.remote_account.AccountListActivity"
android:label="@string/title_activity_select_account" >
</activity>
android:label="@string/title_activity_select_account"></activity>
<activity
android:name=".activities.GeneralSettingsActivity"
android:label="@string/title_activity_general_settings" >
</activity>
android:name=".activities.OCSMSSettingsActivity"
android:label="@string/title_activity_general_settings"></activity>
<activity
android:name=".activities.MainActivity"
android:label="@string/app_name" >
android:label="@string/app_name"
android:theme="@style/OcSmsTheme.Drawer">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!--
<activity
android:name=".MainActivity2"
android:label="@string/title_activity_main_activity2" >
</activity>
-->
<activity
android:name=".activities.remote_account.ContactListActivity"
android:label="@string/title_activity_select_contact" >
</activity>
android:label="@string/title_activity_select_contact"></activity>
<activity
android:name=".activities.remote_account.AccountActionsActivity"
android:label="@string/account_actions"></activity>
<activity
android:name=".activities.remote_account.RestoreMessagesActivity"
android:label="@string/restore_all_messages"></activity>
<activity
android:name=".activities.PrivacyPolicyActivity"
android:label="@string/action_appinfo_privacy_policy"
android:theme="@style/OcSmsTheme.NoActionBar"></activity>
</application>
</manifest>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@ -0,0 +1,292 @@
package fr.unix_experience.android_lib;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
import fr.unix_experience.owncloud_sms.R;
/**
* An activity that displays a list of items by binding to a data source such as
* an array or Cursor, and exposes event handlers when the user selects an item.
* <p>
* ListActivity hosts a {@link android.widget.ListView ListView} object that can
* be bound to different data sources, typically either an array or a Cursor
* holding query results. Binding, screen layout, and row layout are discussed
* in the following sections.
* <p>
* <strong>Screen Layout</strong>
* </p>
* <p>
* ListActivity has a default layout that consists of a single, full-screen list
* in the center of the screen. However, if you desire, you can customize the
* screen layout by setting your own view layout with setContentView() in
* onCreate(). To do this, your own view MUST contain a ListView object with the
* id "@android:id/list" (or {@link android.R.id#list} if it's in code)
* <p>
* Optionally, your custom view can contain another view object of any type to
* display when the list view is empty. This "empty list" notifier must have an
* id "android:id/empty". Note that when an empty view is present, the list view
* will be hidden when there is no data to display.
* <p>
* The following code demonstrates an (ugly) custom screen layout. It has a list
* with a green background, and an alternate red "no data" message.
* </p>
*
* <pre>
* &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
* &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
* android:orientation=&quot;vertical&quot;
* android:layout_width=&quot;match_parent&quot;
* android:layout_height=&quot;match_parent&quot;
* android:paddingLeft=&quot;8dp&quot;
* android:paddingRight=&quot;8dp&quot;&gt;
*
* &lt;ListView android:id=&quot;@android:id/list&quot;
* android:layout_width=&quot;match_parent&quot;
* android:layout_height=&quot;match_parent&quot;
* android:background=&quot;#00FF00&quot;
* android:layout_weight=&quot;1&quot;
* android:drawSelectorOnTop=&quot;false&quot;/&gt;
*
* &lt;TextView android:id=&quot;@android:id/empty&quot;
* android:layout_width=&quot;match_parent&quot;
* android:layout_height=&quot;match_parent&quot;
* android:background=&quot;#FF0000&quot;
* android:text=&quot;No data&quot;/&gt;
* &lt;/LinearLayout&gt;
* </pre>
*
* <p>
* <strong>Row Layout</strong>
* </p>
* <p>
* You can specify the layout of individual rows in the list. You do this by
* specifying a layout resource in the ListAdapter object hosted by the activity
* (the ListAdapter binds the ListView to the data; more on this later).
* <p>
* A ListAdapter constructor takes a parameter that specifies a layout resource
* for each row. It also has two additional parameters that let you specify
* which data field to associate with which object in the row layout resource.
* These two parameters are typically parallel arrays.
* </p>
* <p>
* Android provides some standard row layout resources. These are in the
* {@link android.R.layout} class, and have names such as simple_list_item_1,
* simple_list_item_2, and two_line_list_item. The following layout XML is the
* source for the resource two_line_list_item, which displays two data
* fields,one above the other, for each list row.
* </p>
*
* <pre>
* &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
* &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
* android:layout_width=&quot;match_parent&quot;
* android:layout_height=&quot;wrap_content&quot;
* android:orientation=&quot;vertical&quot;&gt;
*
* &lt;TextView android:id=&quot;@+id/text1&quot;
* android:textSize=&quot;16sp&quot;
* android:textStyle=&quot;bold&quot;
* android:layout_width=&quot;match_parent&quot;
* android:layout_height=&quot;wrap_content&quot;/&gt;
*
* &lt;TextView android:id=&quot;@+id/text2&quot;
* android:textSize=&quot;16sp&quot;
* android:layout_width=&quot;match_parent&quot;
* android:layout_height=&quot;wrap_content&quot;/&gt;
* &lt;/LinearLayout&gt;
* </pre>
*
* <p>
* You must identify the data bound to each TextView object in this layout. The
* syntax for this is discussed in the next section.
* </p>
* <p>
* <strong>Binding to Data</strong>
* </p>
* <p>
* You bind the ListActivity's ListView object to data using a class that
* implements the {@link android.widget.ListAdapter ListAdapter} interface.
* Android provides two standard list adapters:
* {@link android.widget.SimpleAdapter SimpleAdapter} for static data (Maps),
* and {@link android.widget.SimpleCursorAdapter SimpleCursorAdapter} for Cursor
* query results.
* </p>
* <p>
* The following code from a custom ListActivity demonstrates querying the
* Contacts provider for all contacts, then binding the Name and Company fields
* to a two line row layout in the activity's ListView.
* </p>
*
* <pre>
* public class MyListAdapter extends ListActivity {
*
* &#064;Override
* protected void onCreate(Bundle savedInstanceState){
* super.onCreate(savedInstanceState);
*
* // We'll define a custom screen layout here (the one shown above), but
* // typically, you could just use the standard ListActivity layout.
* setContentView(R.layout.custom_list_activity_view);
*
* // Query for all people contacts using the {@link android.provider.Contacts.People} convenience class.
* // Put a managed wrapper around the retrieved cursor so we don't have to worry about
* // requerying or closing it as the activity changes state.
* mCursor = this.getContentResolver().query(People.CONTENT_URI, null, null, null, null);
* startManagingCursor(mCursor);
*
* // Now create a new list adapter bound to the cursor.
* // SimpleListAdapter is designed for binding to a Cursor.
* ListAdapter adapter = new SimpleCursorAdapter(
* this, // Context.
* android.R.layout.two_line_list_item, // Specify the row template to use (here, two columns bound to the two retrieved cursor
* rows).
* mCursor, // Pass in the cursor to bind to.
* new String[] {People.NAME, People.COMPANY}, // Array of cursor columns to bind to.
* new int[] {android.R.id.text1, android.R.id.text2}); // Parallel array of which template objects to bind to those columns.
*
* // Bind to our new adapter.
* setListAdapter(adapter);
* }
* }
* </pre>
*
* @see #setListAdapter
* @see android.widget.ListView
*/
public class AppCompatListActivity extends AppCompatActivity {
protected ListAdapter mAdapter;
protected ListView mList;
private Handler mHandler = new Handler();
private boolean mFinishedStart = false;
private Runnable mRequestFocus = new Runnable() {
public void run() {
mList.focusableViewAvailable(mList);
}
};
protected void onListItemClick(ListView l, View v, int position, long id) {
}
/**
* Ensures the list view has been created before Activity restores all
* of the view states.
*
*@see Activity#onRestoreInstanceState(Bundle)
*/
@Override
protected void onRestoreInstanceState(Bundle state) {
ensureList();
super.onRestoreInstanceState(state);
}
/**
* @see Activity#onDestroy()
*/
@Override
protected void onDestroy() {
mHandler.removeCallbacks(mRequestFocus);
super.onDestroy();
}
/**
* Updates the screen state (current list and other views) when the
* content changes.
*
* @see Activity#onContentChanged()
*/
@Override
public void onContentChanged() {
super.onContentChanged();
View emptyView = findViewById(R.id.empty);
mList = findViewById(R.id.list);
if (mList == null) {
throw new RuntimeException(
"Your content must have a ListView whose id attribute is " +
"'android.R.id.list'");
}
if (emptyView != null) {
mList.setEmptyView(emptyView);
}
mList.setOnItemClickListener(mOnClickListener);
if (mFinishedStart) {
setListAdapter(mAdapter);
}
mHandler.post(mRequestFocus);
mFinishedStart = true;
}
/**
* Provide the cursor for the list view.
*/
public void setListAdapter(ListAdapter adapter) {
synchronized (this) {
ensureList();
mAdapter = adapter;
mList.setAdapter(adapter);
}
}
/**
* Set the currently selected list item to the specified
* position with the adapter's data
*
* @param position
*/
public void setSelection(int position) {
mList.setSelection(position);
}
/**
* Get the position of the currently selected list item.
*/
public int getSelectedItemPosition() {
return mList.getSelectedItemPosition();
}
/**
* Get the cursor row ID of the currently selected list item.
*/
public long getSelectedItemId() {
return mList.getSelectedItemId();
}
/**
* Get the activity's list view widget.
*/
public ListView getListView() {
ensureList();
return mList;
}
/**
* Get the ListAdapter associated with this activity's ListView.
*/
public ListAdapter getListAdapter() {
return mAdapter;
}
private void ensureList() {
if (mList != null) {
return;
}
setContentView(R.layout.default_list);
}
private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id)
{
onListItemClick((ListView)parent, v, position, id);
}
};
}

View File

@ -1,128 +0,0 @@
package fr.unix_experience.owncloud_sms.activities;
/*
* Copyright (c) 2014-2015, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.ContentResolver;
import android.content.PeriodicSync;
import android.os.Bundle;
import android.preference.ListPreference;
import android.util.Log;
import java.util.List;
import fr.nrz.androidlib.activities.NrzSettingsActivity;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.defines.DefaultPrefs;
import fr.unix_experience.owncloud_sms.prefs.OCSMSSharedPrefs;
public class GeneralSettingsActivity extends NrzSettingsActivity {
private static final String TAG = GeneralSettingsActivity.class.getSimpleName();
private static AccountManager _accountMgr;
private static String _accountAuthority;
private static String _accountType;
@Override
protected void onPostCreate(Bundle savedInstanceState) {
GeneralSettingsActivity._accountMgr = AccountManager.get(getBaseContext());
GeneralSettingsActivity._accountAuthority = getString(R.string.account_authority);
GeneralSettingsActivity._accountType = getString(R.string.account_type);
NrzSettingsActivity._prefsRessourceFile = R.xml.pref_data_sync;
// Bind our boolean preferences
NrzSettingsActivity._boolPrefs.add(new BindObjectPref("push_on_receive", DefaultPrefs.pushOnReceive));
NrzSettingsActivity._boolPrefs.add(new BindObjectPref("sync_wifi", DefaultPrefs.syncWifi));
NrzSettingsActivity._boolPrefs.add(new BindObjectPref("sync_4g", DefaultPrefs.sync4G));
NrzSettingsActivity._boolPrefs.add(new BindObjectPref("sync_3g", DefaultPrefs.sync3G));
NrzSettingsActivity._boolPrefs.add(new BindObjectPref("sync_gprs", DefaultPrefs.syncGPRS));
NrzSettingsActivity._boolPrefs.add(new BindObjectPref("sync_2g", DefaultPrefs.sync2G));
NrzSettingsActivity._boolPrefs.add(new BindObjectPref("sync_others", DefaultPrefs.syncOthers));
// Bind our string preferences
NrzSettingsActivity._stringPrefs.add(new BindObjectPref("sync_frequency", "15"));
NrzSettingsActivity._stringPrefs.add(new BindObjectPref("sync_bulk_messages", "-1"));
// Must be at the end, after preference bind
super.onPostCreate(savedInstanceState);
}
protected void handleCheckboxPreference(String key, Boolean value) {
// Network types allowed for sync
if("push_on_receive".equals(key) ||
"sync_wifi".equals(key) || "sync_2g".equals(key) ||
"sync_3g".equals(key) || "sync_gprs".equals(key) ||
"sync_4g".equals(key) || "sync_others".equals(key)) {
OCSMSSharedPrefs prefs = new OCSMSSharedPrefs(NrzSettingsActivity._context);
Log.d(GeneralSettingsActivity.TAG,"GeneralSettingsActivity.handleCheckboxPreference: set " + key + " to "
+ value.toString());
prefs.putBoolean(key, value);
}
}
protected void handleListPreference(String key, String value,
ListPreference preference) {
// For list preferences, look up the correct display value in
// the preference's 'entries' list.
int index = preference.findIndexOfValue(value);
// Set the summary to reflect the new value.
preference
.setSummary((index >= 0) ? preference.getEntries()[index]
: null);
Log.d(TAG, "Modifying listPreference " + key);
OCSMSSharedPrefs prefs = new OCSMSSharedPrefs(NrzSettingsActivity._context);
// Handle sync frequency change
if ("sync_frequency".equals(key)) {
Account[] myAccountList = GeneralSettingsActivity._accountMgr.getAccountsByType(GeneralSettingsActivity._accountType);
long syncFreq = Long.parseLong(value);
// Get ownCloud SMS account list
for (Account acct: myAccountList) {
// And get all authorities for this account
List<PeriodicSync> syncList = ContentResolver.getPeriodicSyncs(acct, GeneralSettingsActivity._accountAuthority);
boolean foundSameSyncCycle = false;
for (PeriodicSync ps: syncList) {
if ((ps.period == syncFreq) && (ps.extras.getInt("synctype") == 1)) {
foundSameSyncCycle = true;
}
}
if (!foundSameSyncCycle) {
Bundle b = new Bundle();
b.putInt("synctype", 1);
ContentResolver.removePeriodicSync(acct, GeneralSettingsActivity._accountAuthority, b);
if (syncFreq > 0) {
ContentResolver.addPeriodicSync(acct, GeneralSettingsActivity._accountAuthority, b, syncFreq * 60);
}
}
prefs.putLong(key, syncFreq);
}
}
else if ("sync_bulk_messages".equals(key)) {
prefs.putInteger(key, Integer.parseInt(value));
}
}
}

View File

@ -22,38 +22,44 @@ import android.accounts.AccountManager;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.OwnCloudClientFactory;
import com.owncloud.android.lib.common.OwnCloudCredentialsFactory;
import com.dd.processbutton.iml.ActionProcessButton;
import java.net.MalformedURLException;
import java.net.URL;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.authenticators.OwnCloudAuthenticator;
import fr.unix_experience.owncloud_sms.defines.DefaultPrefs;
import fr.unix_experience.owncloud_sms.enums.LoginReturnCode;
import fr.unix_experience.owncloud_sms.engine.OCHttpClient;
import fr.unix_experience.owncloud_sms.exceptions.OCSyncException;
/**
* A login screen that offers login via email/password.
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class LoginActivity extends Activity {
public class LoginActivity extends AppCompatActivity {
private static final String TAG = LoginActivity.class.getCanonicalName();
/**
* Keep track of the login task to ensure we can cancel it if requested.
*/
@ -64,6 +70,7 @@ public class LoginActivity extends Activity {
private EditText _loginView;
private EditText _passwordView;
private EditText _serverView;
private ActionProcessButton _signInButton;
private View mProgressView;
private View mLoginFormView;
@ -72,17 +79,20 @@ public class LoginActivity extends Activity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
// Set up the login form.
_protocolView = (Spinner) findViewById(R.id.oc_protocol);
_serverView = (EditText) findViewById(R.id.oc_server);
_loginView = (EditText) findViewById(R.id.oc_login);
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
_passwordView = (EditText) findViewById(R.id.oc_password);
// Set up the login form.
_protocolView = findViewById(R.id.oc_protocol);
_serverView = findViewById(R.id.oc_server);
_loginView = findViewById(R.id.oc_login);
_passwordView = findViewById(R.id.oc_password);
_passwordView
.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView textView, int id,
KeyEvent keyEvent) {
KeyEvent keyEvent) {
if ((id == R.id.oc_login) || (id == EditorInfo.IME_NULL)) {
attemptLogin();
return true;
@ -91,7 +101,7 @@ public class LoginActivity extends Activity {
}
});
Button _signInButton = (Button) findViewById(R.id.oc_signin_button);
_signInButton = findViewById(R.id.oc_signin_button);
_signInButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
@ -103,6 +113,19 @@ public class LoginActivity extends Activity {
mProgressView = findViewById(R.id.login_progress);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean retval = true;
switch (item.getItemId()) {
case android.R.id.home:
finish();
break;
default:
retval = super.onOptionsItemSelected(item);
}
return retval;
}
/**
* Attempts to sign in or register the account specified by the login form.
* If there are form errors (invalid email, missing fields, etc.), the
@ -125,12 +148,12 @@ public class LoginActivity extends Activity {
boolean cancel = false;
View focusView = null;
// Check for a valid server address.
if (TextUtils.isEmpty(protocol)) {
cancel = true;
}
// Check for a valid server address.
if (TextUtils.isEmpty(serverAddr)) {
_serverView.setError(getString(R.string.error_field_required));
@ -144,14 +167,14 @@ public class LoginActivity extends Activity {
focusView = _loginView;
cancel = true;
}
// Check for a valid password
if (TextUtils.isEmpty(password)) {
_passwordView.setError(getString(R.string.error_field_required));
focusView = _passwordView;
cancel = true;
}
if (!isPasswordValid(password)) {
_passwordView.setError(getString(R.string.error_invalid_password));
focusView = _passwordView;
@ -161,16 +184,23 @@ public class LoginActivity extends Activity {
if (cancel) {
// There was an error; don't attempt login and focus the first
// form field with an error.
if (focusView != null) {
focusView.requestFocus();
}
} else {
// reset the button progress
_signInButton.setProgress(0);
if (focusView != null) {
focusView.requestFocus();
}
} else {
// Show a progress spinner, and kick off a background task to
// perform the user login attempt.
_signInButton.setProgress(25);
showProgress(true);
String serverURL = protocol + serverAddr;
mAuthTask = new UserLoginTask(serverURL, login, password);
mAuthTask.execute((Void) null);
try {
mAuthTask = new UserLoginTask(serverURL, login, password);
mAuthTask.execute((Void) null);
} catch (MalformedURLException e) {
Log.e(TAG, "Invalid server URL " + serverURL);
}
}
}
@ -224,103 +254,118 @@ public class LoginActivity extends Activity {
* Represents an asynchronous login/registration task used to authenticate
* the user.
*/
public class UserLoginTask extends AsyncTask<Void, Void, Boolean> {
public class UserLoginTask extends AsyncTask<Void, Void, Boolean> {
UserLoginTask(String serverURI, String login, String password) {
_serverURI = Uri.parse(serverURI);
UserLoginTask(String serverURL, String login, String password) throws MalformedURLException {
_serverURL = new URL(serverURL);
Log.i(TAG, "_serverURL = " + serverURL);
_login = login;
_password = password;
_last_http_error = null;
}
@Override
protected Boolean doInBackground(Void... params) {
// Create client object to perform remote operations
OwnCloudClient ocClient = OwnCloudClientFactory.createOwnCloudClient(
_serverURI, getBaseContext(),
// Activity or Service context
true
);
// Set basic credentials
ocClient.setCredentials(
OwnCloudCredentialsFactory.newBasicCredentials(_login, _password)
);
// Send an authentication test to ownCloud
OwnCloudAuthenticator at = new OwnCloudAuthenticator(getBaseContext());
at.setClient(ocClient);
_returnCode = at.testCredentials();
return (_returnCode == LoginReturnCode.OK);
_returnCode = 0;
OCHttpClient http = new OCHttpClient(getBaseContext(), _serverURL, _login, _password);
try {
Pair<Integer, Integer> vPair = http.getVersion();
_returnCode = vPair.first;
} catch (IllegalArgumentException e) {
Log.w(TAG, "Failed to getVersion, IllegalArgumentException occured: " + e.getMessage());
_returnCode = 597;
} catch (OCSyncException e) {
Log.w(TAG, "Failed to login, OCSyncException occured: " + e.getMessage());
_returnCode = 599;
}
_last_http_error = http.getLastError();
return (_returnCode == 200);
}
@Override
protected void onPostExecute(Boolean success) {
mAuthTask = null;
showProgress(false);
_signInButton.setProgress(90);
if (success) {
_signInButton.setProgress(100);
String accountType = getIntent().getStringExtra(UserLoginTask.PARAM_AUTHTOKEN_TYPE);
if (accountType == null) {
accountType = getString(R.string.account_type);
}
if (accountType == null) {
accountType = getString(R.string.account_type);
}
// Generate a label
String accountLabel = _login + "@" + _serverURI.getHost();
String accountLabel = _login + "@" + _serverURL.getHost();
// We create the account
Account account = new Account(accountLabel, accountType);
Bundle accountBundle = new Bundle();
accountBundle.putString("ocLogin", _login);
accountBundle.putString("ocURI", _serverURI.toString());
accountBundle.putString("ocURI", _serverURL.toString());
// And we push it to Android
AccountManager accMgr = AccountManager.get(getApplicationContext());
accMgr.addAccountExplicitly(account, _password, accountBundle);
accMgr.addAccountExplicitly(account, _password, accountBundle);
// Set sync options
ContentResolver.setSyncAutomatically(account, getString(R.string.account_authority), true);
Bundle b = new Bundle();
b.putInt("synctype", 1);
ContentResolver.addPeriodicSync(account, getString(R.string.account_authority), b, DefaultPrefs.syncInterval * 60);
// Then it's finished
finish();
// Start sync settings, we have finished to configure account
Intent settingsIntent = new Intent(Settings.ACTION_SYNC_SETTINGS);
settingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplicationContext().startActivity(settingsIntent);
} else {
boolean serverViewRequestFocus = true;
switch (_returnCode) {
case OK:
break;
case INVALID_ADDR:
case 0:
if (!_last_http_error.isEmpty()) {
_serverView.setError("Low level error: " + _last_http_error);
}
else {
_serverView.setError("Unknown error");
}
break;
case 404:
_serverView.setError(getString(R.string.error_connection_failed_not_found));
break;
case 597:
_serverView.setError(getString(R.string.error_invalid_server_address));
_serverView.requestFocus();
break;
case HTTP_CONN_FAILED:
_serverView.setError(getString(R.string.error_http_connection_failed));
_serverView.requestFocus();
break;
case CONN_FAILED:
case 400:
case 598:
_serverView.setError(getString(R.string.error_connection_failed));
_serverView.requestFocus();
break;
case INVALID_LOGIN:
case 599:
_serverView.setError(getString(R.string.error_http_connection_failed));
break;
case 401:
case 403:
_passwordView.setError(getString(R.string.error_invalid_login));
_passwordView.requestFocus();
// Warning, there is no break here to disable serverViewRequestFocus too
case 200:
default:
serverViewRequestFocus = false;
break;
case UNKNOWN_ERROR:
_serverView.setError("UNK");
_serverView.requestFocus();
break;
default:
break;
}
if (serverViewRequestFocus) {
_serverView.requestFocus();
}
// If not ok, reset the progress
if (_returnCode != 200) {
_signInButton.setProgress(0);
}
}
}
@ -329,12 +374,14 @@ public class LoginActivity extends Activity {
mAuthTask = null;
showProgress(false);
}
private final Uri _serverURI;
private final URL _serverURL;
private final String _login;
private final String _password;
private LoginReturnCode _returnCode;
public static final String PARAM_AUTHTOKEN_TYPE = "auth.token";
}
private String _last_http_error;
private int _returnCode;
static final String PARAM_AUTHTOKEN_TYPE = "auth.token";
private final String TAG = UserLoginTask.class.getCanonicalName();
}
}

View File

@ -1,7 +1,7 @@
package fr.unix_experience.owncloud_sms.activities;
/*
* Copyright (c) 2014-2015, Loic Blot <loic.blot@unix-experience.fr>
* Copyright (c) 2014-2016, Loic Blot <loic.blot@unix-experience.fr>
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -25,174 +25,255 @@ package fr.unix_experience.owncloud_sms.activities;
* SUCH DAMAGE.
*/
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v13.app.FragmentPagerAdapter;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.support.annotation.NonNull;
import android.support.design.widget.NavigationView;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.MenuItem;
import android.view.Window;
import android.widget.Toast;
import org.json.JSONArray;
import java.util.List;
import java.util.Vector;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.activities.remote_account.AccountListActivity;
import fr.unix_experience.owncloud_sms.engine.ASyncSMSSync.SyncTask;
import fr.unix_experience.owncloud_sms.engine.ConnectivityMonitor;
import fr.unix_experience.owncloud_sms.engine.SmsFetcher;
import fr.unix_experience.owncloud_sms.notifications.OCSMSNotificationManager;
import fr.unix_experience.owncloud_sms.enums.PermissionID;
import fr.unix_experience.owncloud_sms.prefs.PermissionChecker;
public class MainActivity extends Activity {
import static fr.unix_experience.owncloud_sms.enums.PermissionID.REQUEST_MAX;
import static fr.unix_experience.owncloud_sms.enums.PermissionID.REQUEST_SMS;
/**
* The {@link android.support.v4.view.PagerAdapter} that will provide
* fragments for each of the sections. We use a {@link FragmentPagerAdapter}
* derivative, which will keep every loaded fragment in memory. If this
* becomes too memory intensive, it may be best to switch to a
* {@link android.support.v4.app.FragmentStatePagerAdapter}.
*/
PagerAdapter mPagerAdapter;
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
/**
* The {@link ViewPager} that will host the section contents.
*/
ViewPager mViewPager;
private ConnectivityMonitor _ConnectivityMonitor = null;
private DrawerLayout drawer;
@Override
protected void onCreate(Bundle savedInstanceState) {
if (_ConnectivityMonitor == null) {
_ConnectivityMonitor = new ConnectivityMonitor(getApplicationContext());
}
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
setupToolbar();
if (getSupportActionBar() != null) {
getSupportActionBar().setHomeButtonEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
List<Fragment> fragments = new Vector<>();
drawer = findViewById(R.id.drawer_layout);
setupDrawer();
drawer.openDrawer(GravityCompat.START);
}
/*
* Add the Main tabs here
*/
protected void setupToolbar() {
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
}
fragments.add(Fragment.instantiate(this,StarterFragment.class.getName()));
fragments.add(Fragment.instantiate(this,SecondTestFragment.class.getName()));
fragments.add(Fragment.instantiate(this,ThanksAndRateFragment.class.getName()));
private void setupDrawer() {
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, null, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
assert drawer != null;
drawer.addDrawerListener(toggle);
toggle.syncState();
toggle.setDrawerIndicatorEnabled(true);
mPagerAdapter = new MainPagerAdapter(getFragmentManager(), fragments);
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mPagerAdapter);
NavigationView navigationView = findViewById(R.id.nav_view);
assert navigationView != null;
navigationView.setNavigationItemSelectedListener(this);
}
/**
* A {@link FragmentPagerAdapter} that returns a fragment corresponding to
* one of the sections/tabs/pages.
* checks if the drawer exists and is opened.
*
* @return <code>true</code> if the drawer is open, else <code>false</code>
*/
public class MainPagerAdapter extends FragmentPagerAdapter {
public boolean isDrawerOpen() {
return drawer != null && drawer.isDrawerOpen(GravityCompat.START);
}
private final List<Fragment> mFragments;
public MainPagerAdapter(FragmentManager fragmentManager, List<Fragment> fragments) {
super(fragmentManager);
mFragments = fragments;
}
@Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class
// below).
return mFragments.get(position);
}
@Override
public int getCount() {
// Show 3 total pages.
return mFragments.size();
/**
* closes the drawer.
*/
public void closeDrawer() {
if (drawer != null) {
drawer.closeDrawer(GravityCompat.START);
}
}
/**
* Fragments for activity must be there
* opens the drawer.
*/
public static class StarterFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_mainactivity_main, container,
false);
public void openDrawer() {
if (drawer != null) {
drawer.openDrawer(GravityCompat.START);
}
}
public static class SecondTestFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_mainactivity_gotosettings, container,
false);
@Override
public void onBackPressed() {
if (isDrawerOpen()) {
closeDrawer();
} else {
super.onBackPressed();
}
}
public static class ThanksAndRateFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_mainactivity_thanks_note, container,
false);
}
}
public void openAppSettings(View view) {
startActivity(new Intent(this, GeneralSettingsActivity.class));
}
public void openAddAccount(View view) {
startActivity(new Intent(Settings.ACTION_ADD_ACCOUNT));
}
public void syncAllMessages(View view) {
Context ctx = getApplicationContext();
ConnectivityMonitor cMon = new ConnectivityMonitor(ctx);
if (cMon.isValid()) {
// Now fetch messages since last stored date
JSONArray smsList = new JSONArray();
new SmsFetcher(ctx).bufferMessagesSinceDate(smsList, (long) 0);
if (smsList.length() > 0) {
OCSMSNotificationManager nMgr = new OCSMSNotificationManager(ctx);
nMgr.setSyncProcessMsg();
new SyncTask(getApplicationContext(), smsList).execute();
@Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean retval = true;
switch (item.getItemId()) {
case android.R.id.home: {
if (isDrawerOpen()) {
closeDrawer();
} else {
openDrawer();
}
break;
}
default:
retval = super.onOptionsItemSelected(item);
}
else {
return retval;
}
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
int id = item.getItemId();
boolean res = true;
switch (id) {
case R.id.nav_sync:
syncAllMessages();
break;
case R.id.nav_manage:
res = openAppSettings();
break;
case R.id.nav_rateus:
res = openGooglePlayStore();
break;
case R.id.nav_add_account:
res = openAddAccount();
break;
case R.id.nav_my_accounts:
res = openMyAccounts();
break;
case R.id.nav_appinfo_perms:
res = openAppInfos();
break;
case R.id.nav_appinfo_privacy_policy:
res = openPrivacyPolicy();
break;
default:
Log.e(TAG, "Unhandled navigation item " + Integer.toString(id));
}
closeDrawer();
return res;
}
private boolean openAppSettings() {
startActivity(new Intent(this, OCSMSSettingsActivity.class));
return true;
}
private boolean openAddAccount() {
startActivity(new Intent(Settings.ACTION_ADD_ACCOUNT));
return true;
}
public void syncAllMessages() {
Log.v(MainActivity.TAG, "Launch syncAllMessages()");
if (!PermissionChecker.checkPermission(this, Manifest.permission.READ_SMS,
REQUEST_SMS)) {
return;
}
Context ctx = getApplicationContext();
if (!_ConnectivityMonitor.isValid()) {
Toast.makeText(ctx, ctx.getString(R.string.err_sync_no_connection_available), Toast.LENGTH_SHORT).show();
Log.v(MainActivity.TAG, "Finish syncAllMessages(): invalid connection");
return;
}
new SyncTask(this).execute();
Log.v(MainActivity.TAG, "Finish syncAllMessages()");
}
public void selectRemoteAccount(View view) {
private boolean openMyAccounts() {
startActivity(new Intent(this, AccountListActivity.class));
return true;
}
public void openGooglePlayStore(View view) {
private boolean openGooglePlayStore() {
Intent intent;
try {
intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + getPackageName()));
} catch (android.content.ActivityNotFoundException anfe) {
} catch (android.content.ActivityNotFoundException e) {
intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + getPackageName()));
}
startActivity(intent);
return true;
}
private boolean openAppInfos() {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivity(intent);
return true;
}
private boolean openPrivacyPolicy() {
startActivity(new Intent(this, PrivacyPolicyActivity.class));
return true;
}
/*
* Permissions
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
PermissionID requestCodeID = REQUEST_MAX;
if ((requestCode > 0) || (requestCode < REQUEST_MAX.ordinal())) {
requestCodeID = PermissionID.values()[requestCode];
}
switch (requestCodeID) {
case REQUEST_SMS:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
syncAllMessages();
} else {
// Permission Denied
Toast.makeText(this, getString(R.string.err_cannot_read_sms) + " " +
getString(R.string.please_fix_it), Toast.LENGTH_SHORT)
.show();
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
private static final String TAG = MainActivity.class.getSimpleName();
}

View File

@ -0,0 +1,189 @@
package fr.unix_experience.owncloud_sms.activities;
/*
* Copyright (c) 2014-2017, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import android.Manifest;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.ContentResolver;
import android.content.PeriodicSync;
import android.os.Bundle;
import android.preference.ListPreference;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatDelegate;
import android.util.Log;
import android.util.Pair;
import android.view.MenuItem;
import java.util.List;
import java.util.Vector;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.activities.virtual.VirtualSettingsActivity;
import fr.unix_experience.owncloud_sms.defines.DefaultPrefs;
import fr.unix_experience.owncloud_sms.prefs.OCSMSSharedPrefs;
import fr.unix_experience.owncloud_sms.prefs.PermissionChecker;
import static fr.unix_experience.owncloud_sms.enums.PermissionID.REQUEST_ACCOUNTS;
public class OCSMSSettingsActivity extends VirtualSettingsActivity {
private static final String TAG = OCSMSSettingsActivity.class.getSimpleName();
private static AccountManager _accountMgr;
private static String _accountAuthority;
private static String _accountType;
private static Vector<Pair<Integer, Boolean>> _boolSettings;
private AppCompatDelegate mDelegate;
@SuppressWarnings("deprecation")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
}
private AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, null);
}
return mDelegate;
}
public ActionBar getSupportActionBar() {
return getDelegate().getSupportActionBar();
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
OCSMSSettingsActivity._accountMgr = AccountManager.get(getBaseContext());
OCSMSSettingsActivity._accountAuthority = getString(R.string.account_authority);
OCSMSSettingsActivity._accountType = getString(R.string.account_type);
VirtualSettingsActivity._prefsRessourceFile = R.xml.pref_data_sync;
// Bind our boolean preferences
VirtualSettingsActivity._boolPrefs.add(
new BindObjectPref(R.string.setting_push_on_receive, DefaultPrefs.pushOnReceive));
VirtualSettingsActivity._boolPrefs.add(
new BindObjectPref(R.string.setting_show_sync_notifications, DefaultPrefs.showSyncNotifications));
VirtualSettingsActivity._boolPrefs.add(
new BindObjectPref(R.string.setting_sync_wifi, DefaultPrefs.syncWifi));
VirtualSettingsActivity._boolPrefs.add(
new BindObjectPref(R.string.setting_sync_4g, DefaultPrefs.sync4G));
VirtualSettingsActivity._boolPrefs.add(
new BindObjectPref(R.string.setting_sync_3g, DefaultPrefs.sync3G));
VirtualSettingsActivity._boolPrefs.add(
new BindObjectPref(R.string.setting_sync_gprs, DefaultPrefs.syncGPRS));
VirtualSettingsActivity._boolPrefs.add(
new BindObjectPref(R.string.setting_sync_2g, DefaultPrefs.sync2G));
VirtualSettingsActivity._boolPrefs.add(
new BindObjectPref(R.string.setting_sync_others, DefaultPrefs.syncOthers));
// Bind our string preferences
VirtualSettingsActivity._stringPrefs.add(
new BindObjectPref(R.string.setting_sync_frequency, "15"));
VirtualSettingsActivity._stringPrefs.add(
new BindObjectPref(R.string.setting_sync_bulk_messages, "-1"));
VirtualSettingsActivity._stringPrefs.add(
new BindObjectPref(R.string.setting_minimum_sync_chars, "1"));
// Must be at the end, after preference bind
super.onPostCreate(savedInstanceState);
}
protected void handleCheckboxPreference(String key, Boolean value) {
// Network types allowed for sync
if ("push_on_receive".equals(key) || "show_sync_notifications".equals(key) ||
"sync_wifi".equals(key) || "sync_2g".equals(key) ||
"sync_3g".equals(key) || "sync_gprs".equals(key) ||
"sync_4g".equals(key) || "sync_others".equals(key)) {
OCSMSSharedPrefs prefs = new OCSMSSharedPrefs(VirtualSettingsActivity._context);
Log.i(OCSMSSettingsActivity.TAG, "OCSMSSettingsActivity.handleCheckboxPreference: set " + key + " to "
+ value.toString());
prefs.putBoolean(key, value);
}
}
protected void handleListPreference(String key, String value,
ListPreference preference) {
// For list preferences, look up the correct display value in
// the preference's 'entries' list.
int index = preference.findIndexOfValue(value);
// Set the summary to reflect the new value.
preference.setSummary((index >= 0) ? preference.getEntries()[index] : null);
Log.i(OCSMSSettingsActivity.TAG, "Modifying listPreference " + key);
OCSMSSharedPrefs prefs = new OCSMSSharedPrefs(VirtualSettingsActivity._context);
// Handle sync frequency change
if ("sync_frequency".equals(key)) {
if (!PermissionChecker.checkPermission(this, Manifest.permission.GET_ACCOUNTS,
REQUEST_ACCOUNTS)) {
return;
}
Account[] myAccountList = OCSMSSettingsActivity._accountMgr.getAccountsByType(OCSMSSettingsActivity._accountType);
long syncFreq = Long.parseLong(value);
// Get ownCloud SMS account list
for (Account acct : myAccountList) {
// And get all authorities for this account
List<PeriodicSync> syncList = ContentResolver.getPeriodicSyncs(acct, OCSMSSettingsActivity._accountAuthority);
boolean foundSameSyncCycle = false;
for (PeriodicSync ps : syncList) {
if ((ps.period == syncFreq) && (ps.extras.getInt("synctype") == 1)) {
foundSameSyncCycle = true;
}
}
if (!foundSameSyncCycle) {
Bundle b = new Bundle();
b.putInt("synctype", 1);
ContentResolver.removePeriodicSync(acct, OCSMSSettingsActivity._accountAuthority, b);
if (syncFreq > 0) {
ContentResolver.addPeriodicSync(acct, OCSMSSettingsActivity._accountAuthority, b, syncFreq * 60);
}
}
prefs.putLong(key, syncFreq);
}
} else if ("sync_bulk_messages".equals(key) || "minimum_sync_chars".equals(key)) {
prefs.putInteger(key, Integer.parseInt(value));
}
}
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
super.onMenuItemSelected(featureId, item);
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
break;
default:
return false;
}
return true;
}
}

View File

@ -0,0 +1,21 @@
package fr.unix_experience.owncloud_sms.activities;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import fr.unix_experience.owncloud_sms.R;
public class PrivacyPolicyActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_privacy_policy);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
}
}

View File

@ -0,0 +1,95 @@
package fr.unix_experience.owncloud_sms.activities.remote_account;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.ActionBar;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import java.util.ArrayList;
import fr.unix_experience.android_lib.AppCompatListActivity;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.prefs.OCSMSSharedPrefs;
public class AccountActionsActivity extends AppCompatListActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_account_actions);
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
ArrayList<String> itemList = new ArrayList<>();
ArrayAdapter<String> adp = new ArrayAdapter<>(getBaseContext(),
android.R.layout.simple_dropdown_item_1line, itemList);
setListAdapter(adp);
// Create item list
itemList.add(getBaseContext().getString(R.string.restore_all_messages));
itemList.add(getBaseContext().getString(R.string.reinit_sync_cursor));
adp.notifyDataSetChanged();
// Fetch account name from intent
_accountName = getIntent().getStringExtra("account");
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean retval = true;
switch (item.getItemId()) {
case android.R.id.home:
finish();
break;
default:
retval = super.onOptionsItemSelected(item);
}
return retval;
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
switch (position) {
case 0:
Intent intent = new Intent(this, RestoreMessagesActivity.class);
intent.putExtra("account", _accountName);
try {
startActivity(intent);
}
catch (IllegalStateException e) {
Log.e(AccountActionsActivity.TAG, e.getMessage());
}
break;
case 1:
final Context me = this;
new AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.reinit_sync_cursor)
.setMessage(R.string.reinit_sync_cursor_confirm)
.setPositiveButton(R.string.yes_confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
(new OCSMSSharedPrefs(me)).setLastMessageDate(0L);
Log.i(AccountActionsActivity.TAG, "Synchronization cursor reinitialized");
}
})
.setNegativeButton(R.string.no_confirm, null)
.show();
break;
default: break; // Unhandled
}
}
private String _accountName = "";
private static final String TAG = AccountActionsActivity.class.getSimpleName();
}

View File

@ -2,18 +2,17 @@ package fr.unix_experience.owncloud_sms.activities.remote_account;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.ListActivity;
import android.os.Bundle;
import android.view.MenuItem;
import java.util.ArrayList;
import java.util.Collections;
import fr.nrz.androidlib.adapters.AndroidAccountAdapter;
import fr.unix_experience.android_lib.AppCompatListActivity;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.adapters.AndroidAccountAdapter;
public class AccountListActivity extends ListActivity {
ArrayList<Account> listItems = new ArrayList<>();
AndroidAccountAdapter adapter;
public class AccountListActivity extends AppCompatListActivity {
@Override
public void onCreate(Bundle icicle) {
@ -22,18 +21,36 @@ public class AccountListActivity extends ListActivity {
AccountManager _accountMgr = AccountManager.get(getBaseContext());
setContentView(R.layout.restore_activity_accountlist);
adapter = new AndroidAccountAdapter(this,
getSupportActionBar().setHomeButtonEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
ArrayList<Account> itemList = new ArrayList<>();
AndroidAccountAdapter adapter = new AndroidAccountAdapter(this,
android.R.layout.simple_list_item_1,
listItems,
itemList,
R.layout.account_list_item,
R.id.accountname, ContactListActivity.class);
R.id.accountname, AccountActionsActivity.class);
setListAdapter(adapter);
Account[] accountList =
_accountMgr.getAccountsByType(getString(R.string.account_type));
Collections.addAll(listItems, accountList);
Collections.addAll(itemList, accountList);
adapter.notifyDataSetChanged();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
break;
default:
return super.onOptionsItemSelected(item);
}
return true;
}
}

View File

@ -1,33 +1,47 @@
package fr.unix_experience.owncloud_sms.activities.remote_account;
import android.Manifest;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Handler;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Vector;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.adapters.ContactListAdapter;
import fr.unix_experience.owncloud_sms.adapters.RecoveryPhoneNumberListViewAdapter;
import fr.unix_experience.owncloud_sms.engine.ASyncContactLoad;
import fr.unix_experience.owncloud_sms.enums.PermissionID;
import fr.unix_experience.owncloud_sms.prefs.PermissionChecker;
public class ContactListActivity extends Activity implements ASyncContactLoad {
import static fr.unix_experience.owncloud_sms.enums.PermissionID.REQUEST_CONTACTS;
import static fr.unix_experience.owncloud_sms.enums.PermissionID.REQUEST_MAX;
static AccountManager _accountMgr;
ContactListAdapter adapter;
SwipeRefreshLayout _layout;
ArrayList<String> objects;
public class ContactListActivity extends AppCompatActivity implements ASyncContactLoad {
static AccountManager mAccountMgr;
ContactListAdapter mAdapter = null;
SwipeRefreshLayout mLayout = null;
LinearLayout mContactInfos = null;
ArrayList<String> mObjects;
String mFetchedContact;
RecoveryPhoneNumberListViewAdapter mContactPhoneListAdapter = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -35,126 +49,59 @@ public class ContactListActivity extends Activity implements ASyncContactLoad {
assert getIntent().getExtras() != null;
String accountName = getIntent().getExtras().getString("account");
ContactListActivity.mAccountMgr = AccountManager.get(getBaseContext());
// accountName cannot be null, devel error
assert accountName != null;
ContactListActivity._accountMgr = AccountManager.get(getBaseContext());
Account[] myAccountList =
ContactListActivity._accountMgr.getAccountsByType(getString(R.string.account_type));
// Init view
objects = new ArrayList<>();
mObjects = new ArrayList<>();
setContentView(R.layout.restore_activity_contactlist);
_layout = (SwipeRefreshLayout) findViewById(R.id.contactlist_swipe_container);
mLayout = findViewById(R.id.contactlist_swipe_container);
_layout.setColorScheme(android.R.color.holo_blue_bright,
android.R.color.holo_green_light,
android.R.color.holo_orange_light,
android.R.color.holo_red_light);
adapter = new ContactListAdapter(getBaseContext(),
android.R.layout.simple_spinner_item,
objects,
R.layout.contact_list_item,
R.id.contactname, this);
final Spinner sp = (Spinner) findViewById(R.id.contact_spinner);
final LinearLayout contactInfos = (LinearLayout) findViewById(R.id.contactinfos_layout);
final ProgressBar contactProgressBar = (ProgressBar) findViewById(R.id.contactlist_pgbar);
final TextView contactPhoneList = (TextView) findViewById(R.id.contact_phonelist);
mAdapter = new ContactListAdapter(getBaseContext(), mObjects);
sp.setVisibility(View.INVISIBLE);
contactInfos.setVisibility(View.INVISIBLE);
mContactInfos = findViewById(R.id.contactinfos_layout);
ListView contactPhoneListView = findViewById(R.id.contact_phonelistView);
mContactPhoneListAdapter = new RecoveryPhoneNumberListViewAdapter(getBaseContext());
assert contactPhoneListView != null;
contactPhoneListView.setAdapter(mContactPhoneListAdapter);
sp.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
contactInfos.setVisibility(View.INVISIBLE);
mContactInfos.setVisibility(View.INVISIBLE);
String contactName = sp.getSelectedItem().toString();
Vector<String> phoneList = fetchContact(contactName);
Integer smsCount = 0;
// @TODO asynctask to load more datas
initSpinner();
createAccountList();
}
if (!phoneList.isEmpty()) {
String res = "";
for (String pn: phoneList) {
res += "- " + pn + "\n";
}
contactPhoneList.setText(res);
} else {
contactPhoneList.setText(contactName);
}
private void createAccountList() {
final ProgressBar contactProgressBar = findViewById(R.id.contactlist_pgbar);
assert contactProgressBar != null;
contactInfos.setVisibility(View.VISIBLE);
}
String accountName = getIntent().getExtras().getString("account");
assert accountName != null;
@Override
public void onNothingSelected(AdapterView<?> parent) {
// Nothing to do there
}
private Vector<String> fetchContact(String name) {
Cursor people = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
null, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " = ?",
new String[]{name}, null);
if (people == null) {
return new Vector<>();
}
people.moveToFirst();
Vector<String> r = new Vector<>();
if (people.getCount() == 0) {
return r;
}
String contactId = people.getString(people.getColumnIndex(ContactsContract.Contacts._ID));
if ("1".equalsIgnoreCase(people.getString(people.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)))) {
Cursor phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
new String[]{contactId}, null);
while ((phones != null) && phones.moveToNext()) {
r.add(phones.getString(phones.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER))
.replaceAll(" ", ""));
}
if (phones != null) {
phones.close();
}
}
return r;
}
});
sp.setAdapter(adapter);
Account[] myAccountList =
ContactListActivity.mAccountMgr.getAccountsByType(getString(R.string.account_type));
for (final Account element : myAccountList) {
if (element.name.equals(accountName)) {
// Load "contacts"
contactProgressBar.setVisibility(View.VISIBLE);
sp.setVisibility(View.INVISIBLE);
new ContactLoadTask(element, getBaseContext(), adapter, objects, _layout, contactProgressBar, sp).execute();
new ContactLoadTask(element, getBaseContext(), mAdapter, mObjects, mLayout,
contactProgressBar, mContactInfos).execute();
// Add refresh handler
_layout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
mLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
_layout.setRefreshing(true);
sp.setVisibility(View.INVISIBLE);
mLayout.setRefreshing(true);
mContactInfos.setVisibility(View.INVISIBLE);
contactProgressBar.setVisibility(View.VISIBLE);
(new Handler()).post(new Runnable() {
@Override
public void run() {
objects.clear();
adapter.notifyDataSetChanged();
new ContactLoadTask(element, getBaseContext(), adapter, objects, _layout, contactProgressBar, sp).execute();
mObjects.clear();
mAdapter.notifyDataSetChanged();
new ContactLoadTask(element, getBaseContext(), mAdapter, mObjects,
mLayout, contactProgressBar, mContactInfos).execute();
}
});
}
@ -163,4 +110,110 @@ public class ContactListActivity extends Activity implements ASyncContactLoad {
}
}
}
private void initSpinner() {
final Spinner sp = findViewById(R.id.contact_spinner);
assert sp != null;
sp.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
mContactInfos.setVisibility(View.INVISIBLE);
mContactPhoneListAdapter.clear();
mFetchedContact = sp.getSelectedItem().toString();
fetchContact(mFetchedContact);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
// Nothing to do there
}
});
sp.setAdapter(mAdapter);
}
private void fetchContact(String name) {
if (!PermissionChecker.checkPermission(this, Manifest.permission.READ_CONTACTS,
REQUEST_CONTACTS)) {
return;
}
Cursor people = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
null, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " = ?",
new String[]{name}, null);
if (people == null) {
return;
}
people.moveToFirst();
Vector<String> r = new Vector<>();
if (people.getCount() == 0) {
return;
}
String contactId = people.getString(people.getColumnIndex(ContactsContract.Contacts._ID));
if ("1".equalsIgnoreCase(people.getString(people.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)))) {
Cursor phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
new String[]{contactId}, null);
while ((phones != null) && phones.moveToNext()) {
r.add(phones.getString(phones.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER))
.replaceAll(" ", ""));
}
if (phones != null) {
phones.close();
}
}
Integer smsCount = 0;
// @TODO asynctask to load more datas
if (!r.isEmpty()) {
for (String pn: r) {
mContactPhoneListAdapter.add(pn);
}
} else {
mContactPhoneListAdapter.add(mFetchedContact);
}
mContactInfos.setVisibility(View.VISIBLE);
mContactPhoneListAdapter.notifyDataSetChanged();
}
/*
* Permissions
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
PermissionID requestCodeID = REQUEST_MAX;
if ((requestCode > 0) || (requestCode < REQUEST_MAX.ordinal())) {
requestCodeID = PermissionID.values()[requestCode];
}
switch (requestCodeID) {
case REQUEST_CONTACTS:
for (int grantResult : grantResults) {
Log.i("OcSMS", Integer.toString(grantResult));
}
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
fetchContact(mFetchedContact);
} else {
// Permission Denied
Toast.makeText(this, getString(R.string.err_cannot_read_contacts) + " " +
getString(R.string.please_fix_it), Toast.LENGTH_SHORT)
.show();
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}

View File

@ -0,0 +1,237 @@
package fr.unix_experience.owncloud_sms.activities.remote_account;
/*
* Copyright (c) 2014-2016, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import android.Manifest;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.provider.Telephony;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.engine.ASyncSMSRecovery;
import fr.unix_experience.owncloud_sms.engine.ConnectivityMonitor;
public class RestoreMessagesActivity extends AppCompatActivity {
Account _account = null;
String _defaultSmsApp;
private static final int REQUEST_DEFAULT_SMSAPP = 1;
boolean restoreInProgress = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_restore_messages);
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
assert getIntent().getExtras() != null;
String accountName = getIntent().getExtras().getString("account");
// accountName cannot be null, devel error
assert accountName != null;
AccountManager accountManager = AccountManager.get(getBaseContext());
Account[] accountList = accountManager.getAccountsByType(getString(R.string.account_type));
for (Account element : accountList) {
if (element.name.equals(accountName)) {
_account = element;
}
}
if (_account == null) {
throw new IllegalStateException(getString(R.string.err_didnt_find_account_restore));
}
initInterface();
Button fix_button = findViewById(R.id.button_fix_permissions);
final Button launch_restore = findViewById(R.id.button_launch_restore);
final ProgressBar pb = findViewById(R.id.progressbar_restore);
final RestoreMessagesActivity me = this;
fix_button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
notifyIncompatibleVersion();
return;
}
if (!new ConnectivityMonitor(me).isValid()) {
notifyNoConnectivity();
return;
}
Log.i(RestoreMessagesActivity.TAG, "Ask to change the default SMS app");
Intent intent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT);
intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, getBaseContext().getPackageName());
startActivityForResult(intent, REQUEST_DEFAULT_SMSAPP);
}
});
launch_restore.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (!new ConnectivityMonitor(me).isValid()) {
notifyNoConnectivity();
return;
}
launch_restore.setVisibility(View.INVISIBLE);
pb.setVisibility(View.VISIBLE);
// Verify connectivity
Log.i(RestoreMessagesActivity.TAG, "Launching restore asynchronously");
restoreInProgress = true;
new ASyncSMSRecovery.SMSRecoveryTask(me, _account).execute();
}
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean retval = true;
switch (item.getItemId()) {
case android.R.id.home:
finish();
break;
default:
retval = super.onOptionsItemSelected(item);
}
return retval;
}
private void initInterface() {
TextView tv_error = findViewById(R.id.tv_error_default_smsapp);
tv_error.setText(R.string.error_make_default_sms_app);
findViewById(R.id.tv_restore_finished).setVisibility(View.INVISIBLE);
findViewById(R.id.tv_progress_value).setVisibility(View.INVISIBLE);
Button fix_button = findViewById(R.id.button_fix_permissions);
Button launch_restore = findViewById(R.id.button_launch_restore);
ProgressBar pb = findViewById(R.id.progressbar_restore);
pb.setVisibility(View.INVISIBLE);
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
notifyIncompatibleVersion();
return;
}
_defaultSmsApp = Telephony.Sms.getDefaultSmsPackage(this);
if (!Telephony.Sms.getDefaultSmsPackage(this).equals(getPackageName())) {
_defaultSmsApp = Telephony.Sms.getDefaultSmsPackage(getBaseContext());
tv_error.setVisibility(View.VISIBLE);
fix_button.setVisibility(View.VISIBLE);
launch_restore.setVisibility(View.INVISIBLE);
} else {
tv_error.setVisibility(View.INVISIBLE);
fix_button.setVisibility(View.INVISIBLE);
launch_restore.setVisibility(View.VISIBLE);
}
}
private void errorNotification(int err) {
TextView tv = findViewById(R.id.tv_error_default_smsapp);
Button fix_button = findViewById(R.id.button_fix_permissions);
Button launch_restore = findViewById(R.id.button_launch_restore);
ProgressBar pb = findViewById(R.id.progressbar_restore);
tv.setText(err);
tv.setVisibility(View.VISIBLE);
fix_button.setVisibility(View.INVISIBLE);
launch_restore.setVisibility(View.INVISIBLE);
pb.setVisibility(View.INVISIBLE);
}
private void notifyIncompatibleVersion() {
errorNotification(R.string.err_kitkat_required);
}
private void notifyNoConnectivity() {
errorNotification(R.string.err_no_connection);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case RestoreMessagesActivity.REQUEST_DEFAULT_SMSAPP:
if (resultCode == Activity.RESULT_OK) {
TextView tv = findViewById(R.id.tv_error_default_smsapp);
Button fix_button = findViewById(R.id.button_fix_permissions);
Button launch_restore = findViewById(R.id.button_launch_restore);
tv.setVisibility(View.INVISIBLE);
fix_button.setVisibility(View.INVISIBLE);
launch_restore.setVisibility(View.VISIBLE);
}
break;
default:
break;
}
}
@Override
protected void onResume() {
super.onResume();
if (!new ConnectivityMonitor(this).isValid()) {
notifyNoConnectivity();
return;
}
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
notifyIncompatibleVersion();
return;
}
if (!restoreInProgress) {
initInterface();
}
}
public void onRestoreDone() {
findViewById(R.id.progressbar_restore).setVisibility(View.INVISIBLE);
findViewById(R.id.tv_restore_finished).setVisibility(View.VISIBLE);
Intent finalIntent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT);
finalIntent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, _defaultSmsApp);
startActivity(finalIntent);
restoreInProgress = false;
}
public void onProgressUpdate(Integer value) {
TextView tv_progress = findViewById(R.id.tv_progress_value);
if (tv_progress.getVisibility() == View.INVISIBLE) {
tv_progress.setVisibility(View.VISIBLE);
}
tv_progress.setText(value.toString() + " " + getString(R.string.x_messages_restored));
}
private static final String TAG = RestoreMessagesActivity.class.getSimpleName();
}

View File

@ -0,0 +1,213 @@
package fr.unix_experience.owncloud_sms.activities.virtual;
/**
* Copyright (c) 2013-2015, Loic Blot <loic.blot@unix-experience.fr>
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those
* of the authors and should not be interpreted as representing official policies,
* either expressed or implied, of the FreeBSD Project.
*/
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.util.Log;
import java.util.Vector;
public class VirtualSettingsActivity extends PreferenceActivity {
private static String TAG = VirtualSettingsActivity.class.getSimpleName();
protected static Context _context;
protected static int _prefsRessourceFile;
protected static Vector<BindObjectPref> _boolPrefs = new Vector<>();
protected static Vector<BindObjectPref> _stringPrefs = new Vector<>();
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
VirtualSettingsActivity._context = getBaseContext();
setupSimplePreferencesScreen();
}
/**
* Shows the simplified settings UI if the device configuration if the
* device configuration dictates that a simplified, single-pane UI should be
* shown.
*/
@SuppressWarnings("deprecation")
private void setupSimplePreferencesScreen() {
if (!VirtualSettingsActivity.isSimplePreferences(this)) {
return;
}
// In the simplified UI, fragments are not used at all and we instead
// use the older PreferenceActivity APIs.
addPreferencesFromResource(VirtualSettingsActivity._prefsRessourceFile);
bindPreferences();
}
/** {@inheritDoc} */
@Override
public boolean onIsMultiPane() {
return VirtualSettingsActivity.isXLargeTablet(this) && !VirtualSettingsActivity.isSimplePreferences(this);
}
/**
* Helper method to determine if the device has an extra-large screen. For
* example, 10" tablets are extra-large.
*/
private static boolean isXLargeTablet(Context context) {
return (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE;
}
/**
* Determines whether the simplified settings UI should be shown. This is
* true if this is forced via the device
* doesn't have newer APIs like {@link PreferenceFragment}, or the device
* doesn't have an extra-large screen. In these cases, a single-pane
* "simplified" settings UI should be shown.
*/
protected static boolean isSimplePreferences(Context context) {
return (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
|| !VirtualSettingsActivity.isXLargeTablet(context);
}
/**
* Binds a preference's summary to its value. More specifically, when the
* preference's value is changed, its summary (line of text below the
* preference title) is updated to reflect the value. The summary is also
* immediately updated upon calling this method. The exact display format is
* dependent on the type of preference.
*
* @see #bindPreferenceBooleanToValue
*/
public void bindPreferenceBooleanToValue(Preference preference, Boolean defValue) {
// Set the listener to watch for value changes.
preference
.setOnPreferenceChangeListener(_bindPreferenceListener);
// Trigger the listener immediately with the preference's
// current value.
_bindPreferenceListener.onPreferenceChange(
preference,
PreferenceManager.getDefaultSharedPreferences(
preference.getContext()).getBoolean(
preference.getKey(),
defValue
)
);
}
public void bindPreferenceStringToValue(Preference preference, String defValue) {
// Set the listener to watch for value changes.
preference
.setOnPreferenceChangeListener(_bindPreferenceListener);
// Trigger the listener immediately with the preference's
// current value.
_bindPreferenceListener.onPreferenceChange(
preference,
PreferenceManager.getDefaultSharedPreferences(
preference.getContext()).getString(
preference.getKey(),
defValue
)
);
}
/**
* This fragment shows data and sync preferences only. It is used when the
* activity is showing a two-pane settings UI.
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class DataSyncPreferenceFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(VirtualSettingsActivity._prefsRessourceFile);
VirtualSettingsActivity a = (VirtualSettingsActivity) getActivity();
a.bindPreferences();
}
}
public void bindPreferences() {
// Bind the summaries of EditText/List/Dialog/Ringtone preferences to
// their values. When their values change, their summaries are updated
// to reflect the new value, per the Android Design guidelines.
for (BindObjectPref pref: VirtualSettingsActivity._stringPrefs) {
bindPreferenceStringToValue(findPreference(pref.name),
(String) pref.value);
}
for (BindObjectPref pref: VirtualSettingsActivity._boolPrefs) {
bindPreferenceBooleanToValue(findPreference(pref.name),
(Boolean) pref.value);
}
}
// The preference object, it's only a key value pair
protected class BindObjectPref {
public String name;
Object value;
public BindObjectPref(int resId, Object prefVal) {
name = getString(resId);
value = prefVal;
}
}
/**
* A preference value change listener that updates the preference's summary
* to reflect its new value.
*/
private final Preference.OnPreferenceChangeListener _bindPreferenceListener = new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object value) {
if (preference instanceof ListPreference) {
Log.i(TAG, "Changed list preference " + preference.toString() + " value " + value.toString());
handleListPreference(preference.getKey(), value.toString(), (ListPreference) preference);
} else if (preference instanceof CheckBoxPreference) {
Log.i(TAG, "Changed checkbox preference " + preference.toString() + " value " + value.toString());
handleCheckboxPreference(preference.getKey(), (Boolean) value);
} else {
// For all other preferences, set the summary to the value's
// simple string representation.
//preference.setSummary(boolValue);
}
return true;
}
};
protected void handleCheckboxPreference(String key, Boolean value) {}
protected void handleListPreference(String key, String value,
ListPreference preference) {}
}

View File

@ -0,0 +1,63 @@
package fr.unix_experience.owncloud_sms.adapters;
import android.accounts.Account;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import java.util.ArrayList;
public class AndroidAccountAdapter extends ArrayAdapter<Account> {
private final ArrayList<Account> _accounts;
private static int _itemLayout;
private static int _accountFieldId;
private final Activity _activity;
Class<?> _newActivityClass;
public AndroidAccountAdapter(Activity activity, int resource,
ArrayList<Account> objects, int itemLayout,
int accountFieldId, Class<?> newActivityClass) {
super(activity.getBaseContext(), resource, objects);
_accounts = objects;
AndroidAccountAdapter._itemLayout = itemLayout;
AndroidAccountAdapter._accountFieldId = accountFieldId;
_activity = activity;
_newActivityClass = newActivityClass;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(AndroidAccountAdapter._itemLayout, null);
}
final Account account = _accounts.get(position);
if (account != null) {
TextView label = v.findViewById(AndroidAccountAdapter._accountFieldId);
if (label != null) {
label.setText(account.name + " >");
v.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(_activity, _newActivityClass);
i.putExtra("account", account.name);
_activity.startActivity(i);
}
});
}
}
return v;
}
}

View File

@ -1,6 +1,5 @@
package fr.unix_experience.owncloud_sms.adapters;
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
@ -10,20 +9,18 @@ import android.widget.TextView;
import java.util.ArrayList;
import fr.unix_experience.owncloud_sms.R;
public class ContactListAdapter extends ArrayAdapter<String> {
private final ArrayList<String> _objects;
private static int _itemLayout;
private static int _fieldId;
private final Activity _activity;
// Design
private final static int _itemLayout = R.layout.contact_list_item;
private final static int _fieldId = R.id.contactname;
public ContactListAdapter(Context context, int resource,
ArrayList<String> objects, int itemLayout,
int fieldId, Activity activity) {
super(context, resource, objects);
public ContactListAdapter(Context context, ArrayList<String> objects) {
super(context, android.R.layout.simple_spinner_item, objects);
_objects = objects;
ContactListAdapter._itemLayout = itemLayout;
ContactListAdapter._fieldId = fieldId;
_activity = activity;
}
@Override
@ -42,7 +39,7 @@ public class ContactListAdapter extends ArrayAdapter<String> {
String element = _objects.get(position);
if (element != null) {
TextView label = (TextView) v.findViewById(ContactListAdapter._fieldId);
TextView label = v.findViewById(ContactListAdapter._fieldId);
if (label != null) {
label.setText(element);
}

View File

@ -0,0 +1,55 @@
package fr.unix_experience.owncloud_sms.adapters;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import java.util.ArrayList;
import fr.unix_experience.owncloud_sms.R;
public class RecoveryPhoneNumberListViewAdapter extends ArrayAdapter<String> {
private static final String TAG = "RecPhoneNumberListVAdp";
private static int _fieldId = R.id.recovery_phone;
private static int _itemLayout = R.layout.recovery_phone_list_item;
private static int resource = android.R.layout.simple_list_item_2;
public RecoveryPhoneNumberListViewAdapter(Context context) {
super(context, resource, new ArrayList<String>());
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(RecoveryPhoneNumberListViewAdapter._itemLayout, null);
}
TextView label = v.findViewById(RecoveryPhoneNumberListViewAdapter._fieldId);
if (label != null) {
final String l = getItem(position).toString();
label.setText(getItem(position).toString());
v.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i(TAG, "Clicked on phone " + l);
}
});
v.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
Log.i(TAG, "Long clicked on phone " + l);
return false;
}
});
}
return v;
}
}

View File

@ -25,19 +25,8 @@ import android.accounts.NetworkErrorException;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import com.owncloud.android.lib.common.OwnCloudClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.http.HttpStatus;
import org.json.JSONObject;
import java.io.IOException;
import fr.unix_experience.owncloud_sms.activities.LoginActivity;
import fr.unix_experience.owncloud_sms.enums.LoginReturnCode;
public class OwnCloudAuthenticator extends AbstractAccountAuthenticator {
// Simple constructor
@ -103,91 +92,8 @@ public class OwnCloudAuthenticator extends AbstractAccountAuthenticator {
// TODO Auto-generated method stub
return null;
}
/*
* Return codes
* 1: invalid address
* 2: HTTP failed
* 3: connexion failed
* 4: invalid login
* 5: unknown error
*/
public LoginReturnCode testCredentials() {
LoginReturnCode bRet = LoginReturnCode.OK;
GetMethod get;
int status;
try {
get = new GetMethod(_client.getBaseUri() + "/index.php/ocs/cloud/user?format=json");
} catch (IllegalArgumentException e) {
return LoginReturnCode.INVALID_ADDR;
}
get.addRequestHeader("OCS-APIREQUEST", "true");
try {
status = _client.executeMethod(get);
} catch (IllegalArgumentException e) {
return LoginReturnCode.INVALID_ADDR;
} catch (HttpException e) {
return LoginReturnCode.HTTP_CONN_FAILED;
} catch (IOException e) {
return LoginReturnCode.CONN_FAILED;
}
try {
if(isSuccess(status)) {
String response = get.getResponseBodyAsString();
Log.d(OwnCloudAuthenticator.TAG, "Successful response: " + response);
// Parse the response
JSONObject respJSON = new JSONObject(response);
JSONObject respOCS = respJSON.getJSONObject(OwnCloudAuthenticator.NODE_OCS);
JSONObject respData = respOCS.getJSONObject(OwnCloudAuthenticator.NODE_DATA);
String id = respData.getString(OwnCloudAuthenticator.NODE_ID);
String displayName = respData.getString(OwnCloudAuthenticator.NODE_DISPLAY_NAME);
String email = respData.getString(OwnCloudAuthenticator.NODE_EMAIL);
Log.d(OwnCloudAuthenticator.TAG, "*** Parsed user information: " + id + " - " + displayName + " - " + email);
} else {
String response = get.getResponseBodyAsString();
Log.e(OwnCloudAuthenticator.TAG, "Failed response while getting user information ");
if (response != null) {
Log.e(OwnCloudAuthenticator.TAG, "*** status code: " + status + " ; response message: " + response);
} else {
Log.e(OwnCloudAuthenticator.TAG, "*** status code: " + status);
}
bRet = (status == 401) ? LoginReturnCode.INVALID_LOGIN : LoginReturnCode.UNKNOWN_ERROR;
}
} catch (Exception e) {
Log.e(OwnCloudAuthenticator.TAG, "Exception while getting OC user information", e);
bRet = LoginReturnCode.UNKNOWN_ERROR;
} finally {
get.releaseConnection();
}
return bRet;
}
private boolean isSuccess(int status) {
return (status == HttpStatus.SC_OK);
}
public void setClient(OwnCloudClient oc) {
_client = oc;
}
private final Context _context;
private OwnCloudClient _client;
private static final String TAG = OwnCloudAuthenticator.class.getSimpleName();
private static final String NODE_OCS = "ocs";
private static final String NODE_DATA = "data";
private static final String NODE_ID = "id";
private static final String NODE_DISPLAY_NAME= "display-name";
private static final String NODE_EMAIL= "email";
}

View File

@ -17,6 +17,7 @@ package fr.unix_experience.owncloud_sms.broadcast_receivers;
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import android.Manifest;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.BroadcastReceiver;
@ -24,15 +25,16 @@ import android.content.Context;
import android.content.Intent;
import android.util.Log;
import org.json.JSONArray;
import java.util.concurrent.atomic.AtomicReference;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.engine.ASyncSMSSync;
import fr.unix_experience.owncloud_sms.engine.AndroidSmsFetcher;
import fr.unix_experience.owncloud_sms.engine.ConnectivityMonitor;
import fr.unix_experience.owncloud_sms.engine.SmsFetcher;
import fr.unix_experience.owncloud_sms.enums.PermissionID;
import fr.unix_experience.owncloud_sms.prefs.OCSMSSharedPrefs;
import fr.unix_experience.owncloud_sms.prefs.PermissionChecker;
import ncsmsgo.SmsBuffer;
public class ConnectivityChanged extends BroadcastReceiver implements ASyncSMSSync {
@ -50,38 +52,43 @@ public class ConnectivityChanged extends BroadcastReceiver implements ASyncSMSSy
OCSMSSharedPrefs prefs = new OCSMSSharedPrefs(context);
if (!prefs.pushOnReceive()) {
Log.d(ConnectivityChanged.TAG,"ConnectivityChanges.onReceive: pushOnReceive is disabled");
Log.i(ConnectivityChanged.TAG,"ConnectivityChanges.onReceive: pushOnReceive is disabled");
return;
}
// If data is available and previous dataConnectionState was false, then we need to sync
if (cMon.isValid() && !ConnectivityChanged.dataConnectionAvailable) {
ConnectivityChanged.dataConnectionAvailable = true;
Log.d(ConnectivityChanged.TAG,"ConnectivityChanged.onReceive, data conn available");
Log.i(ConnectivityChanged.TAG,"ConnectivityChanged.onReceive, data conn available");
if (!PermissionChecker.checkPermission(context, Manifest.permission.READ_SMS,
PermissionID.REQUEST_SMS)) {
return;
}
checkMessagesAndSend(context);
}
// No data available and previous dataConnectionState was true
else if (ConnectivityChanged.dataConnectionAvailable && !cMon.isValid()) {
ConnectivityChanged.dataConnectionAvailable = false;
Log.d(ConnectivityChanged.TAG,"ConnectivityChanges.onReceive: data conn is off");
Log.i(ConnectivityChanged.TAG,"ConnectivityChanges.onReceive: data conn is off");
}
}
private void checkMessagesAndSend(Context context) {
// Get last message synced from preferences
Long lastMessageSynced = (new OCSMSSharedPrefs(context)).getLastMessageDate();
Log.d(ConnectivityChanged.TAG,"Synced Last:" + lastMessageSynced);
Log.i(ConnectivityChanged.TAG,"Synced Last:" + lastMessageSynced);
// Now fetch messages since last stored date
JSONArray smsList = new JSONArray();
new SmsFetcher(context).bufferMessagesSinceDate(smsList, lastMessageSynced);
SmsBuffer smsBuffer = new SmsBuffer();
new AndroidSmsFetcher(context).bufferMessagesSinceDate(smsBuffer, lastMessageSynced);
AtomicReference<ConnectivityMonitor> cMon = new AtomicReference<>(new ConnectivityMonitor(context));
// Synchronize if network is valid and there are SMS
if (cMon.get().isValid() && (smsList.length() > 0)) {
new SyncTask(context, smsList).execute();
if (cMon.get().isValid() && !smsBuffer.empty()) {
new SyncTask(context, smsBuffer).execute();
}
}

View File

@ -31,7 +31,7 @@ public class IncomingSms extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (IncomingSms._mboxObserver == null) {
Log.d(IncomingSms.TAG,"_mboxObserver == null");
Log.i(IncomingSms.TAG,"_mboxObserver == null");
IncomingSms._mboxObserver = new SmsObserver(new Handler(), context);
context.getContentResolver().
registerContentObserver(Uri.parse("content://sms"), true, IncomingSms._mboxObserver);

View File

@ -2,7 +2,9 @@ package fr.unix_experience.owncloud_sms.defines;
public class DefaultPrefs {
public final static Integer syncInterval = 15;
public final static Integer minimumCharsForSync = 0;
public final static Boolean pushOnReceive = true;
public final static Boolean showSyncNotifications = true;
public final static Boolean syncWifi = true;
public final static Boolean sync2G = true;

View File

@ -5,16 +5,12 @@ import android.accounts.AccountManager;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.ContactsContract;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Spinner;
import org.json.JSONArray;
import org.json.JSONException;
import java.util.ArrayList;
import java.util.Collections;
@ -22,6 +18,7 @@ import java.util.Collections;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.adapters.ContactListAdapter;
import fr.unix_experience.owncloud_sms.exceptions.OCSyncException;
import ncsmsgo.SmsPhoneListResponse;
public interface ASyncContactLoad {
class ContactLoadTask extends AsyncTask<Void, Void, Boolean> {
@ -32,11 +29,11 @@ public interface ASyncContactLoad {
private final ArrayList<String> _objects;
private final SwipeRefreshLayout _layout;
private final ProgressBar _pg;
private final Spinner _contactSpinner;
private final LinearLayout _contactLayout;
public ContactLoadTask(Account account, Context context,
ContactListAdapter adapter, ArrayList<String> objects, SwipeRefreshLayout layout,
ProgressBar pg, Spinner sp) {
ProgressBar pg, LinearLayout sp) {
if (ContactLoadTask._accountMgr == null) {
ContactLoadTask._accountMgr = AccountManager.get(context);
}
@ -47,23 +44,18 @@ public interface ASyncContactLoad {
_objects = objects;
_layout = layout;
_pg = pg;
_contactSpinner = sp;
_contactLayout = sp;
}
@Override
protected Boolean doInBackground(Void... params) {
// Create client
String ocURI = ContactLoadTask._accountMgr.getUserData(ContactLoadTask._account, "ocURI");
if (ocURI == null) {
// @TODO: Handle the problem
OCSMSOwnCloudClient _client = null;
try {
_client = new OCSMSOwnCloudClient(_context, ContactLoadTask._account);
}
catch (IllegalStateException e) {
return false;
}
Uri serverURI = Uri.parse(ocURI);
OCSMSOwnCloudClient _client = new OCSMSOwnCloudClient(_context,
serverURI, ContactLoadTask._accountMgr.getUserData(ContactLoadTask._account, "ocLogin"),
ContactLoadTask._accountMgr.getPassword(ContactLoadTask._account));
// Remove all objects, due to refreshing handling
_objects.clear();
try {
@ -74,59 +66,24 @@ public interface ASyncContactLoad {
ArrayList<String> serverPhoneList = new ArrayList<>();
JSONArray phoneNumbers = _client.getServerPhoneNumbers();
for (int i = 0; i < phoneNumbers.length(); i++) {
String phone = phoneNumbers.getString(i);
serverPhoneList.add(phone);
SmsPhoneListResponse splr = _client.getServerPhoneNumbers();
if (splr == null) {
_objects.add(_context.getString(R.string.err_fetch_phonelist));
return false;
}
String phoneNumber;
while (!(phoneNumber = splr.getNextEntry()).equals("")) {
serverPhoneList.add(phoneNumber);
}
// Read all contacts
ContentResolver cr = _context.getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI,
null, null, null, null);
if (((cur != null) ? cur.getCount() : 0) > 0) {
String id, name;
while ((cur != null) && cur.moveToNext()) {
id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
name = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
if (Integer.parseInt(cur.getString(
cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) {
readContacts(serverPhoneList);
// Fetch all phone numbers
Cursor pCur = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
new String[]{id}, null);
while ((pCur != null) && pCur.moveToNext()) {
String phoneNo = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
.replaceAll(" ", "");
if (serverPhoneList.contains(phoneNo)) {
if (!_objects.contains(name)) {
_objects.add(name);
}
serverPhoneList.remove(phoneNo);
}
}
if (pCur != null) {
pCur.close();
}
}
}
}
if (cur != null) {
cur.close();
}
for (String phone : serverPhoneList) {
_objects.add(phone);
}
_objects.addAll(serverPhoneList);
// Sort phone numbers
Collections.sort(_objects);
} catch (JSONException e) {
_objects.add(_context.getString(R.string.err_fetch_phonelist));
return false;
} catch (OCSyncException e) {
_objects.add(_context.getString(e.getErrorId()));
return false;
@ -134,6 +91,51 @@ public interface ASyncContactLoad {
return true;
}
private void readContacts(ArrayList<String> serverPhoneList) {
ContentResolver cr = _context.getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI,
null, null, null, null);
if (cur == null) {
return;
}
if (cur.getCount() == 0) {
cur.close();
return;
}
String id, name;
while (cur.moveToNext()) {
id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
name = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
if (Integer.parseInt(cur.getString(
cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) {
// Fetch all phone numbers
Cursor pCur = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
new String[]{id}, null);
while ((pCur != null) && pCur.moveToNext()) {
String phoneNo = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
.replaceAll(" ", "");
if (serverPhoneList.contains(phoneNo)) {
if (!_objects.contains(name)) {
_objects.add(name);
}
serverPhoneList.remove(phoneNo);
}
}
if (pCur != null) {
pCur.close();
}
}
}
cur.close();
}
protected void onPostExecute(Boolean success) {
_adapter.notifyDataSetChanged();
_layout.setRefreshing(false);
@ -141,8 +143,8 @@ public interface ASyncContactLoad {
_pg.setVisibility(View.INVISIBLE);
}
if (_contactSpinner != null) {
_contactSpinner.setVisibility(View.VISIBLE);
if (_contactLayout != null) {
_contactLayout.setVisibility(View.VISIBLE);
}
}
}

View File

@ -0,0 +1,145 @@
package fr.unix_experience.owncloud_sms.engine;
import android.accounts.Account;
import android.content.ContentValues;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.Telephony;
import android.util.Log;
import fr.unix_experience.owncloud_sms.activities.remote_account.RestoreMessagesActivity;
import fr.unix_experience.owncloud_sms.enums.MailboxID;
import fr.unix_experience.owncloud_sms.providers.SmsDataProvider;
import ncsmsgo.SmsMessage;
import ncsmsgo.SmsMessagesResponse;
/*
* Copyright (c) 2014-2016, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
public interface ASyncSMSRecovery {
class SMSRecoveryTask extends AsyncTask<Void, Integer, Void> {
private final RestoreMessagesActivity _context;
private final Account _account;
public SMSRecoveryTask(RestoreMessagesActivity context, Account account) {
_context = context;
_account = account;
}
@Override
protected Void doInBackground(Void... params) {
// This feature is only available for Android 4.4 and greater
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
return null;
}
if (!new ConnectivityMonitor(_context).isValid()) {
Log.e(ASyncSMSRecovery.TAG, "Restore connectivity problems, aborting");
return null;
}
Log.i(ASyncSMSRecovery.TAG, "Starting background recovery");
Long start = (long) 0;
OCSMSOwnCloudClient client = new OCSMSOwnCloudClient(_context, _account);
SmsDataProvider smsDataProvider = new SmsDataProvider(_context);
SmsMessagesResponse obj = client.retrieveSomeMessages(start, 500);
if (obj == null) {
Log.i(ASyncSMSRecovery.TAG, "Retrieved returns failure");
return null;
}
Integer nb = 0;
while ((obj != null) && (obj.getLastID() != start)) {
Log.i(TAG, "Retrieving messages from " + Long.toString(start)
+ " to " + Long.toString(obj.getLastID()));
SmsMessage message;
while ((message = obj.getNextMessage()) != null) {
int mbid = (int) message.getMailbox();
// Ignore invalid mailbox
if (mbid > MailboxID.ALL.getId()) {
Log.e(ASyncSMSRecovery.TAG, "Invalid mailbox found: " + mbid);
continue;
}
String address = message.getAddress();
String body = message.getMessage();
int type = (int) message.getType();
if (address.isEmpty() || body.isEmpty()) {
Log.e(ASyncSMSRecovery.TAG, "Invalid SMS message found: " + message.toString());
continue;
}
MailboxID mailbox_id = MailboxID.fromInt(mbid);
String date = Long.toString(message.getDate());
// Ignore already existing messages
if (smsDataProvider.messageExists(address, body, date, mailbox_id)) {
publishProgress(nb);
continue;
}
ContentValues values = new ContentValues();
values.put(Telephony.Sms.ADDRESS, address);
values.put(Telephony.Sms.BODY, body);
values.put(Telephony.Sms.DATE, date);
values.put(Telephony.Sms.TYPE, type);
values.put(Telephony.Sms.SEEN, 1);
values.put(Telephony.Sms.READ, 1);
_context.getContentResolver().insert(Uri.parse(mailbox_id.getURI()), values);
nb++;
if ((nb % 10) == 0) {
publishProgress(nb);
}
}
start = obj.getLastID();
if (!new ConnectivityMonitor(_context).isValid()) {
Log.e(ASyncSMSRecovery.TAG, "Restore connectivity problems, aborting");
return null;
}
obj = client.retrieveSomeMessages(start, 500);
}
// Force this refresh to fix dates
_context.getContentResolver().delete(Uri.parse("content://sms/conversations/-1"),
null, null);
publishProgress(nb);
Log.i(ASyncSMSRecovery.TAG, "Finishing background recovery");
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
_context.onProgressUpdate(values[0]);
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
_context.onRestoreDone();
}
}
String TAG = ASyncSMSRecovery.class.getSimpleName();
}

View File

@ -1,7 +1,7 @@
package fr.unix_experience.owncloud_sms.engine;
/*
* Copyright (c) 2014-2015, Loic Blot <loic.blot@unix-experience.fr>
* Copyright (c) 2014-2016, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@ -19,53 +19,121 @@ package fr.unix_experience.owncloud_sms.engine;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Context;
import android.net.Uri;
import android.os.AsyncTask;
import android.util.Log;
import org.json.JSONArray;
import android.widget.Toast;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.enums.OCSMSNotificationType;
import fr.unix_experience.owncloud_sms.exceptions.OCSyncException;
import fr.unix_experience.owncloud_sms.notifications.OCSMSNotificationManager;
import fr.unix_experience.owncloud_sms.notifications.OCSMSNotificationUI;
import fr.unix_experience.owncloud_sms.prefs.OCSMSSharedPrefs;
import ncsmsgo.SmsBuffer;
public interface ASyncSMSSync {
class SyncTask extends AsyncTask<Void, Void, Void> {
public SyncTask(Context context, JSONArray smsList) {
public SyncTask(Activity context) {
_activity = context;
_context = context;
_smsList = smsList;
_smsBuffer = null;
}
public SyncTask(Context context, SmsBuffer buffer) {
_activity = null;
_context = context;
_smsBuffer = buffer;
}
@Override
protected Void doInBackground(Void... params) {
Log.i(ASyncSMSSync.TAG, "Starting background sync");
// If no smsBuffer given it's a full sync
if (_smsBuffer == null) {
doFullSync();
}
else {
performSync(_smsBuffer);
}
Log.i(ASyncSMSSync.TAG, "Stopping background sync");
return null;
}
private void doFullSync() {
OCSMSSharedPrefs prefs = new OCSMSSharedPrefs(_context);
long syncStartupDate = prefs.getLastMessageDate();
Log.i(ASyncSMSSync.TAG, "Current message date is " + syncStartupDate);
boolean shouldSync = true;
boolean hasSyncSomething = false;
AndroidSmsFetcher fetcher = new AndroidSmsFetcher(_context);
while (shouldSync) {
SmsBuffer smsBuffer = new SmsBuffer();
fetcher.bufferMessagesSinceDate(smsBuffer, syncStartupDate);
if (smsBuffer.empty()) {
if (_activity != null) {
final boolean syncComplete = hasSyncSomething;
_activity.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(_context,
_context.getString(syncComplete ? R.string.sync_complete : R.string.nothing_to_sync),
Toast.LENGTH_SHORT).show();
}
});
}
Log.i(ASyncSMSSync.TAG, "Finish syncAllMessages(): no more sms");
smsBuffer.clear();
shouldSync = false;
continue;
}
if (prefs.showSyncNotifications()) {
OCSMSNotificationUI.notify(_context, _context.getString(R.string.sync_title),
_context.getString(R.string.sync_inprogress), OCSMSNotificationType.SYNC);
}
try {
syncStartupDate = smsBuffer.getLastMessageDate();
performSync(smsBuffer);
hasSyncSomething = true;
} finally {
OCSMSNotificationUI.cancel(_context, OCSMSNotificationType.SYNC);
}
}
}
private void performSync(SmsBuffer smsBuffer) {
// Get ownCloud SMS account list
AccountManager _accountMgr = AccountManager.get(_context);
Account[] myAccountList = _accountMgr.getAccountsByType(_context.getString(R.string.account_type));
// Notify that we are syncing SMS
OCSMSNotificationManager nMgr = new OCSMSNotificationManager(_context);
for (Account element : myAccountList) {
Uri serverURI = Uri.parse(_accountMgr.getUserData(element, "ocURI"));
OCSMSOwnCloudClient _client = new OCSMSOwnCloudClient(_context,
serverURI, _accountMgr.getUserData(element, "ocLogin"),
_accountMgr.getPassword(element));
try {
_client.doPushRequest(_smsList);
nMgr.dropSyncErrorMsg();
OCSMSOwnCloudClient _client = new OCSMSOwnCloudClient(_context, element);
// Fetch API version first to do some early verifications
Log.i(ASyncSMSSync.TAG, "Server API version: " + _client.getServerAPIVersion());
_client.doPushRequest(smsBuffer);
OCSMSNotificationUI.cancel(_context, OCSMSNotificationType.SYNC_FAILED);
} catch (IllegalStateException e) { // Fail to read account data
OCSMSNotificationUI.notify(_context, _context.getString(R.string.fatal_error),
e.getMessage(), OCSMSNotificationType.SYNC_FAILED);
} catch (OCSyncException e) {
Log.e(ASyncSMSSync.TAG, _context.getString(e.getErrorId()));
nMgr.setSyncErrorMsg(_context.getString(e.getErrorId()));
OCSMSNotificationUI.notify(_context, _context.getString(R.string.fatal_error),
e.getMessage(), OCSMSNotificationType.SYNC_FAILED);
}
}
nMgr.dropSyncProcessMsg();
return null;
smsBuffer.clear();
}
private final SmsBuffer _smsBuffer;
private final Context _context;
private final JSONArray _smsList;
private final Activity _activity;
}
String TAG = ASyncSMSSync.class.getSimpleName();

View File

@ -0,0 +1,254 @@
package fr.unix_experience.owncloud_sms.engine;
/*
* Copyright (c) 2014-2015, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import android.content.Context;
import android.database.Cursor;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import fr.unix_experience.owncloud_sms.enums.MailboxID;
import fr.unix_experience.owncloud_sms.providers.SmsDataProvider;
import ncsmsgo.SmsBuffer;
public class AndroidSmsFetcher {
public AndroidSmsFetcher(Context ct) {
_context = ct;
_existingInboxMessages = null;
_existingSentMessages = null;
_existingDraftsMessages = null;
}
void fetchAllMessages(SmsBuffer result) {
bufferMailboxMessages(result, MailboxID.INBOX);
bufferMailboxMessages(result, MailboxID.SENT);
bufferMailboxMessages(result, MailboxID.DRAFTS);
}
private void readMailBox(Cursor c, SmsBuffer smsBuffer, MailboxID mbID) {
do {
SmsEntry entry = new SmsEntry();
for (int idx = 0; idx < c.getColumnCount(); idx++) {
handleProviderColumn(c, idx, entry);
}
// Mailbox ID is required by server
entry.mailboxId = mbID.ordinal();
smsBuffer.push(entry.id,
mbID.ordinal(),
entry.type,
entry.date,
entry.address,
entry.body,
entry.read ? "true" : "false",
entry.seen ? "true" : "false");
}
while (c.moveToNext());
}
private void bufferMailboxMessages(SmsBuffer smsBuffer, MailboxID mbID) {
if ((_context == null)) {
return;
}
if ((mbID != MailboxID.INBOX) && (mbID != MailboxID.SENT) &&
(mbID != MailboxID.DRAFTS)) {
Log.e(AndroidSmsFetcher.TAG, "Unhandled MailboxID " + mbID.ordinal());
return;
}
// We generate a ID list for this message box
String existingIDs = buildExistingMessagesString(mbID);
Cursor c = new SmsDataProvider(_context).queryNonExistingMessages(mbID.getURI(), existingIDs);
if (c == null) {
return;
}
// Reading mailbox
readMailBox(c, smsBuffer, mbID);
Log.i(AndroidSmsFetcher.TAG, c.getCount() + " messages read from " + mbID.getURI());
c.close();
}
// Used by Content Observer
public SmsBuffer getLastMessage(MailboxID mbID) {
if ((_context == null)) {
return null;
}
// Fetch Sent SMS Message from Built-in Content Provider
Cursor c = (new SmsDataProvider(_context)).query(mbID.getURI());
if (c == null) {
return null;
}
// We create a list of strings to store results
SmsEntry entry = new SmsEntry();
SmsBuffer results = new SmsBuffer();
Integer mboxId = -1;
for (int idx = 0; idx < c.getColumnCount(); idx++) {
Integer rid = handleProviderColumn(c, idx, entry);
if (rid != -1) {
mboxId = rid;
}
}
/*
* Mailbox ID is required by server
* mboxId is greater than server mboxId by 1 because types
* aren't indexed in the same mean
*/
entry.mailboxId = mboxId - 1;
results.push(entry.id,
mbID.ordinal(),
entry.type,
entry.date,
entry.address,
entry.body,
entry.read ? "true" : "false",
entry.seen ? "true" : "false");
c.close();
return results;
}
// Used by ConnectivityChanged Event
public void bufferMessagesSinceDate(SmsBuffer smsBuffer, Long sinceDate) {
bufferMessagesSinceDate(smsBuffer, MailboxID.INBOX, sinceDate);
bufferMessagesSinceDate(smsBuffer, MailboxID.SENT, sinceDate);
bufferMessagesSinceDate(smsBuffer, MailboxID.DRAFTS, sinceDate);
}
// Used by ConnectivityChanged Event
private void bufferMessagesSinceDate(SmsBuffer smsBuffer, MailboxID mbID, Long sinceDate) {
Log.i(AndroidSmsFetcher.TAG, "bufferMessagesSinceDate for " + mbID.toString() + " sinceDate " + sinceDate.toString());
if ((_context == null)) {
return;
}
Cursor c = new SmsDataProvider(_context).queryMessagesSinceDate(mbID.getURI(), sinceDate);
if (c != null) {
Log.i(AndroidSmsFetcher.TAG, "Retrieved " + c.getCount() + " messages.");
} else {
Log.i(AndroidSmsFetcher.TAG, "No message retrieved.");
return;
}
// Read Mailbox
readMailBox(c, smsBuffer, mbID);
Log.i(AndroidSmsFetcher.TAG, c.getCount() + " messages read from " + mbID.getURI());
c.close();
}
private Integer handleProviderColumn(Cursor c, int idx, SmsEntry entry) {
String colName = c.getColumnName(idx);
// Id column is must be an integer
switch (colName) {
case "_id":
entry.id = c.getInt(idx);
break;
case "type":
entry.type = c.getInt(idx);
return c.getInt(idx);
/* For debug purpose
case "length(address)":
Log.i(AndroidSmsFetcher.TAG, "Column name " + colName + " " + c.getString(idx));
break;*/
// Seen and read must be pseudo boolean
case "read":
entry.read = (c.getInt(idx) > 0);
break;
case "seen":
entry.seen = (c.getInt(idx) > 0);
break;
case "date":
entry.date = c.getLong(idx);
break;
case "address":
entry.address = c.getString(idx);
break;
case "body":
entry.body = c.getString(idx);
break;
default:
// Unhandled column
break;
}
return -1;
}
private String buildExistingMessagesString(MailboxID _mbID) {
JSONArray existingMessages = null;
if (_mbID == MailboxID.INBOX) {
existingMessages = _existingInboxMessages;
} else if (_mbID == MailboxID.DRAFTS) {
existingMessages = _existingDraftsMessages;
} else if (_mbID == MailboxID.SENT) {
existingMessages = _existingSentMessages;
}
if (existingMessages == null) {
return "";
}
// Note: The default case isn't possible, we check the mailbox before
StringBuilder sb = new StringBuilder();
int len = existingMessages.length();
for (int i = 0; i < len; i++) {
try {
if (sb.length() > 0) {
sb.append(",");
}
sb.append(existingMessages.getInt(i));
} catch (JSONException ignored) {
}
}
return sb.toString();
}
void setExistingInboxMessages(JSONArray inboxMessages) {
_existingInboxMessages = inboxMessages;
}
void setExistingSentMessages(JSONArray sentMessages) {
_existingSentMessages = sentMessages;
}
void setExistingDraftsMessages(JSONArray draftMessages) {
_existingDraftsMessages = draftMessages;
}
private final Context _context;
private JSONArray _existingInboxMessages;
private JSONArray _existingSentMessages;
private JSONArray _existingDraftsMessages;
private static final String TAG = AndroidSmsFetcher.class.getSimpleName();
}

View File

@ -20,65 +20,66 @@ import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.telephony.TelephonyManager;
import fr.unix_experience.owncloud_sms.prefs.OCSMSSharedPrefs;
public class ConnectivityMonitor {
public ConnectivityMonitor(Context context) {
_context = context;
}
public ConnectivityMonitor(Context context) {
_context = context;
}
// Valid connection = WiFi or Mobile data
public boolean isValid() {
if (_cMgr == null) {
_cMgr = (ConnectivityManager) _context.getSystemService(Context.CONNECTIVITY_SERVICE);
}
// Valid connection = WiFi or Mobile data
public boolean isValid() {
if (_cMgr == null) {
_cMgr = (ConnectivityManager) _context.getSystemService(Context.CONNECTIVITY_SERVICE);
}
android.net.NetworkInfo niWiFi = _cMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
android.net.NetworkInfo niMobile = _cMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
android.net.NetworkInfo niWiFi = _cMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
android.net.NetworkInfo niMobile = _cMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
if (niWiFi.isAvailable() || niMobile.isAvailable()) {
// Load the connectivity manager to determine on which network we are connected
NetworkInfo netInfo = _cMgr.getActiveNetworkInfo();
if (netInfo == null) {
return false;
}
if (niWiFi.isAvailable() || niMobile.isAvailable()) {
// Load the connectivity manager to determine on which network we are connected
NetworkInfo netInfo = _cMgr.getActiveNetworkInfo();
if (netInfo == null) {
return false;
}
OCSMSSharedPrefs prefs = new OCSMSSharedPrefs(_context);
OCSMSSharedPrefs prefs = new OCSMSSharedPrefs(_context);
// Check
switch (netInfo.getType()) {
case ConnectivityManager.TYPE_WIFI:
return prefs.syncInWifi();
case ConnectivityManager.TYPE_MOBILE:
switch (netInfo.getSubtype()) {
case TelephonyManager.NETWORK_TYPE_EDGE:
case TelephonyManager.NETWORK_TYPE_CDMA:
case TelephonyManager.NETWORK_TYPE_1xRTT:
case TelephonyManager.NETWORK_TYPE_IDEN:
return prefs.syncIn2G();
case TelephonyManager.NETWORK_TYPE_GPRS:
return prefs.syncInGPRS();
case TelephonyManager.NETWORK_TYPE_HSDPA:
case TelephonyManager.NETWORK_TYPE_HSPA:
case TelephonyManager.NETWORK_TYPE_HSUPA:
case TelephonyManager.NETWORK_TYPE_UMTS:
case TelephonyManager.NETWORK_TYPE_EHRPD:
case TelephonyManager.NETWORK_TYPE_EVDO_B:
case TelephonyManager.NETWORK_TYPE_HSPAP:
return prefs.syncIn3G();
case TelephonyManager.NETWORK_TYPE_LTE:
return prefs.syncIn4G();
default:
return prefs.syncInOtherModes();
}
default:
return prefs.syncInOtherModes();
}
}
// Check
switch (netInfo.getType()) {
case ConnectivityManager.TYPE_WIFI:
return prefs.syncInWifi();
case ConnectivityManager.TYPE_MOBILE:
switch (netInfo.getSubtype()) {
case TelephonyManager.NETWORK_TYPE_EDGE:
case TelephonyManager.NETWORK_TYPE_CDMA:
case TelephonyManager.NETWORK_TYPE_1xRTT:
case TelephonyManager.NETWORK_TYPE_IDEN:
return prefs.syncIn2G();
case TelephonyManager.NETWORK_TYPE_GPRS:
return prefs.syncInGPRS();
case TelephonyManager.NETWORK_TYPE_HSDPA:
case TelephonyManager.NETWORK_TYPE_HSPA:
case TelephonyManager.NETWORK_TYPE_HSUPA:
case TelephonyManager.NETWORK_TYPE_UMTS:
case TelephonyManager.NETWORK_TYPE_EHRPD:
case TelephonyManager.NETWORK_TYPE_EVDO_B:
case TelephonyManager.NETWORK_TYPE_HSPAP:
return prefs.syncIn3G();
case TelephonyManager.NETWORK_TYPE_LTE:
return prefs.syncIn4G();
default:
return prefs.syncInOtherModes();
}
default:
return prefs.syncInOtherModes();
}
}
return false;
}
return false;
}
private ConnectivityManager _cMgr;
private final Context _context;
private ConnectivityManager _cMgr;
private final Context _context;
}

View File

@ -0,0 +1,114 @@
package fr.unix_experience.owncloud_sms.engine;
/*
* Copyright (c) 2014-2016, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import android.content.Context;
import android.util.Pair;
import java.net.URL;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.enums.OCSyncErrorType;
import fr.unix_experience.owncloud_sms.exceptions.OCSyncException;
import fr.unix_experience.owncloud_sms.providers.AndroidVersionProvider;
import ncsmsgo.SmsBuffer;
import ncsmsgo.SmsHTTPClient;
import ncsmsgo.SmsIDListResponse;
import ncsmsgo.SmsMessagesResponse;
import ncsmsgo.SmsPhoneListResponse;
import ncsmsgo.SmsPushResponse;
public class OCHttpClient {
private SmsHTTPClient _smsHttpClient;
public OCHttpClient(Context context, URL serverURL, String accountName, String accountPassword) {
_smsHttpClient = new SmsHTTPClient();
// @TODO: at a point add a flag to permit insecure connections somewhere instead of trusting them
_smsHttpClient.init(serverURL.toString(), new AndroidVersionProvider(context).getVersionCode(),
accountName, accountPassword, false);
}
private void handleEarlyHTTPStatus(int httpStatus) throws OCSyncException {
switch (httpStatus) {
case 403: {
// Authentication failed
throw new OCSyncException(R.string.err_sync_auth_failed, OCSyncErrorType.AUTH);
}
}
}
public String getLastError() {
return _smsHttpClient.getLastError();
}
Pair<Integer, SmsIDListResponse> getAllSmsIds() throws OCSyncException {
SmsIDListResponse silr = _smsHttpClient.doGetSmsIDList();
int httpStatus = (int) _smsHttpClient.getLastHTTPStatus();
handleEarlyHTTPStatus(httpStatus);
return new Pair<>(httpStatus, silr);
}
// Perform the GoLang doVersionCall and handle return
public Pair<Integer, Integer> getVersion() throws OCSyncException {
Integer serverAPIVersion = (int) _smsHttpClient.doVersionCall();
int httpStatus = (int) _smsHttpClient.getLastHTTPStatus();
handleEarlyHTTPStatus(httpStatus);
// If last status is not 200, send the wrong status now
if (httpStatus != 200) {
return new Pair<>(httpStatus, 0);
}
if (serverAPIVersion > 0) {
return new Pair<>(200, serverAPIVersion);
}
else if (serverAPIVersion == 0) {
// Return default version
return new Pair<>(200, 1);
}
else if (serverAPIVersion == -1) {
// This return code from API means I/O error
throw new OCSyncException(R.string.err_sync_http_request_ioexception, OCSyncErrorType.IO);
}
else {
throw new OCSyncException(R.string.err_sync_http_request_returncode_unhandled, OCSyncErrorType.SERVER_ERROR);
}
}
Pair<Integer, SmsPushResponse> pushSms(SmsBuffer smsBuf) throws OCSyncException {
SmsPushResponse spr = _smsHttpClient.doPushCall(smsBuf);
int httpStatus = (int) _smsHttpClient.getLastHTTPStatus();
handleEarlyHTTPStatus(httpStatus);
return new Pair<>(httpStatus, spr);
}
Pair<Integer, SmsPhoneListResponse> getPhoneList() throws OCSyncException {
SmsPhoneListResponse splr = _smsHttpClient.doGetPhoneList();
int httpStatus = (int) _smsHttpClient.getLastHTTPStatus();
handleEarlyHTTPStatus(httpStatus);
return new Pair<>(httpStatus, splr);
}
Pair<Integer, SmsMessagesResponse> getMessages(Long start, Integer limit) throws OCSyncException {
SmsMessagesResponse smr = _smsHttpClient.doGetMessagesCall(start, limit);
int httpStatus = (int) _smsHttpClient.getLastHTTPStatus();
handleEarlyHTTPStatus(httpStatus);
return new Pair<>(httpStatus, smr);
}
}

View File

@ -1,7 +1,7 @@
package fr.unix_experience.owncloud_sms.engine;
/*
* Copyright (c) 2014-2015, Loic Blot <loic.blot@unix-experience.fr>
* Copyright (c) 2014-2016, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@ -17,385 +17,139 @@ package fr.unix_experience.owncloud_sms.engine;
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.Context;
import android.net.Uri;
import android.util.Log;
import android.util.Pair;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.OwnCloudClientFactory;
import com.owncloud.android.lib.common.OwnCloudCredentialsFactory;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.http.HttpStatus;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.net.URL;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.enums.OCSyncErrorType;
import fr.unix_experience.owncloud_sms.exceptions.OCSyncException;
import fr.unix_experience.owncloud_sms.prefs.OCSMSSharedPrefs;
import ncsmsgo.SmsBuffer;
import ncsmsgo.SmsIDListResponse;
import ncsmsgo.SmsMessagesResponse;
import ncsmsgo.SmsPhoneListResponse;
import ncsmsgo.SmsPushResponse;
@SuppressWarnings("deprecation")
public class OCSMSOwnCloudClient {
public OCSMSOwnCloudClient(Context context, Uri serverURI, String accountName, String accountPassword) {
private static final Integer SERVER_RECOVERY_MSG_LIMIT = 500;
public OCSMSOwnCloudClient(Context context, Account account) {
_context = context;
_ocClient = OwnCloudClientFactory.createOwnCloudClient(
serverURI, _context, true);
// Set basic credentials
_ocClient.setCredentials(
OwnCloudCredentialsFactory.newBasicCredentials(accountName, accountPassword)
);
_serverAPIVersion = 1;
AccountManager accountManager = AccountManager.get(context);
String ocURI = accountManager.getUserData(account, "ocURI");
if (ocURI == null) {
throw new IllegalStateException(context.getString(R.string.err_sync_account_unparsable));
}
try {
URL serverURL = new URL(ocURI);
_http = new OCHttpClient(context,
serverURL, accountManager.getUserData(account, "ocLogin"),
accountManager.getPassword(account));
} catch (MalformedURLException e) {
throw new IllegalStateException(context.getString(R.string.err_sync_account_unparsable));
}
}
public Integer getServerAPIVersion() throws OCSyncException {
GetMethod get = createGetVersionRequest();
JSONObject obj = doHttpRequest(get, true);
if (obj == null) {
// Return default version
return 1;
Pair<Integer, Integer> vPair = _http.getVersion();
_serverAPIVersion = vPair.second;
if (vPair.first == 200 && _serverAPIVersion > 0) {
return _serverAPIVersion;
}
try {
_serverAPIVersion = obj.getInt("version");
}
catch (JSONException e) {
Log.e(OCSMSOwnCloudClient.TAG, "No version received from server, assuming version 1", e);
_serverAPIVersion = 1;
}
return _serverAPIVersion;
return 0;
}
public JSONArray getServerPhoneNumbers() throws OCSyncException {
GetMethod get = createGetPhoneListRequest();
JSONObject obj = doHttpRequest(get, true);
if (obj == null) {
SmsPhoneListResponse getServerPhoneNumbers() throws OCSyncException {
Pair<Integer, SmsPhoneListResponse> response = _http.getPhoneList();
if (response.second == null || response.first != 200) {
return null;
}
try {
return obj.getJSONArray("phoneList");
} catch (JSONException e) {
Log.e(OCSMSOwnCloudClient.TAG, "No phonelist received from server, empty it", e);
return null;
}
return response.second;
}
public void doPushRequest(JSONArray smsList) throws OCSyncException {
/**
public void doPushRequest(SmsBuffer smsBuffer) throws OCSyncException {
/*
* If we need other API push, set it here
*/
switch (_serverAPIVersion) {
case 1:
default: doPushRequestV1(smsList); break;
default: doPushRequestV1(smsBuffer); break;
}
}
public void doPushRequestV1(JSONArray smsList) throws OCSyncException {
// We need to save this date as a step for connectivity change
Long lastMsgDate = (long) 0;
if (smsList == null) {
GetMethod get = createGetSmsIdListRequest();
JSONObject smsGetObj = doHttpRequest(get);
if (smsGetObj == null) {
private void doPushRequestV1(SmsBuffer smsBuffer) throws OCSyncException {
if (smsBuffer == null) {
Pair<Integer, SmsIDListResponse> response = _http.getAllSmsIds();
if (response.second == null) {
return;
}
JSONObject smsBoxes = new JSONObject();
JSONArray inboxSmsList = null, sentSmsList = null, draftsSmsList = null;
try {
smsBoxes = smsGetObj.getJSONObject("smslist");
} catch (JSONException e) {
try {
smsGetObj.getJSONArray("smslist");
} catch (JSONException e2) {
Log.e(OCSMSOwnCloudClient.TAG, "Invalid datas received from server (doPushRequest, get SMS list)", e);
throw new OCSyncException(R.string.err_sync_get_smslist, OCSyncErrorType.PARSE);
}
}
try {
inboxSmsList = smsBoxes.getJSONArray("inbox");
} catch (JSONException e) {
Log.d(OCSMSOwnCloudClient.TAG, "No inbox Sms received from server (doPushRequest, get SMS list)");
}
try {
sentSmsList = smsBoxes.getJSONArray("sent");
} catch (JSONException e) {
Log.d(OCSMSOwnCloudClient.TAG, "No sent Sms received from server (doPushRequest, get SMS list)");
}
try {
draftsSmsList = smsBoxes.getJSONArray("drafts");
} catch (JSONException e) {
Log.d(OCSMSOwnCloudClient.TAG, "No drafts Sms received from server (doPushRequest, get SMS list)");
}
SmsFetcher fetcher = new SmsFetcher(_context);
fetcher.setExistingInboxMessages(inboxSmsList);
fetcher.setExistingSentMessages(sentSmsList);
fetcher.setExistingDraftsMessages(draftsSmsList);
smsList = new JSONArray();
fetcher.fetchAllMessages(smsList);
// Get maximum message date present in smsList to keep a step when connectivity changes
lastMsgDate = fetcher.getLastMessageDate();
// Create new SmsBuffer to get results
smsBuffer = new SmsBuffer();
}
if (smsList.length() == 0) {
Log.d(OCSMSOwnCloudClient.TAG, "No new SMS to sync, sync done");
if (smsBuffer.empty()) {
Log.i(OCSMSOwnCloudClient.TAG, "No new SMS to sync, sync done");
return;
}
PostMethod post = createPushRequest(smsList);
if (post == null) {
Log.e(OCSMSOwnCloudClient.TAG,"Push request for POST is null");
throw new OCSyncException(R.string.err_sync_craft_http_request, OCSyncErrorType.IO);
}
Pair<Integer, SmsPushResponse> response = _http.pushSms(smsBuffer);
JSONObject obj = doHttpRequest(post);
if (obj == null) {
Log.e(OCSMSOwnCloudClient.TAG,"Request failed. It doesn't return a valid JSON Object");
if (response.second == null) {
Log.e(OCSMSOwnCloudClient.TAG,"Push request failed. GoLang response is empty.");
throw new OCSyncException(R.string.err_sync_push_request, OCSyncErrorType.IO);
}
Boolean pushStatus;
String pushMessage;
try {
pushStatus = obj.getBoolean("status");
pushMessage = obj.getString("msg");
}
catch (JSONException e) {
Log.e(OCSMSOwnCloudClient.TAG, "Invalid datas received from server", e);
throw new OCSyncException(R.string.err_sync_push_request_resp, OCSyncErrorType.PARSE);
}
// Push was OK, we can save the lastMessageDate which was saved to server
(new OCSMSSharedPrefs(_context)).setLastMessageDate(lastMsgDate);
(new OCSMSSharedPrefs(_context)).setLastMessageDate(smsBuffer.getLastMessageDate());
Log.d(OCSMSOwnCloudClient.TAG, "SMS Push request said: status " + pushStatus + " - " + pushMessage);
Log.i(OCSMSOwnCloudClient.TAG, "SMS Push request said: status " +
response.second.getStatus() + " - " + response.second.getMessage());
Log.i(OCSMSOwnCloudClient.TAG, "LastMessageDate set to: " + smsBuffer.getLastMessageDate());
}
public GetMethod createGetVersionRequest() {
return createGetRequest(OCSMSOwnCloudClient.OC_GET_VERSION);
}
public GetMethod createGetPhoneListRequest() {
return createGetRequest(OCSMSOwnCloudClient.OC_GET_PHONELIST);
}
public GetMethod createGetSmsIdListRequest() {
return createGetRequest(OCSMSOwnCloudClient.OC_GET_ALL_SMS_IDS);
}
public GetMethod createGetSmsIdListWithStateRequest() {
return createGetRequest(OCSMSOwnCloudClient.OC_GET_ALL_SMS_IDS_WITH_STATUS);
}
public GetMethod createGetLastSmsTimestampRequest() {
return createGetRequest(OCSMSOwnCloudClient.OC_GET_LAST_MSG_TIMESTAMP);
}
private GetMethod createGetRequest(String oc_call) {
GetMethod get = new GetMethod(_ocClient.getBaseUri() + oc_call);
get.addRequestHeader("OCS-APIREQUEST", "true");
return get;
}
public PostMethod createPushRequest(JSONArray smsList) throws OCSyncException {
JSONObject obj = createPushJSONObject(smsList);
if (obj == null) {
SmsMessagesResponse retrieveSomeMessages(Long start, Integer limit) {
// This is not allowed by server
if (limit > OCSMSOwnCloudClient.SERVER_RECOVERY_MSG_LIMIT) {
Log.e(OCSMSOwnCloudClient.TAG, "Message recovery limit exceeded");
return null;
}
StringRequestEntity ent = createJSONRequestEntity(obj);
if (ent == null) {
Pair<Integer, SmsMessagesResponse> response;
try {
response = _http.getMessages(start, limit);
} catch (OCSyncException e) {
Log.e(OCSMSOwnCloudClient.TAG, "Request failed.");
return null;
}
PostMethod post = new PostMethod(_ocClient.getBaseUri() + OCSMSOwnCloudClient.OC_PUSH_ROUTE);
post.addRequestHeader("OCS-APIREQUEST", "true");
post.setRequestEntity(ent);
return post;
}
private JSONObject createPushJSONObject(JSONArray smsList) throws OCSyncException {
if (smsList == null) {
Log.e(OCSMSOwnCloudClient.TAG,"NULL SMS List");
throw new OCSyncException(R.string.err_sync_create_json_null_smslist, OCSyncErrorType.IO);
if (response.second == null) {
Log.e(OCSMSOwnCloudClient.TAG,
"Invalid response received from server, either messages or last_id field is missing.");
return null;
}
JSONObject reqJSON = new JSONObject();
try {
reqJSON.put("smsDatas", smsList);
reqJSON.put("smsCount", smsList.length());
} catch (JSONException e) {
Log.e(OCSMSOwnCloudClient.TAG,"JSON Exception when creating JSON request object");
throw new OCSyncException(R.string.err_sync_create_json_put_smslist, OCSyncErrorType.PARSE);
}
return reqJSON;
return response.second;
}
private StringRequestEntity createJSONRequestEntity(JSONObject obj) throws OCSyncException {
StringRequestEntity requestEntity;
try {
requestEntity = new StringRequestEntity(
obj.toString(),
"application/json",
"UTF-8");
} catch (UnsupportedEncodingException e) {
Log.e(OCSMSOwnCloudClient.TAG,"Unsupported encoding when generating request");
throw new OCSyncException(R.string.err_sync_create_json_request_encoding, OCSyncErrorType.PARSE);
}
return requestEntity;
}
private JSONObject doHttpRequest(HttpMethod req) throws OCSyncException {
return doHttpRequest(req, false);
}
// skipError permit to skip invalid JSON datas
private JSONObject doHttpRequest(HttpMethod req, Boolean skipError) throws OCSyncException {
JSONObject respJSON;
int status = 0;
// We try maximumHttpReqTries because sometimes network is slow or unstable
int tryNb = 0;
ConnectivityMonitor cMon = new ConnectivityMonitor(_context);
while (tryNb < OCSMSOwnCloudClient.maximumHttpReqTries) {
tryNb++;
if (!cMon.isValid()) {
if (tryNb == OCSMSOwnCloudClient.maximumHttpReqTries) {
req.releaseConnection();
throw new OCSyncException(R.string.err_sync_no_connection_available, OCSyncErrorType.IO);
}
continue;
}
try {
status = _ocClient.executeMethod(req);
Log.d(OCSMSOwnCloudClient.TAG, "HTTP Request done at try " + tryNb);
// Force loop exit
tryNb = OCSMSOwnCloudClient.maximumHttpReqTries;
} catch (ConnectException e) {
Log.e(OCSMSOwnCloudClient.TAG, "Unable to perform a connection to ownCloud instance", e);
// If it's the last try
if (tryNb == OCSMSOwnCloudClient.maximumHttpReqTries) {
req.releaseConnection();
throw new OCSyncException(R.string.err_sync_http_request_connect, OCSyncErrorType.IO);
}
} catch (HttpException e) {
Log.e(OCSMSOwnCloudClient.TAG, "Unable to perform a connection to ownCloud instance", e);
// If it's the last try
if (tryNb == OCSMSOwnCloudClient.maximumHttpReqTries) {
req.releaseConnection();
throw new OCSyncException(R.string.err_sync_http_request_httpexception, OCSyncErrorType.IO);
}
} catch (IOException e) {
Log.e(OCSMSOwnCloudClient.TAG, "Unable to perform a connection to ownCloud instance", e);
// If it's the last try
if (tryNb == OCSMSOwnCloudClient.maximumHttpReqTries) {
req.releaseConnection();
throw new OCSyncException(R.string.err_sync_http_request_ioexception, OCSyncErrorType.IO);
}
}
}
if (status == HttpStatus.SC_OK) {
String response;
try {
response = req.getResponseBodyAsString();
} catch (IOException e) {
Log.e(OCSMSOwnCloudClient.TAG, "Unable to parse server response", e);
throw new OCSyncException(R.string.err_sync_http_request_resp, OCSyncErrorType.IO);
}
//Log.d(TAG, "Successful response: " + response);
// Parse the response
try {
respJSON = new JSONObject(response);
} catch (JSONException e) {
if (!skipError) {
if (response.contains("ownCloud") && response.contains("DOCTYPE")) {
Log.e(OCSMSOwnCloudClient.TAG, "OcSMS app not enabled or ownCloud upgrade is required");
throw new OCSyncException(R.string.err_sync_ocsms_not_installed_or_oc_upgrade_required,
OCSyncErrorType.SERVER_ERROR);
}
else {
Log.e(OCSMSOwnCloudClient.TAG, "Unable to parse server response", e);
throw new OCSyncException(R.string.err_sync_http_request_parse_resp, OCSyncErrorType.PARSE);
}
}
return null;
}
} else if (status == HttpStatus.SC_FORBIDDEN) {
// Authentication failed
throw new OCSyncException(R.string.err_sync_auth_failed, OCSyncErrorType.AUTH);
} else {
// Unk error
String response;
try {
response = req.getResponseBodyAsString();
} catch (IOException e) {
Log.e(OCSMSOwnCloudClient.TAG, "Unable to parse server response", e);
throw new OCSyncException(R.string.err_sync_http_request_resp, OCSyncErrorType.PARSE);
}
Log.e(OCSMSOwnCloudClient.TAG, "Server set unhandled HTTP return code " + status);
if (response != null) {
Log.e(OCSMSOwnCloudClient.TAG, "Status code: " + status + ". Response message: " + response);
} else {
Log.e(OCSMSOwnCloudClient.TAG, "Status code: " + status);
}
throw new OCSyncException(R.string.err_sync_http_request_returncode_unhandled, OCSyncErrorType.SERVER_ERROR);
}
return respJSON;
}
private static final int maximumHttpReqTries = 3;
private final OwnCloudClient _ocClient;
private final OCHttpClient _http;
private final Context _context;
private Integer _serverAPIVersion;
private static final String OC_GET_VERSION = "/index.php/apps/ocsms/get/apiversion?format=json";
private static final String OC_GET_ALL_SMS_IDS = "/index.php/apps/ocsms/get/smsidlist?format=json";
private static final String OC_GET_ALL_SMS_IDS_WITH_STATUS = "/index.php/apps/ocsms/get/smsidstate?format=json";
private static final String OC_GET_LAST_MSG_TIMESTAMP = "/index.php/apps/ocsms/get/lastmsgtime?format=json";
private static final String OC_PUSH_ROUTE = "/index.php/apps/ocsms/push?format=json";
private static final String OC_GET_PHONELIST = "/index.php/apps/ocsms/get/phones/numberlist?format=json";
private static final String TAG = OCSMSOwnCloudClient.class.getSimpleName();

View File

@ -0,0 +1,29 @@
package fr.unix_experience.owncloud_sms.engine;
/*
* Copyright (c) 2014-2016, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
public class SmsEntry {
public int id;
public int mailboxId;
public int type;
public boolean read;
public boolean seen;
public long date;
public String address;
public String body;
}

View File

@ -1,320 +0,0 @@
package fr.unix_experience.owncloud_sms.engine;
/*
* Copyright (c) 2014-2015, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import android.content.Context;
import android.database.Cursor;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import fr.unix_experience.owncloud_sms.enums.MailboxID;
import fr.unix_experience.owncloud_sms.providers.SmsDataProvider;
public class SmsFetcher {
public SmsFetcher(Context ct) {
_lastMsgDate = (long) 0;
_context = ct;
_existingInboxMessages = null;
_existingSentMessages = null;
_existingDraftsMessages = null;
}
public void fetchAllMessages(JSONArray result) {
bufferMailboxMessages(result, MailboxID.INBOX);
bufferMailboxMessages(result, MailboxID.SENT);
bufferMailboxMessages(result, MailboxID.DRAFTS);
}
private void bufferMailboxMessages(JSONArray result, MailboxID mbID) {
String mbURI = mapMailboxIDToURI(mbID);
if ((_context == null) || (mbURI == null)) {
return;
}
if ((mbID != MailboxID.INBOX) && (mbID != MailboxID.SENT) &&
(mbID != MailboxID.DRAFTS)) {
Log.e(SmsFetcher.TAG,"Unhandled MailboxID " + mbID.ordinal());
return;
}
// We generate a ID list for this message box
String existingIDs = buildExistingMessagesString(mbID);
Cursor c = new SmsDataProvider(_context).queryNonExistingMessages(mbURI, existingIDs);
// Reading mailbox
if ((c != null) && (c.getCount() > 0)) {
c.moveToFirst();
do {
JSONObject entry = new JSONObject();
try {
String colName;
for(int idx=0;idx<c.getColumnCount();idx++) {
colName = c.getColumnName(idx);
// Id column is must be an integer
switch (colName) {
case "_id":
case "type":
entry.put(colName, c.getInt(idx));
break;
// Seen and read must be pseudo boolean
case "read":
case "seen":
entry.put(colName, (c.getInt(idx) > 0) ? "true" : "false");
break;
default:
// Special case for date, we need to record last without searching
if ("date".equals(colName)) {
Long tmpDate = c.getLong(idx);
if (tmpDate > _lastMsgDate) {
_lastMsgDate = tmpDate;
}
}
entry.put(colName, c.getString(idx));
break;
}
}
// Mailbox ID is required by server
entry.put("mbox", mbID.ordinal());
result.put(entry);
} catch (JSONException e) {
Log.e(SmsFetcher.TAG, "JSON Exception when reading SMS Mailbox", e);
c.close();
}
}
while (c.moveToNext());
Log.d(SmsFetcher.TAG, c.getCount() + " messages read from " + mbURI);
c.close();
}
}
// Used by Content Observer
public JSONArray getLastMessage(MailboxID mbID) {
String mbURI = mapMailboxIDToURI(mbID);
if ((_context == null) || (mbURI == null)) {
return null;
}
// Fetch Sent SMS Message from Built-in Content Provider
Cursor c = (new SmsDataProvider(_context)).query(mbURI);
c.moveToNext();
// We create a list of strings to store results
JSONArray results = new JSONArray();
JSONObject entry = new JSONObject();
try {
Integer mboxId = -1;
String colName;
for(int idx = 0;idx < c.getColumnCount(); idx++) {
colName = c.getColumnName(idx);
// Id column is must be an integer
switch (colName) {
case "_id":
entry.put(colName, c.getInt(idx));
break;
// Seen and read must be pseudo boolean
case "read":
case "seen":
entry.put(colName, (c.getInt(idx) > 0) ? "true" : "false");
break;
case "type":
mboxId = c.getInt(idx);
entry.put(colName, c.getInt(idx));
break;
default:
entry.put(colName, c.getString(idx));
break;
}
}
/*
* Mailbox ID is required by server
* mboxId is greater than server mboxId by 1 because types
* aren't indexed in the same mean
*/
entry.put("mbox", (mboxId - 1));
results.put(entry);
} catch (JSONException e) {
Log.e(SmsFetcher.TAG, "JSON Exception when reading SMS Mailbox", e);
c.close();
}
c.close();
return results;
}
// Used by ConnectivityChanged Event
public void bufferMessagesSinceDate(JSONArray result, Long sinceDate) {
bufferMessagesSinceDate(result, MailboxID.INBOX, sinceDate);
bufferMessagesSinceDate(result, MailboxID.SENT, sinceDate);
bufferMessagesSinceDate(result, MailboxID.DRAFTS, sinceDate);
}
// Used by ConnectivityChanged Event
public void bufferMessagesSinceDate(JSONArray result, MailboxID mbID, Long sinceDate) {
String mbURI = mapMailboxIDToURI(mbID);
if ((_context == null) || (mbURI == null)) {
return;
}
Cursor c = new SmsDataProvider(_context).queryMessagesSinceDate(mbURI, sinceDate);
// Reading mailbox
if ((c != null) && (c.getCount() > 0)) {
c.moveToFirst();
do {
JSONObject entry = new JSONObject();
try {
String colName;
for (int idx = 0; idx < c.getColumnCount(); idx++) {
colName = c.getColumnName(idx);
// Id column is must be an integer
switch (colName) {
case "_id":
case "type":
entry.put(colName, c.getInt(idx));
break;
// Seen and read must be pseudo boolean
case "read":
case "seen":
entry.put(colName, (c.getInt(idx) > 0) ? "true" : "false");
break;
default:
// Special case for date, we need to record last without searching
if ("date".equals(colName)) {
Long tmpDate = c.getLong(idx);
if (tmpDate > _lastMsgDate) {
_lastMsgDate = tmpDate;
}
}
entry.put(colName, c.getString(idx));
break;
}
}
// Mailbox ID is required by server
entry.put("mbox", mbID.ordinal());
result.put(entry);
} catch (JSONException e) {
Log.e(SmsFetcher.TAG, "JSON Exception when reading SMS Mailbox", e);
c.close();
}
}
while (c.moveToNext());
Log.d(SmsFetcher.TAG, c.getCount() + " messages read from " + mbURI);
c.close();
}
}
private String mapMailboxIDToURI(MailboxID mbID) {
if (mbID == MailboxID.INBOX) {
return "content://sms/inbox";
}
else if (mbID == MailboxID.DRAFTS) {
return "content://sms/drafts";
}
else if (mbID == MailboxID.SENT) {
return "content://sms/sent";
}
else if (mbID == MailboxID.ALL) {
return "content://sms";
}
return null;
}
private String buildExistingMessagesString(MailboxID _mbID) {
JSONArray existingMessages = null;
if (_mbID == MailboxID.INBOX) {
existingMessages = _existingInboxMessages;
} else if (_mbID == MailboxID.DRAFTS) {
existingMessages = _existingDraftsMessages;
} else if (_mbID == MailboxID.SENT) {
existingMessages = _existingSentMessages;
} else {
return "";
}
// Note: The default case isn't possible, we check the mailbox before
StringBuilder sb = new StringBuilder();
int len = existingMessages.length();
for (int i = 0; i < len; i++) {
try {
if (sb.length() > 0) {
sb.append(",");
}
sb.append(existingMessages.getInt(i));
} catch (JSONException ignored) {
}
}
return sb.toString();
}
public void setExistingInboxMessages(JSONArray inboxMessages) {
_existingInboxMessages = inboxMessages;
}
public void setExistingSentMessages(JSONArray sentMessages) {
_existingSentMessages = sentMessages;
}
public void setExistingDraftsMessages(JSONArray draftMessages) {
_existingDraftsMessages = draftMessages;
}
public Long getLastMessageDate() {
return _lastMsgDate;
}
private final Context _context;
private JSONArray _existingInboxMessages;
private JSONArray _existingSentMessages;
private JSONArray _existingDraftsMessages;
private Long _lastMsgDate;
private static final String TAG = SmsFetcher.class.getSimpleName();
}

View File

@ -30,6 +30,8 @@ public enum LoginReturnCode {
INVALID_ADDR,
HTTP_CONN_FAILED,
CONN_FAILED,
CONN_FAILED_NOT_FOUND,
INVALID_LOGIN,
UNKNOWN_ERROR,
}

View File

@ -26,8 +26,33 @@ package fr.unix_experience.owncloud_sms.enums;
*/
public enum MailboxID {
INBOX,
SENT,
DRAFTS,
ALL,
INBOX(0),
SENT(1),
DRAFTS(2),
ALL(3);
MailboxID(int id) {
switch (id) {
case 0: uri = "content://sms/inbox"; break;
case 1: uri = "content://sms/sent"; break;
case 2: uri = "content://sms/drafts"; break;
case 3: uri = "content://sms"; break;
default: throw new AssertionError();
}
this.id = id;
}
public static MailboxID fromInt(int id) {
switch (id) {
case 0: return MailboxID.INBOX;
case 1: return MailboxID.SENT;
case 2: return MailboxID.DRAFTS;
case 3: return MailboxID.ALL;
default: throw new AssertionError();
}
}
private final String uri;
private final int id;
public int getId() { return id; }
public String getURI() { return uri; }
}

View File

@ -0,0 +1,48 @@
package fr.unix_experience.owncloud_sms.enums;
import android.app.NotificationManager;
import android.os.Build;
import android.support.annotation.StringRes;
import fr.unix_experience.owncloud_sms.R;
public enum OCSMSNotificationChannel {
DEFAULT("OCSMS_DEFAULT", R.string.notification_channel_name_default, null),
SYNC("OCSMS_SYNC", R.string.notification_channel_name_sync, null);
static {
// well, that's a bit of a hack :/
// can be inlined in the future
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
DEFAULT.importance = NotificationManager.IMPORTANCE_DEFAULT;
SYNC.importance = NotificationManager.IMPORTANCE_LOW;
}
}
private final String channelId;
private final int nameResId;
private final Integer descResId;
private int importance;
OCSMSNotificationChannel(String channelId, @StringRes int nameResId, @StringRes Integer descResId) {
this.channelId = channelId;
this.nameResId = nameResId;
this.descResId = descResId;
}
public String getChannelId() {
return channelId;
}
public int getNameResId() {
return nameResId;
}
public Integer getDescResId() {
return descResId;
}
public int getImportance() {
return importance;
}
}

View File

@ -18,7 +18,23 @@ package fr.unix_experience.owncloud_sms.enums;
*/
public enum OCSMSNotificationType {
SYNC,
SYNC_FAILED,
DEBUG,
SYNC(OCSMSNotificationChannel.SYNC, 0),
SYNC_FAILED(OCSMSNotificationChannel.DEFAULT, 1),
PERMISSION(OCSMSNotificationChannel.DEFAULT, 2);
private final OCSMSNotificationChannel channel;
private final int notificationId;
OCSMSNotificationType(OCSMSNotificationChannel channel, int notificationId) {
this.channel = channel;
this.notificationId = notificationId;
}
public OCSMSNotificationChannel getChannel() {
return channel;
}
public int getNotificationId() {
return notificationId;
}
}

View File

@ -1,5 +1,6 @@
<!--
/*
package fr.unix_experience.owncloud_sms.enums;
/*
* Copyright (c) 2014-2015, Loic Blot <loic.blot@unix-experience.fr>
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
@ -23,17 +24,10 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
-->
<resources>
<style name="OcSmsTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
<item name="android:actionBarStyle">@style/OcSmsActionBar</item>
</style>
<style name="OcSmsActionBar"
parent="@android:style/Widget.Holo.Light.ActionBar.Solid.Inverse">
<item name="android:background">#56a6cf</item>
</style>
</resources>
public enum PermissionID {
REQUEST_SMS,
REQUEST_CONTACTS,
REQUEST_ACCOUNTS,
REQUEST_MAX,
}

View File

@ -0,0 +1,6 @@
package fr.unix_experience.owncloud_sms.misc;
import android.app.Activity;
public class ComposeSmsActivity extends Activity {
}

View File

@ -0,0 +1,14 @@
package fr.unix_experience.owncloud_sms.misc;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
public class HeadlessSmsSendService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}

View File

@ -0,0 +1,12 @@
package fr.unix_experience.owncloud_sms.misc;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class MmsReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
}
}

View File

@ -1,67 +0,0 @@
package fr.unix_experience.owncloud_sms.notifications;
/*
* Copyright (c) 2014-2015, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import android.content.Context;
import fr.nrz.androidlib.notifications.NrzNotification;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.enums.OCSMSNotificationType;
public class OCSMSNotificationManager {
public OCSMSNotificationManager(Context context) {
_context = context;
_notification = new NrzNotification(_context, R.drawable.ic_stat_ocsms);
}
public void setSyncProcessMsg() {
createNotificationIfPossible(OCSMSNotificationType.SYNC,
_context.getString(R.string.sync_title),
_context.getString(R.string.sync_inprogress)
);
}
public void dropSyncProcessMsg() {
_notification.cancelNotify(OCSMSNotificationType.SYNC.ordinal());
}
public void setSyncErrorMsg(String errMsg) {
createNotificationIfPossible(OCSMSNotificationType.SYNC_FAILED,
_context.getString(R.string.sync_title),
_context.getString(R.string.fatal_error) + "\n" + errMsg
);
}
public void dropSyncErrorMsg() {
_notification.cancelNotify(OCSMSNotificationType.SYNC_FAILED.ordinal());
}
public void setDebugMsg(String errMsg) {
createNotificationIfPossible(OCSMSNotificationType.DEBUG,
"DEBUG", errMsg
);
}
private void createNotificationIfPossible(OCSMSNotificationType nType, String nTitle, String nMsg) {
_notification.createNotify(nType.ordinal(), nTitle, nMsg);
}
private final Context _context;
private final NrzNotification _notification;
}

View File

@ -0,0 +1,133 @@
package fr.unix_experience.owncloud_sms.notifications;
/*
* Copyright (c) 2014-2017, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.support.v4.app.NotificationCompat;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.enums.OCSMSNotificationChannel;
import fr.unix_experience.owncloud_sms.enums.OCSMSNotificationType;
/**
* Helper class for showing and canceling ui
* notifications.
* <p/>
* This class makes heavy use of the {@link NotificationCompat.Builder} helper
* class to create notifications in a backward-compatible way.
*/
public class OCSMSNotificationUI {
/**
* The unique identifier for this type of notification.
*/
private static final String NOTIFICATION_TAG = "OCSMS_NOTIFICATION";
public static void notify(Context context, String titleString,
String contentString, OCSMSNotificationType type) {
notify(context, titleString, contentString,
type.getChannel().getChannelId(), type.getNotificationId());
}
/**
* Shows the notification, or updates a previously shown notification of
* this type, with the given parameters.
*
* @see #cancel(Context, OCSMSNotificationType)
*/
public static void notify(Context context, String titleString, String contentString,
String channelId, int notificationId) {
Resources res = context.getResources();
// This image is used as the notification's large icon (thumbnail).
// TODO: Remove this if your notification has no relevant thumbnail.
// Bitmap picture = BitmapFactory.decodeResource(res, R.mipmap.ic_launcher);
// String ticker = (titleString.length() > 20) ? titleString.substring(0, 20) : titleString;
String title = res.getString(R.string.ui_notification_title_template, titleString);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId)
// Set appropriate defaults for the notification light, sound,
// and vibration.
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle(title)
.setContentText(contentString)
// All fields below this line are optional.
// Use a default priority (recognized on devices running Android
// 4.1 or later)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
// Set ticker text (preview) information for this notification.
//.setTicker(ticker)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(contentString)
.setBigContentTitle(title)
.setSummaryText(titleString))
.setAutoCancel(true)
.setColor(context.getResources().getColor(R.color.oc_primary));
notify(context, builder.build(), notificationId);
}
private static void notify(Context context, Notification notification, int notificationId) {
NotificationManager nm = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
createNotificationChannels(context, nm);
nm.notify(OCSMSNotificationUI.NOTIFICATION_TAG, notificationId, notification);
}
/**
* Cancels any notifications of this type previously shown using
* {@link #notify(Context, String, String, OCSMSNotificationType)}.
*/
public static void cancel(Context context, OCSMSNotificationType type) {
cancel(context, type.getNotificationId());
}
public static void cancel(Context context, int notificationId) {
NotificationManager nm = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
nm.cancel(OCSMSNotificationUI.NOTIFICATION_TAG, notificationId);
}
private static void createNotificationChannels(Context context, NotificationManager nm) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
for (OCSMSNotificationChannel ocsmsChannel : OCSMSNotificationChannel.values()) {
NotificationChannel channel = new NotificationChannel(
ocsmsChannel.getChannelId(),
context.getString(ocsmsChannel.getNameResId()),
ocsmsChannel.getImportance());
if (ocsmsChannel.getDescResId() != null) {
channel.setDescription(context.getString(ocsmsChannel.getDescResId()));
}
nm.createNotificationChannel(channel);
}
}
}
}

View File

@ -17,6 +17,7 @@ package fr.unix_experience.owncloud_sms.observers;
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import android.Manifest;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.Context;
@ -24,29 +25,31 @@ import android.database.ContentObserver;
import android.os.Handler;
import android.util.Log;
import org.json.JSONArray;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.engine.ASyncSMSSync;
import fr.unix_experience.owncloud_sms.engine.AndroidSmsFetcher;
import fr.unix_experience.owncloud_sms.engine.ConnectivityMonitor;
import fr.unix_experience.owncloud_sms.engine.OCSMSOwnCloudClient;
import fr.unix_experience.owncloud_sms.engine.SmsFetcher;
import fr.unix_experience.owncloud_sms.enums.MailboxID;
import fr.unix_experience.owncloud_sms.enums.PermissionID;
import fr.unix_experience.owncloud_sms.prefs.PermissionChecker;
import ncsmsgo.SmsBuffer;
public class SmsObserver extends ContentObserver implements ASyncSMSSync {
public SmsObserver(Handler handler) {
super(handler);
}
public SmsObserver(Handler handler, Context ct) {
public SmsObserver(Handler handler, Context ct) {
super(handler);
_context = ct;
}
public void onChange(boolean selfChange) {
if (!PermissionChecker.checkPermission(_context, Manifest.permission.READ_SMS,
PermissionID.REQUEST_SMS)) {
return;
}
super.onChange(selfChange);
Log.d(SmsObserver.TAG, "onChange SmsObserver");
Log.i(SmsObserver.TAG, "onChange SmsObserver");
// No account, abort
Account[] myAccountList = AccountManager.get(_context).
@ -55,14 +58,14 @@ public class SmsObserver extends ContentObserver implements ASyncSMSSync {
return;
}
SmsFetcher fetcher = new SmsFetcher(_context);
JSONArray smsList = fetcher.getLastMessage(MailboxID.ALL);
AndroidSmsFetcher fetcher = new AndroidSmsFetcher(_context);
SmsBuffer smsBuffer = fetcher.getLastMessage(MailboxID.ALL);
ConnectivityMonitor cMon = new ConnectivityMonitor(_context);
// Synchronize if network is valid and there are SMS
if (cMon.isValid() && (smsList != null)) {
new SyncTask(_context, smsList).execute();
if (cMon.isValid() && (smsBuffer != null)) {
new SyncTask(_context, smsBuffer).execute();
}
}

View File

@ -19,7 +19,7 @@ package fr.unix_experience.owncloud_sms.prefs;
import android.content.Context;
import android.content.SharedPreferences;
import fr.nrz.androidlib.common.SharedPrefs;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.defines.DefaultPrefs;
@ -43,6 +43,10 @@ public class OCSMSSharedPrefs extends SharedPrefs {
return _sPrefs.getBoolean("push_on_receive", DefaultPrefs.pushOnReceive);
}
public Boolean showSyncNotifications() {
return _sPrefs.getBoolean("show_sync_notifications", DefaultPrefs.showSyncNotifications);
}
public Boolean syncInWifi() {
return _sPrefs.getBoolean("sync_wifi", DefaultPrefs.syncWifi);
}
@ -70,4 +74,6 @@ public class OCSMSSharedPrefs extends SharedPrefs {
public Integer getSyncBulkLimit() {
return _sPrefs.getInt("sync_bulk_messages", -1);
}
public Integer getMinPhoneNumberCharsToSync() { return _sPrefs.getInt("minimum_sync_chars", DefaultPrefs.minimumCharsForSync); }
}

View File

@ -0,0 +1,87 @@
package fr.unix_experience.owncloud_sms.prefs;
/*
* Copyright (c) 2014-2015, Loic Blot <loic.blot@unix-experience.fr>
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.enums.OCSMSNotificationType;
import fr.unix_experience.owncloud_sms.enums.PermissionID;
import fr.unix_experience.owncloud_sms.notifications.OCSMSNotificationUI;
public class PermissionChecker {
public static boolean checkPermission(Context context, final String permissionName,
final PermissionID permissionId) {
int hasWriteContactsPermission = ContextCompat.checkSelfPermission(context, permissionName);
if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
// Check if we only have a context or a full activity
final Activity activity = (context instanceof Activity) ? ((Activity) context) : null;
if (activity != null) {
// For activity notify directly the user
if (!ActivityCompat.shouldShowRequestPermissionRationale(activity, permissionName)) {
PermissionChecker.showMessageOKCancel(activity, "You need to fix application permissions.",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(activity,
new String[]{ permissionName }, permissionId.ordinal());
}
});
return false;
}
ActivityCompat.requestPermissions(activity, new String[]{permissionName},
permissionId.ordinal());
return false;
}
// For context only show a notification
OCSMSNotificationUI.notify(context, context.getString(R.string.notif_permission_required),
context.getString(R.string.notif_permission_required_content),
OCSMSNotificationType.PERMISSION);
return false;
}
return true;
}
private static void showMessageOKCancel(Activity activity, String message,
DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(activity)
.setMessage(message)
.setPositiveButton(R.string.understood, okListener)
.setNegativeButton(R.string.cancel, null)
.create()
.show();
}
}

View File

@ -0,0 +1,63 @@
package fr.unix_experience.owncloud_sms.prefs;
/**
* Copyright (c) 2013-2015, Loic Blot <loic.blot@unix-experience.fr>
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those
* of the authors and should not be interpreted as representing official policies,
* either expressed or implied, of the FreeBSD Project.
*/
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
public class SharedPrefs {
protected final SharedPreferences _sPrefs;
protected final Context _context;
public SharedPrefs(Context context, int prefFile) {
_context = context;
_sPrefs = _context.getSharedPreferences(_context.getString(prefFile), Context.MODE_PRIVATE);
}
public void putBoolean(String prefKey, Boolean boolValue) {
Editor edit = _sPrefs.edit();
edit.putBoolean(prefKey, boolValue);
edit.apply();
}
public void putInteger(String prefKey, Integer intValue) {
Editor edit = _sPrefs.edit();
edit.putInt(prefKey, intValue);
edit.apply();
}
public void putLong(String prefKey, long longValue) {
Editor edit = _sPrefs.edit();
edit.putLong(prefKey, longValue);
edit.apply();
}
}

View File

@ -0,0 +1,47 @@
package fr.unix_experience.owncloud_sms.providers;
import android.content.Context;
import android.content.pm.PackageManager;
/*
* Copyright (c) 2014-2017, Loic Blot <loic.blot@unix-experience.fr>
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
public class AndroidVersionProvider {
private Context _context;
public AndroidVersionProvider(Context context) {
assert (context != null);
_context = context;
}
public String getVersionCode() {
try {
return _context.getPackageManager().
getPackageInfo(_context.getPackageName(), 0).versionName;
} catch (PackageManager.NameNotFoundException e) {
return "unknown version";
}
}
}

View File

@ -23,78 +23,116 @@ import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.util.Log;
import fr.unix_experience.owncloud_sms.enums.MailboxID;
import fr.unix_experience.owncloud_sms.prefs.OCSMSSharedPrefs;
public class SmsDataProvider extends ContentProvider {
public SmsDataProvider () {}
static String[] messageFields = {
"read",
"date",
"address",
"seen",
"body",
"_id",
"type",
//"length(address)" // For debug purposes
};
public SmsDataProvider (Context ct) {
// WARNING: mandatory
public SmsDataProvider() {}
public SmsDataProvider (Context ct) {
super();
_context = ct;
}
@Override
public boolean onCreate() {
return false;
return true;
}
public Cursor query(String mailBox) {
return query(Uri.parse(mailBox),
new String[] { "read", "date", "address", "seen", "body", "_id", "type", },
SmsDataProvider.messageFields,
null, null, null
);
}
public Cursor query(String mailBox, String selection) {
return query(Uri.parse(mailBox),
new String[] { "read", "date", "address", "seen", "body", "_id", "type", },
selection, null, null
);
}
// NOTE: in APIv2 this call should be modified to use date instead of _id which is likely unique
public Cursor queryNonExistingMessages(String mailBox, String existingIds) {
Log.i(SmsDataProvider.TAG, "queryNonExistingMessages !");
if (!existingIds.isEmpty()) {
return query(mailBox, "_id NOT IN (" + existingIds + ")");
return query(Uri.parse(mailBox), SmsDataProvider.messageFields,
"_id NOT IN (" + existingIds + ")", null, null
);
}
return query(mailBox);
}
public Cursor queryMessagesSinceDate(String mailBox, Long sinceDate) {
OCSMSSharedPrefs prefs = new OCSMSSharedPrefs(_context);
Integer bulkLimit = prefs.getSyncBulkLimit();
String bulkStr = "";
if (bulkLimit > 0) {
bulkStr = "LIMIT " + bulkLimit.toString();
}
return query(mailBox, "date > ?", new String[] { sinceDate.toString() });
return query(Uri.parse(mailBox), SmsDataProvider.messageFields,
"date > ?", new String[] { sinceDate.toString() }, "date ASC"
);
}
public Cursor query(String mailBox, String selection, String[] selectionArgs) {
return query(Uri.parse(mailBox),
new String[] { "read", "date", "address", "seen", "body", "_id", "type", },
selection, selectionArgs, null
);
public boolean messageExists(String address, String body, String date, MailboxID mb) {
Cursor c = query(Uri.parse(mb.getURI()), new String[] { "_id" },
"address = ? AND body = ? AND date = ?",
new String[] { address, body, date }, null);
if (c == null) {
return false;
}
boolean exists = c.getCount() > 0;
c.close();
return exists;
}
@Override
public Cursor query(@NonNull Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
if ((_context == null) || (_context.getContentResolver() == null)) {
Log.e(SmsDataProvider.TAG, "query: context is null or content resolver is null, abort.");
return null;
}
OCSMSSharedPrefs prefs = new OCSMSSharedPrefs(_context);
Integer bulkLimit = prefs.getSyncBulkLimit();
Integer senderMinSize = prefs.getMinPhoneNumberCharsToSync();
if (senderMinSize < 0) {
senderMinSize = 0;
}
// If minSize > 0 we should filter
if (senderMinSize > 0) {
selection = ((selection == null) || (selection.isEmpty())) ?
("length(address) >= " + senderMinSize.toString()) :
("length(address) >= " + senderMinSize.toString() + " AND " + selection);
}
if (bulkLimit > 0) {
if (sortOrder == null)
sortOrder = "_id ";
sortOrder = "date DESC";
sortOrder += " LIMIT " + bulkLimit.toString();
}
if ((_context != null) && (_context.getContentResolver() != null)) {
return _context.getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
Cursor cursor = _context.getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
if (cursor == null) {
return null;
}
return null;
if (cursor.getCount() == 0) {
cursor.close();
return null;
}
if (!cursor.moveToFirst()) {
cursor.close();
return null;
}
return cursor;
}
@Override

View File

@ -18,56 +18,51 @@ package fr.unix_experience.owncloud_sms.sync_adapters;
*/
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.SyncResult;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.engine.OCSMSOwnCloudClient;
import fr.unix_experience.owncloud_sms.enums.OCSMSNotificationType;
import fr.unix_experience.owncloud_sms.enums.OCSyncErrorType;
import fr.unix_experience.owncloud_sms.exceptions.OCSyncException;
import fr.unix_experience.owncloud_sms.notifications.OCSMSNotificationManager;
import fr.unix_experience.owncloud_sms.notifications.OCSMSNotificationUI;
import fr.unix_experience.owncloud_sms.prefs.OCSMSSharedPrefs;
public class SmsSyncAdapter extends AbstractThreadedSyncAdapter {
class SmsSyncAdapter extends AbstractThreadedSyncAdapter {
public SmsSyncAdapter(final Context context, final boolean autoInitialize) {
SmsSyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
_accountMgr = AccountManager.get(context);
}
@Override
public void onPerformSync(final Account account, final Bundle extras, final String authority,
final ContentProviderClient provider, final SyncResult syncResult) {
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {
final OCSMSNotificationManager nMgr = new OCSMSNotificationManager(getContext());
// Create client
final String ocURI = _accountMgr.getUserData(account, "ocURI");
if (ocURI == null) {
nMgr.setSyncErrorMsg(getContext().getString(R.string.err_sync_account_unparsable));
return;
if (new OCSMSSharedPrefs(getContext()).showSyncNotifications()) {
OCSMSNotificationUI.notify(getContext(), getContext().getString(R.string.sync_title),
getContext().getString(R.string.sync_inprogress), OCSMSNotificationType.SYNC);
}
final Uri serverURI = Uri.parse(ocURI);
nMgr.setSyncProcessMsg();
final OCSMSOwnCloudClient _client = new OCSMSOwnCloudClient(getContext(),
serverURI, _accountMgr.getUserData(account, "ocLogin"),
_accountMgr.getPassword(account));
try {
OCSMSOwnCloudClient _client = new OCSMSOwnCloudClient(getContext(), account);
// getServerAPI version
Log.d(TAG,"Server API version: " + _client.getServerAPIVersion());
Log.i(SmsSyncAdapter.TAG, "Server API version: " + _client.getServerAPIVersion());
// and push datas
_client.doPushRequest(null);
nMgr.dropSyncErrorMsg();
} catch (final OCSyncException e) {
nMgr.setSyncErrorMsg(getContext().getString(e.getErrorId()));
OCSMSNotificationUI.cancel(getContext(), OCSMSNotificationType.SYNC_FAILED);
} catch (IllegalStateException e) {
OCSMSNotificationUI.notify(getContext(), getContext().getString(R.string.fatal_error),
e.getMessage(), OCSMSNotificationType.SYNC_FAILED);
} catch (OCSyncException e) {
OCSMSNotificationUI.notify(getContext(), getContext().getString(R.string.fatal_error),
getContext().getString(e.getErrorId()), OCSMSNotificationType.SYNC_FAILED);
if (e.getErrorType() == OCSyncErrorType.IO) {
syncResult.stats.numIoExceptions++;
}
@ -78,15 +73,12 @@ public class SmsSyncAdapter extends AbstractThreadedSyncAdapter {
syncResult.stats.numAuthExceptions++;
}
else {
// UNHANDLED
Log.w(SmsSyncAdapter.TAG, "onPerformSync: unhandled response");
}
} finally {
OCSMSNotificationUI.cancel(getContext(), OCSMSNotificationType.SYNC);
}
nMgr.dropSyncProcessMsg();
}
private final AccountManager _accountMgr;
private static final String TAG = SmsSyncAdapter.class.getSimpleName();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 558 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 748 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 759 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 646 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 916 B

After

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 446 B

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M22.7,19l-9.1,-9.1c0.9,-2.3 0.4,-5 -1.5,-6.9 -2,-2 -5,-2.4 -7.4,-1.3L9,6 6,9 1.6,4.7C0.4,7.1 0.9,10.1 2.9,12.1c1.9,1.9 4.6,2.4 6.9,1.5l9.1,9.1c0.4,0.4 1,0.4 1.4,0l2.3,-2.3c0.5,-0.4 0.5,-1.1 0.1,-1.4z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 686 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 900 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 604 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Some files were not shown because too many files have changed in this diff Show More