1
0
mirror of https://github.com/nerzhul/ownCloud-SMS-App.git synced 2025-06-11 01:46:24 +00:00

Compare commits

..

No commits in common. "master" and "0.23.0" have entirely different histories.

270 changed files with 2266 additions and 16904 deletions

3
.gitignore vendored
View File

@ -4,6 +4,3 @@ ownCloudSMS.iml
ownCloudSMS-release.apk
lint.xml
.gradle/
.idea/
local.properties
ownCloud-SMS-App.iml

View File

@ -1,231 +0,0 @@
<?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>

View File

@ -1,28 +0,0 @@
---
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-.*'

View File

@ -1,17 +0,0 @@
[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

View File

@ -1,20 +0,0 @@
# 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,45 +1,59 @@
# Nextcloud SMS (Android)
# ownCloud SMS Android Application Offical Repository
[![codebeat badge](https://codebeat.co/badges/df05cef7-6724-4a2f-b170-96ed1ab793f6)](https://codebeat.co/projects/github-com-nerzhul-owncloud-sms-app-master)
## Introduction
Nextcloud SMS app pushes your Android devices conversation into your Nextcloud instance, using [ocsms app](https://github.com/nerzhul/ocsms).
ownCloud SMS app push your Android devices conversation into your ownCloud instance, using ocsms app.
## :arrow_forward: Access
<a href="https://play.google.com/store/apps/details?id=fr.unix_experience.owncloud_sms">
<img src="http://www.android.com/images/brand/android_app_on_play_large.png" alt="Download from Google Play" />
</a>
<a href="https://f-droid.org/repository/browse/?fdid=fr.unix_experience.owncloud_sms">
<img src="https://camo.githubusercontent.com/7df0eafa4433fa4919a56f87c3d99cf81b68d01c/68747470733a2f2f662d64726f69642e6f72672f77696b692f696d616765732f632f63342f462d44726f69642d627574746f6e5f617661696c61626c652d6f6e2e706e67" alt="ownCloud Notes App on fdroid.org" />
</a>
[![Nextcloud Notes App on fdroid.org](https://camo.githubusercontent.com/7df0eafa4433fa4919a56f87c3d99cf81b68d01c/68747470733a2f2f662d64726f69642e6f72672f77696b692f696d616765732f632f63342f462d44726f69642d627574746f6e5f617661696c61626c652d6f6e2e706e67)](https://f-droid.org/repository/browse/?fdid=fr.unix_experience.owncloud_sms)
ocsms app sources are available here: https://github.com/nerzhul/ocsms/
ocsms app sources are available here: https://github.com/nextcloud/ocsms
## Application documentation
## :notebook: Application documentation
You can found application documentation here: https://github.com/nerzhul/ownCloud-SMS-App/wiki
You can find the application documentation here: https://github.com/nerzhul/ncsms-android/wiki
## Licence
## :link: Requirements
- [Nextcloud](https://nextcloud.com/) instance running
- [ocsms](https://github.com/nextcloud/ocsms) app enabled
ownCloud SMS Android Application licence is in reflexion, then sources are partial.
## :exclamation: Reporting issues
- App locales and layouts are under BSD 2 clause licence
- App DataProviders are under AGPLv3
- **Client:** https://github.com/nerzhul/ncsms-android/issues
- **Server:** https://github.com/nextcloud/ocsms/issues
## Contributions
## :rocket: Contributions
We are searching for translations in others langs
- 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
To contribute please download `res/values/strings.xml` and `res/values/google_playstore_strings.xml` and give us a translated version!
### Build requirements
- gradle
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
## Reporting issues
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
## Developers
You can find our continuous integration here: http://jenkins.unix-experience.fr/job/ownCloud%20SMS%20%28Android%29/
### 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

View File

@ -1,26 +1,18 @@
buildscript {
repositories {
mavenCentral()
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
}
}
allprojects {
repositories {
jcenter()
google()
classpath 'com.android.tools.build:gradle:2.2.2'
}
}
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
buildToolsVersion "28.0.3"
compileSdkVersion 25
buildToolsVersion "25.0.0"
useLibrary 'org.apache.http.legacy'
lintOptions {
abortOnError false
@ -28,17 +20,9 @@ android {
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'
}
targetSdkVersion 25
maxSdkVersion 25
}
buildTypes {
@ -57,15 +41,14 @@ repositories {
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')
compile project(':owncloudAndroidLibrary')
compile 'com.android.support:support-v13:25.0.1'
compile 'com.android.support:appcompat-v7:25.0.1'
compile 'com.android.support:design:25.0.1'
compile 'in.srain.cube:ultra-ptr:1.0.11'
compile 'com.github.dmytrodanylyk.android-process-button:library:1.0.4'
compile 'com.android.support:support-v4:25.0.1'
}

Binary file not shown.

View File

@ -1,6 +0,0 @@
#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
View File

@ -1,164 +0,0 @@
#!/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
View File

@ -1,90 +0,0 @@
@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

View File

@ -48,9 +48,9 @@
y="32"
x="32"
id="rect4"
style="fill:#0082C9" /><path
style="fill:#1d2d44" /><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>
sodipodi:nodetypes="sssscccssssssssssssssssssssssssssssssss" /></svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -17,11 +17,11 @@ Tell us what happens instead
### Server configuration
**Nextcloud version:**
**ownCloud version:**
**PHP version:**
**Webserver:**
**HTTPd server:**
**HTTPS:**
@ -31,15 +31,10 @@ Tell us what happens instead
**Phone:**
**Nextcloud SMS app version:**
**ownCloud SMS app version:**
### Logs
```
Insert your log here
```
## Screenshots
<!--
Upload your screenshots here if any
-->

View File

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

Binary file not shown.

View File

@ -1,25 +0,0 @@
<?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>

View File

@ -1,51 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

View File

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

View File

@ -1,43 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="fr.unix_experience.owncloud_sms"> <!-- From Android 4.1 to O -->
<uses-sdk android:maxSdkVersion="26" />
<manifest package="fr.unix_experience.owncloud_sms"
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="42"
android:versionName="0.23.0"> <!-- From Android 4.1 to 7.0 -->
<uses-sdk android:maxSdkVersion="25"/>
<uses-permission android:name="android.permission.READ_SMS" />
<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" />
<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" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<!-- For syncer -->
<uses-permission android:name="android.permission.READ_SYNC_STATS" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_SYNC_STATS"/>
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<!-- For account management -->
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
<!-- 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" />
<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="@mipmap/ic_launcher"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/OcSmsTheme">
<!-- Related to periodic sync -->
@ -46,59 +46,55 @@
android:exported="true"
android:process=":sync">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/sync_adapter" />
android:resource="@xml/sync_adapter"/>
</service>
<provider
android:name=".providers.SmsDataProvider"
android:authorities="@string/account_authority"
android:label="@string/pref_title_sync"></provider>
android:label="@string/pref_title_sync">
</provider>
<!-- Related to Login -->
<service android:name=".authenticators.OwnCloudAuthenticatorService">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
<action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/owncloud_account_authenticator" />
android:resource="@xml/owncloud_account_authenticator"/>
</service>
<receiver
android:name=".broadcast_receivers.IncomingSms"
android:permission="android.permission.BROADCAST_SMS">
<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_RECEIVED"/>
<action android:name="android.provider.Telephony.SMS_DELIVER" />
</intent-filter>
</receiver>
<!-- 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">
<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">
<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" />
@ -107,15 +103,12 @@
</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">
<service android:name=".misc.HeadlessSmsSendService"
android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"
android:exported="true" >
<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" />
@ -125,43 +118,43 @@
<receiver android:name=".broadcast_receivers.ConnectivityChanged">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
</receiver>
<activity
android:name=".activities.LoginActivity"
android:label="@string/title_activity_login"
android:theme="@style/OcSmsTheme.Login"></activity>
android:label="@string/title_activity_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.OCSMSSettingsActivity"
android:label="@string/title_activity_general_settings"></activity>
android:label="@string/title_activity_general_settings">
</activity>
<activity
android:name=".activities.MainActivity"
android:label="@string/app_name"
android:theme="@style/OcSmsTheme.Drawer">
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</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>
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>
android:label="@string/restore_all_messages">
</activity>
</application>
</manifest>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

View File

@ -208,7 +208,7 @@ public class AppCompatListActivity extends AppCompatActivity {
super.onContentChanged();
View emptyView = findViewById(R.id.empty);
mList = findViewById(R.id.list);
mList = (ListView)findViewById(R.id.list);
if (mList == null) {
throw new RuntimeException(
"Your content must have a ListView whose id attribute is " +

View File

@ -24,17 +24,15 @@ import android.animation.AnimatorListenerAdapter;
import android.annotation.TargetApi;
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;
@ -43,23 +41,20 @@ import android.widget.Spinner;
import android.widget.TextView;
import com.dd.processbutton.iml.ActionProcessButton;
import java.net.MalformedURLException;
import java.net.URL;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.OwnCloudClientFactory;
import com.owncloud.android.lib.common.OwnCloudCredentialsFactory;
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.engine.OCHttpClient;
import fr.unix_experience.owncloud_sms.exceptions.OCSyncException;
import fr.unix_experience.owncloud_sms.enums.LoginReturnCode;
/**
* A login screen that offers login via email/password.
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
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.
*/
@ -70,7 +65,7 @@ public class LoginActivity extends AppCompatActivity {
private EditText _loginView;
private EditText _passwordView;
private EditText _serverView;
private ActionProcessButton _signInButton;
private ActionProcessButton _signInButton;
private View mProgressView;
private View mLoginFormView;
@ -79,20 +74,17 @@ public class LoginActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
// Set up the login form.
_protocolView = findViewById(R.id.oc_protocol);
_serverView = findViewById(R.id.oc_server);
_loginView = findViewById(R.id.oc_login);
_protocolView = (Spinner) findViewById(R.id.oc_protocol);
_serverView = (EditText) findViewById(R.id.oc_server);
_loginView = (EditText) findViewById(R.id.oc_login);
_passwordView = findViewById(R.id.oc_password);
_passwordView = (EditText) 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;
@ -101,7 +93,7 @@ public class LoginActivity extends AppCompatActivity {
}
});
_signInButton = findViewById(R.id.oc_signin_button);
_signInButton = (ActionProcessButton) findViewById(R.id.oc_signin_button);
_signInButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
@ -113,19 +105,6 @@ public class LoginActivity extends AppCompatActivity {
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
@ -148,12 +127,12 @@ public class LoginActivity extends AppCompatActivity {
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));
@ -167,14 +146,14 @@ public class LoginActivity extends AppCompatActivity {
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;
@ -184,23 +163,19 @@ public class LoginActivity extends AppCompatActivity {
if (cancel) {
// There was an error; don't attempt login and focus the first
// form field with an error.
// reset the button progress
_signInButton.setProgress(0);
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);
_signInButton.setProgress(25);
showProgress(true);
String serverURL = protocol + serverAddr;
try {
mAuthTask = new UserLoginTask(serverURL, login, password);
mAuthTask.execute((Void) null);
} catch (MalformedURLException e) {
Log.e(TAG, "Invalid server URL " + serverURL);
}
mAuthTask = new UserLoginTask(serverURL, login, password);
mAuthTask.execute((Void) null);
}
}
@ -254,118 +229,114 @@ public class LoginActivity extends AppCompatActivity {
* 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 serverURL, String login, String password) throws MalformedURLException {
_serverURL = new URL(serverURL);
Log.i(TAG, "_serverURL = " + serverURL);
UserLoginTask(String serverURI, String login, String password) {
Log.i(TAG, "_serverURI = " + serverURI);
_serverURI = Uri.parse(serverURI);
_login = login;
_password = password;
_last_http_error = null;
}
@Override
protected Boolean doInBackground(Void... params) {
_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);
// 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);
}
@Override
protected void onPostExecute(Boolean success) {
mAuthTask = null;
showProgress(false);
_signInButton.setProgress(90);
_signInButton.setProgress(90);
if (success) {
_signInButton.setProgress(100);
_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 + "@" + _serverURL.getHost();
String accountLabel = _login + "@" + _serverURI.getHost();
// We create the account
Account account = new Account(accountLabel, accountType);
Bundle accountBundle = new Bundle();
accountBundle.putString("ocLogin", _login);
accountBundle.putString("ocURI", _serverURL.toString());
accountBundle.putString("ocURI", _serverURI.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;
boolean serverViewRequestFocus = true;
switch (_returnCode) {
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:
case INVALID_ADDR:
_serverView.setError(getString(R.string.error_invalid_server_address));
break;
case 400:
case 598:
_serverView.setError(getString(R.string.error_connection_failed));
break;
case 599:
case HTTP_CONN_FAILED:
_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;
case CONN_FAILED:
_serverView.setError(getString(R.string.error_connection_failed));
break;
case CONN_FAILED_NOT_FOUND:
_serverView.setError(getString(R.string.error_connection_failed_not_found));
break;
case UNKNOWN_ERROR:
_serverView.setError("UNK");
break;
case INVALID_LOGIN:
_passwordView.setError(getString(R.string.error_invalid_login));
_passwordView.requestFocus();
// Warning, there is no break here to disable serverViewRequestFocus too
case OK:
default:
serverViewRequestFocus = false;
break;
}
if (serverViewRequestFocus) {
_serverView.requestFocus();
}
if (serverViewRequestFocus) {
_serverView.requestFocus();
}
// If not ok, reset the progress
if (_returnCode != 200) {
_signInButton.setProgress(0);
}
// If not ok, reset the progress
if (_returnCode != LoginReturnCode.OK) {
_signInButton.setProgress(0);
}
}
}
@ -374,14 +345,13 @@ public class LoginActivity extends AppCompatActivity {
mAuthTask = null;
showProgress(false);
}
private final URL _serverURL;
private final Uri _serverURI;
private final String _login;
private final String _password;
private String _last_http_error;
private int _returnCode;
static final String PARAM_AUTHTOKEN_TYPE = "auth.token";
private final String TAG = UserLoginTask.class.getCanonicalName();
}
private LoginReturnCode _returnCode;
public static final String PARAM_AUTHTOKEN_TYPE = "auth.token";
private final String TAG = UserLoginTask.class.getCanonicalName();
}
}

View File

@ -38,242 +38,175 @@ 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 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.AndroidSmsFetcher;
import fr.unix_experience.owncloud_sms.engine.ConnectivityMonitor;
import fr.unix_experience.owncloud_sms.enums.OCSMSNotificationType;
import fr.unix_experience.owncloud_sms.enums.PermissionID;
import fr.unix_experience.owncloud_sms.notifications.OCSMSNotificationUI;
import fr.unix_experience.owncloud_sms.prefs.PermissionChecker;
import static fr.unix_experience.owncloud_sms.enums.PermissionID.REQUEST_MAX;
import static fr.unix_experience.owncloud_sms.enums.PermissionID.REQUEST_SMS;
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
private ConnectivityMonitor _ConnectivityMonitor = null;
private DrawerLayout drawer;
implements NavigationView.OnNavigationItemSelectedListener{
private ConnectivityMonitor _ConnectivityMonitor = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
if (_ConnectivityMonitor == null) {
_ConnectivityMonitor = new ConnectivityMonitor(getApplicationContext());
}
if (_ConnectivityMonitor == null) {
_ConnectivityMonitor = new ConnectivityMonitor(getApplicationContext());
}
requestWindowFeature(Window.FEATURE_NO_TITLE);
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
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();
setupToolbar();
if (getSupportActionBar() != null) {
getSupportActionBar().setHomeButtonEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
assert navigationView != null;
navigationView.setNavigationItemSelectedListener(this);
drawer = findViewById(R.id.drawer_layout);
setupDrawer();
drawer.openDrawer(GravityCompat.START);
drawer.openDrawer(GravityCompat.START);
}
protected void setupToolbar() {
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
}
@Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
assert drawer != null;
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
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);
@Override
public boolean onNavigationItemSelected(MenuItem item) {
int id = item.getItemId();
boolean res = true;
NavigationView navigationView = findViewById(R.id.nav_view);
assert navigationView != null;
navigationView.setNavigationItemSelectedListener(this);
}
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;
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
assert drawer != null;
drawer.closeDrawer(GravityCompat.START);
return res;
}
/**
* checks if the drawer exists and is opened.
*
* @return <code>true</code> if the drawer is open, else <code>false</code>
*/
public boolean isDrawerOpen() {
return drawer != null && drawer.isDrawerOpen(GravityCompat.START);
}
/**
* closes the drawer.
*/
public void closeDrawer() {
if (drawer != null) {
drawer.closeDrawer(GravityCompat.START);
}
}
/**
* opens the drawer.
*/
public void openDrawer() {
if (drawer != null) {
drawer.openDrawer(GravityCompat.START);
}
}
@Override
public void onBackPressed() {
if (isDrawerOpen()) {
closeDrawer();
} else {
super.onBackPressed();
}
}
@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);
}
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() {
private boolean openAppSettings () {
startActivity(new Intent(this, OCSMSSettingsActivity.class));
return true;
return true;
}
private boolean openAddAccount() {
private boolean openAddAccount () {
startActivity(new Intent(Settings.ACTION_ADD_ACCOUNT));
return true;
return true;
}
public void syncAllMessages() {
Log.v(MainActivity.TAG, "Launch syncAllMessages()");
if (!PermissionChecker.checkPermission(this, Manifest.permission.READ_SMS,
REQUEST_SMS)) {
return;
}
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;
if (_ConnectivityMonitor.isValid()) {
// Now fetch messages since last stored date
JSONArray smsList = new JSONArray();
new AndroidSmsFetcher(ctx).bufferMessagesSinceDate(smsList, (long) 0);
if (smsList.length() > 0) {
OCSMSNotificationUI.notify(ctx, ctx.getString(R.string.sync_title),
ctx.getString(R.string.sync_inprogress), OCSMSNotificationType.SYNC.ordinal());
new SyncTask(getApplicationContext(), smsList).execute();
}
else {
Toast.makeText(ctx, ctx.getString(R.string.nothing_to_sync), Toast.LENGTH_SHORT).show();
}
}
new SyncTask(this).execute();
Log.v(MainActivity.TAG, "Finish syncAllMessages()");
else {
Toast.makeText(ctx, ctx.getString(R.string.err_sync_no_connection_available), Toast.LENGTH_SHORT).show();
}
Log.v(MainActivity.TAG, "Finish syncAllMessages()");
}
private boolean openMyAccounts() {
private boolean openMyAccounts () {
startActivity(new Intent(this, AccountListActivity.class));
return true;
return true;
}
private boolean openGooglePlayStore() {
private boolean openGooglePlayStore () {
Intent intent;
try {
intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + getPackageName()));
} catch (android.content.ActivityNotFoundException e) {
} catch (android.content.ActivityNotFoundException anfe) {
intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + getPackageName()));
}
startActivity(intent);
return true;
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 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
*/
/*
* 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];
}
@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);
}
}
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();
private static final String TAG = MainActivity.class.getSimpleName();
}

View File

@ -1,7 +1,7 @@
package fr.unix_experience.owncloud_sms.activities;
/*
* Copyright (c) 2014-2017, Loic Blot <loic.blot@unix-experience.fr>
* 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
@ -24,14 +24,9 @@ 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;
@ -42,115 +37,82 @@ 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 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 static AccountManager _accountMgr;
private static String _accountAuthority;
private static String _accountType;
private AppCompatDelegate mDelegate;
@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;
@SuppressWarnings("deprecation")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Bind our boolean preferences
VirtualSettingsActivity._boolPrefs.add(new BindObjectPref("push_on_receive", DefaultPrefs.pushOnReceive));
VirtualSettingsActivity._boolPrefs.add(new BindObjectPref("sync_wifi", DefaultPrefs.syncWifi));
VirtualSettingsActivity._boolPrefs.add(new BindObjectPref("sync_4g", DefaultPrefs.sync4G));
VirtualSettingsActivity._boolPrefs.add(new BindObjectPref("sync_3g", DefaultPrefs.sync3G));
VirtualSettingsActivity._boolPrefs.add(new BindObjectPref("sync_gprs", DefaultPrefs.syncGPRS));
VirtualSettingsActivity._boolPrefs.add(new BindObjectPref("sync_2g", DefaultPrefs.sync2G));
VirtualSettingsActivity._boolPrefs.add(new BindObjectPref("sync_others", DefaultPrefs.syncOthers));
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
}
// Bind our string preferences
VirtualSettingsActivity._stringPrefs.add(new BindObjectPref("sync_frequency", "15"));
VirtualSettingsActivity._stringPrefs.add(new BindObjectPref("sync_bulk_messages", "-1"));
VirtualSettingsActivity._stringPrefs.add(new BindObjectPref("minimum_sync_chars", "1"));
private AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, null);
}
return mDelegate;
}
// Must be at the end, after preference bind
super.onPostCreate(savedInstanceState);
}
public ActionBar getSupportActionBar() {
return getDelegate().getSupportActionBar();
}
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(VirtualSettingsActivity._context);
Log.i(OCSMSSettingsActivity.TAG, "OCSMSSettingsActivity.handleCheckboxPreference: set " + key + " to "
+ value.toString());
prefs.putBoolean(key, value);
}
}
@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;
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);
// 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));
// Set the summary to reflect the new value.
preference
.setSummary((index >= 0) ? preference.getEntries()[index]
: null);
// 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"));
Log.i(OCSMSSettingsActivity.TAG, "Modifying listPreference " + key);
// Must be at the end, after preference bind
super.onPostCreate(savedInstanceState);
}
OCSMSSharedPrefs prefs = new OCSMSSharedPrefs(VirtualSettingsActivity._context);
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);
}
}
// Handle sync frequency change
if ("sync_frequency".equals(key)) {
if (!PermissionChecker.checkPermission(this, Manifest.permission.GET_ACCOUNTS,
REQUEST_ACCOUNTS)) {
return;
}
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);
Account[] myAccountList = OCSMSSettingsActivity._accountMgr.getAccountsByType(OCSMSSettingsActivity._accountType);
long syncFreq = Long.parseLong(value);
// Get ownCloud SMS account list
for (Account acct : myAccountList) {
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) {
for (PeriodicSync ps: syncList) {
if ((ps.period == syncFreq) && (ps.extras.getInt("synctype") == 1)) {
foundSameSyncCycle = true;
}
@ -161,29 +123,16 @@ public class OCSMSSettingsActivity extends VirtualSettingsActivity {
b.putInt("synctype", 1);
ContentResolver.removePeriodicSync(acct, OCSMSSettingsActivity._accountAuthority, b);
if (syncFreq > 0) {
ContentResolver.addPeriodicSync(acct, OCSMSSettingsActivity._accountAuthority, b, syncFreq * 60);
}
if (syncFreq > 0) {
ContentResolver.addPeriodicSync(acct, OCSMSSettingsActivity._accountAuthority, b, syncFreq * 60);
}
}
prefs.putLong(key, syncFreq);
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;
else if ("sync_bulk_messages".equals(key) || "minimum_sync_chars".equals(key)) {
prefs.putInteger(key, Integer.parseInt(value));
}
}
}

View File

@ -1,21 +0,0 @@
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

@ -1,13 +1,8 @@
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;
@ -16,7 +11,6 @@ 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
@ -25,9 +19,6 @@ public class AccountActionsActivity extends AppCompatListActivity {
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);
@ -35,7 +26,6 @@ public class AccountActionsActivity extends AppCompatListActivity {
// Create item list
itemList.add(getBaseContext().getString(R.string.restore_all_messages));
itemList.add(getBaseContext().getString(R.string.reinit_sync_cursor));
adp.notifyDataSetChanged();
@ -43,19 +33,6 @@ public class AccountActionsActivity extends AppCompatListActivity {
_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) {
@ -69,23 +46,6 @@ public class AccountActionsActivity extends AppCompatListActivity {
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
}
}

View File

@ -3,14 +3,13 @@ package fr.unix_experience.owncloud_sms.activities.remote_account;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.os.Bundle;
import android.view.MenuItem;
import java.util.ArrayList;
import java.util.Collections;
import fr.unix_experience.owncloud_sms.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 AppCompatListActivity {
@ -22,35 +21,21 @@ public class AccountListActivity extends AppCompatListActivity {
setContentView(R.layout.restore_activity_accountlist);
getSupportActionBar().setHomeButtonEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
ArrayList<Account> itemList = new ArrayList<>();
ArrayList<Account> itemList = new ArrayList<>();
AndroidAccountAdapter adapter = new AndroidAccountAdapter(this,
AndroidAccountAdapter adapter = new AndroidAccountAdapter(this,
android.R.layout.simple_list_item_1,
itemList,
itemList,
R.layout.account_list_item,
R.id.accountname, AccountActionsActivity.class);
setListAdapter(adapter);
Account[] accountList =
_accountMgr.getAccountsByType(getString(R.string.account_type));
Collections.addAll(itemList, 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

@ -49,78 +49,41 @@ public class ContactListActivity extends AppCompatActivity implements ASyncConta
assert getIntent().getExtras() != null;
String accountName = getIntent().getExtras().getString("account");
// accountName cannot be null, devel error
assert accountName != null;
ContactListActivity.mAccountMgr = AccountManager.get(getBaseContext());
Account[] myAccountList =
ContactListActivity.mAccountMgr.getAccountsByType(getString(R.string.account_type));
// Init view
mObjects = new ArrayList<>();
setContentView(R.layout.restore_activity_contactlist);
mLayout = findViewById(R.id.contactlist_swipe_container);
mLayout = (SwipeRefreshLayout) findViewById(R.id.contactlist_swipe_container);
mAdapter = new ContactListAdapter(getBaseContext(), mObjects);
mContactInfos = findViewById(R.id.contactinfos_layout);
ListView contactPhoneListView = findViewById(R.id.contact_phonelistView);
final Spinner sp = (Spinner) findViewById(R.id.contact_spinner);
mContactInfos = (LinearLayout) findViewById(R.id.contactinfos_layout);
final ProgressBar contactProgressBar = (ProgressBar) findViewById(R.id.contactlist_pgbar);
ListView contactPhoneListView = (ListView) findViewById(R.id.contact_phonelistView);
mContactPhoneListAdapter = new RecoveryPhoneNumberListViewAdapter(getBaseContext());
assert contactPhoneListView != null;
contactPhoneListView.setAdapter(mContactPhoneListAdapter);
mContactInfos.setVisibility(View.INVISIBLE);
initSpinner();
createAccountList();
}
private void createAccountList() {
final ProgressBar contactProgressBar = findViewById(R.id.contactlist_pgbar);
assert contactProgressBar != null;
String accountName = getIntent().getExtras().getString("account");
assert accountName != null;
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);
new ContactLoadTask(element, getBaseContext(), mAdapter, mObjects, mLayout,
contactProgressBar, mContactInfos).execute();
// Add refresh handler
mLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
mLayout.setRefreshing(true);
mContactInfos.setVisibility(View.INVISIBLE);
contactProgressBar.setVisibility(View.VISIBLE);
(new Handler()).post(new Runnable() {
@Override
public void run() {
mObjects.clear();
mAdapter.notifyDataSetChanged();
new ContactLoadTask(element, getBaseContext(), mAdapter, mObjects,
mLayout, contactProgressBar, mContactInfos).execute();
}
});
}
});
return;
}
}
}
private void initSpinner() {
final Spinner sp = findViewById(R.id.contact_spinner);
assert sp != null;
sp.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
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();
mContactPhoneListAdapter.clear();
mFetchedContact = sp.getSelectedItem().toString();
mFetchedContact = sp.getSelectedItem().toString();
fetchContact(mFetchedContact);
}
@ -132,9 +95,37 @@ public class ContactListActivity extends AppCompatActivity implements ASyncConta
});
sp.setAdapter(mAdapter);
for (final Account element : myAccountList) {
if (element.name.equals(accountName)) {
// Load "contacts"
assert contactProgressBar != null;
contactProgressBar.setVisibility(View.VISIBLE);
new ContactLoadTask(element, getBaseContext(), mAdapter, mObjects, mLayout, contactProgressBar, mContactInfos).execute();
// Add refresh handler
mLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
mLayout.setRefreshing(true);
mContactInfos.setVisibility(View.INVISIBLE);
contactProgressBar.setVisibility(View.VISIBLE);
(new Handler()).post(new Runnable() {
@Override
public void run() {
mObjects.clear();
mAdapter.notifyDataSetChanged();
new ContactLoadTask(element, getBaseContext(), mAdapter, mObjects, mLayout, contactProgressBar, mContactInfos).execute();
}
});
}
});
return;
}
}
}
private void fetchContact(String name) {
private void fetchContact(String name) {
if (!PermissionChecker.checkPermission(this, Manifest.permission.READ_CONTACTS,
REQUEST_CONTACTS)) {
return;

View File

@ -26,10 +26,8 @@ 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;
@ -51,9 +49,6 @@ public class RestoreMessagesActivity extends AppCompatActivity {
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");
@ -73,9 +68,9 @@ public class RestoreMessagesActivity extends AppCompatActivity {
}
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);
Button fix_button = (Button) findViewById(R.id.button_fix_permissions);
final Button launch_restore = (Button) findViewById(R.id.button_launch_restore);
final ProgressBar pb = (ProgressBar) findViewById(R.id.progressbar_restore);
final RestoreMessagesActivity me = this;
fix_button.setOnClickListener(new View.OnClickListener() {
@ -116,27 +111,14 @@ public class RestoreMessagesActivity extends AppCompatActivity {
});
}
@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);
TextView tv_error = (TextView) 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);
Button fix_button = (Button) findViewById(R.id.button_fix_permissions);
Button launch_restore = (Button) findViewById(R.id.button_launch_restore);
ProgressBar pb = (ProgressBar) findViewById(R.id.progressbar_restore);
pb.setVisibility(View.INVISIBLE);
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
@ -150,7 +132,8 @@ public class RestoreMessagesActivity extends AppCompatActivity {
tv_error.setVisibility(View.VISIBLE);
fix_button.setVisibility(View.VISIBLE);
launch_restore.setVisibility(View.INVISIBLE);
} else {
}
else {
tv_error.setVisibility(View.INVISIBLE);
fix_button.setVisibility(View.INVISIBLE);
launch_restore.setVisibility(View.VISIBLE);
@ -158,10 +141,10 @@ public class RestoreMessagesActivity extends AppCompatActivity {
}
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);
TextView tv = (TextView) findViewById(R.id.tv_error_default_smsapp);
Button fix_button = (Button) findViewById(R.id.button_fix_permissions);
Button launch_restore = (Button) findViewById(R.id.button_launch_restore);
ProgressBar pb = (ProgressBar) findViewById(R.id.progressbar_restore);
tv.setText(err);
tv.setVisibility(View.VISIBLE);
fix_button.setVisibility(View.INVISIBLE);
@ -182,9 +165,9 @@ public class RestoreMessagesActivity extends AppCompatActivity {
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);
TextView tv = (TextView) findViewById(R.id.tv_error_default_smsapp);
Button fix_button = (Button) findViewById(R.id.button_fix_permissions);
Button launch_restore = (Button) findViewById(R.id.button_launch_restore);
tv.setVisibility(View.INVISIBLE);
fix_button.setVisibility(View.INVISIBLE);
launch_restore.setVisibility(View.VISIBLE);
@ -225,7 +208,7 @@ public class RestoreMessagesActivity extends AppCompatActivity {
}
public void onProgressUpdate(Integer value) {
TextView tv_progress = findViewById(R.id.tv_progress_value);
TextView tv_progress = (TextView) findViewById(R.id.tv_progress_value);
if (tv_progress.getVisibility() == View.INVISIBLE) {
tv_progress.setVisibility(View.VISIBLE);
}

View File

@ -177,9 +177,9 @@ public class VirtualSettingsActivity extends PreferenceActivity {
// 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);
public Object value;
public BindObjectPref(String prefName, Object prefVal) {
name = prefName;
value = prefVal;
}
}

View File

@ -43,7 +43,7 @@ public class AndroidAccountAdapter extends ArrayAdapter<Account> {
final Account account = _accounts.get(position);
if (account != null) {
TextView label = v.findViewById(AndroidAccountAdapter._accountFieldId);
TextView label = (TextView) v.findViewById(AndroidAccountAdapter._accountFieldId);
if (label != null) {
label.setText(account.name + " >");
v.setOnClickListener(new OnClickListener() {

View File

@ -39,7 +39,7 @@ public class ContactListAdapter extends ArrayAdapter<String> {
String element = _objects.get(position);
if (element != null) {
TextView label = v.findViewById(ContactListAdapter._fieldId);
TextView label = (TextView) v.findViewById(ContactListAdapter._fieldId);
if (label != null) {
label.setText(element);
}

View File

@ -30,7 +30,7 @@ public class RecoveryPhoneNumberListViewAdapter extends ArrayAdapter<String> {
v = inflater.inflate(RecoveryPhoneNumberListViewAdapter._itemLayout, null);
}
TextView label = v.findViewById(RecoveryPhoneNumberListViewAdapter._fieldId);
TextView label = (TextView) v.findViewById(RecoveryPhoneNumberListViewAdapter._fieldId);
if (label != null) {
final String l = getItem(position).toString();
label.setText(getItem(position).toString());

View File

@ -25,8 +25,19 @@ 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
@ -92,8 +103,95 @@ 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(OwnCloudAuthenticator.isSuccess(status)) {
String response = get.getResponseBodyAsString();
Log.i(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.i(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);
}
switch (status) {
case 401: bRet = LoginReturnCode.INVALID_LOGIN; break;
case 404: bRet = LoginReturnCode.CONN_FAILED_NOT_FOUND; break;
default: bRet = LoginReturnCode.UNKNOWN_ERROR; break;
}
}
} catch (Exception e) {
Log.e(OwnCloudAuthenticator.TAG, "Exception while getting OC user information", e);
bRet = LoginReturnCode.UNKNOWN_ERROR;
} finally {
get.releaseConnection();
}
return bRet;
}
private static 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

@ -25,6 +25,8 @@ 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;
@ -34,7 +36,6 @@ import fr.unix_experience.owncloud_sms.engine.ConnectivityMonitor;
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 {
@ -81,14 +82,14 @@ public class ConnectivityChanged extends BroadcastReceiver implements ASyncSMSSy
Log.i(ConnectivityChanged.TAG,"Synced Last:" + lastMessageSynced);
// Now fetch messages since last stored date
SmsBuffer smsBuffer = new SmsBuffer();
new AndroidSmsFetcher(context).bufferMessagesSinceDate(smsBuffer, lastMessageSynced);
JSONArray smsList = new JSONArray();
new AndroidSmsFetcher(context).bufferMessagesSinceDate(smsList, lastMessageSynced);
AtomicReference<ConnectivityMonitor> cMon = new AtomicReference<>(new ConnectivityMonitor(context));
// Synchronize if network is valid and there are SMS
if (cMon.get().isValid() && !smsBuffer.empty()) {
new SyncTask(context, smsBuffer).execute();
if (cMon.get().isValid() && (smsList.length() > 0)) {
new SyncTask(context, smsList).execute();
}
}

View File

@ -4,7 +4,6 @@ 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

@ -12,13 +12,15 @@ import android.view.View;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import org.json.JSONArray;
import org.json.JSONException;
import java.util.ArrayList;
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> {
@ -66,24 +68,59 @@ public interface ASyncContactLoad {
ArrayList<String> serverPhoneList = new ArrayList<>();
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);
JSONArray phoneNumbers = _client.getServerPhoneNumbers();
for (int i = 0; i < phoneNumbers.length(); i++) {
String phone = phoneNumbers.getString(i);
serverPhoneList.add(phone);
}
// Read all contacts
readContacts(serverPhoneList);
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) {
_objects.addAll(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);
}
// 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;
@ -91,51 +128,6 @@ 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);

View File

@ -7,11 +7,14 @@ import android.os.AsyncTask;
import android.provider.Telephony;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Iterator;
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>
@ -57,70 +60,81 @@ public interface ASyncSMSRecovery {
OCSMSOwnCloudClient client = new OCSMSOwnCloudClient(_context, _account);
SmsDataProvider smsDataProvider = new SmsDataProvider(_context);
SmsMessagesResponse obj = client.retrieveSomeMessages(start, 500);
JSONObject 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;
try {
while (obj.getLong("last_id") != start) {
JSONObject messages = obj.getJSONObject("messages");
Iterator<?> keys = messages.keys();
while (keys.hasNext()) {
String key = (String)keys.next();
if (messages.get(key) instanceof JSONObject) {
JSONObject msg = messages.getJSONObject(key);
int mbid = msg.getInt("mailbox");
// Ignore invalid mailbox
if (mbid > MailboxID.ALL.getId()) {
Log.e(ASyncSMSRecovery.TAG, "Invalid mailbox found: " + msg.getString("mailbox"));
continue;
}
String address;
String body;
int type;
try {
address = msg.getString("address");
body = msg.getString("msg");
type = msg.getInt("type");
}
catch (JSONException e) {
Log.e(ASyncSMSRecovery.TAG, "Invalid SMS data found: " + e.getMessage());
continue;
}
MailboxID mailbox_id = MailboxID.fromInt(mbid);
// Ignore already existing messages
if (smsDataProvider.messageExists(address, body, key, 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, key);
values.put(Telephony.Sms.TYPE, type);
values.put(Telephony.Sms.SEEN, 1);
values.put(Telephony.Sms.READ, 1);
// @TODO verify message exists before inserting it
_context.getContentResolver().insert(Uri.parse(mailbox_id.getURI()), values);
nb++;
if ((nb % 5) == 0) {
publishProgress(nb);
}
}
}
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.getLong("last_id");
if (!new ConnectivityMonitor(_context).isValid()) {
Log.e(ASyncSMSRecovery.TAG, "Restore connectivity problems, aborting");
return null;
}
obj = client.retrieveSomeMessages(start, 500);
}
start = obj.getLastID();
if (!new ConnectivityMonitor(_context).isValid()) {
Log.e(ASyncSMSRecovery.TAG, "Restore connectivity problems, aborting");
return null;
}
obj = client.retrieveSomeMessages(start, 500);
} catch (JSONException e) {
Log.e(ASyncSMSRecovery.TAG, "Missing last_id field!");
}
// Force this refresh to fix dates
_context.getContentResolver().delete(Uri.parse("content://sms/conversations/-1"),
null, null);
_context.getContentResolver().delete(Uri.parse("content://sms/conversations/-1"), null, null);
publishProgress(nb);

View File

@ -19,94 +19,28 @@ 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.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;
import org.json.JSONArray;
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.OCSMSNotificationUI;
import fr.unix_experience.owncloud_sms.prefs.OCSMSSharedPrefs;
import ncsmsgo.SmsBuffer;
public interface ASyncSMSSync {
class SyncTask extends AsyncTask<Void, Void, Void> {
public SyncTask(Activity context) {
_activity = context;
public SyncTask(Context context, JSONArray smsList) {
_context = context;
_smsBuffer = null;
}
public SyncTask(Context context, SmsBuffer buffer) {
_activity = null;
_context = context;
_smsBuffer = buffer;
_smsList = smsList;
}
@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));
@ -115,25 +49,24 @@ public interface ASyncSMSSync {
for (Account element : myAccountList) {
try {
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);
_client.doPushRequest(_smsList);
OCSMSNotificationUI.cancel(_context);
} catch (IllegalStateException e) { // Fail to read account data
OCSMSNotificationUI.notify(_context, _context.getString(R.string.fatal_error),
e.getMessage(), OCSMSNotificationType.SYNC_FAILED);
e.getMessage(), OCSMSNotificationType.SYNC_FAILED.ordinal());
} catch (OCSyncException e) {
Log.e(ASyncSMSSync.TAG, _context.getString(e.getErrorId()));
OCSMSNotificationUI.notify(_context, _context.getString(R.string.fatal_error),
e.getMessage(), OCSMSNotificationType.SYNC_FAILED);
OCSMSNotificationUI.notify(_context, _context.getString(R.string.fatal_error),
e.getMessage(), OCSMSNotificationType.SYNC_FAILED.ordinal());
}
}
smsBuffer.clear();
OCSMSNotificationUI.cancel(_context);
Log.i(ASyncSMSSync.TAG, "Stopping background sync");
return null;
}
private final SmsBuffer _smsBuffer;
private final Context _context;
private final Activity _activity;
private final JSONArray _smsList;
}
String TAG = ASyncSMSSync.class.getSimpleName();

View File

@ -23,13 +23,14 @@ 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;
import ncsmsgo.SmsBuffer;
public class AndroidSmsFetcher {
public AndroidSmsFetcher(Context ct) {
_lastMsgDate = (long) 0;
_context = ct;
_existingInboxMessages = null;
@ -37,35 +38,13 @@ public class AndroidSmsFetcher {
_existingDraftsMessages = null;
}
void fetchAllMessages(SmsBuffer result) {
void fetchAllMessages(JSONArray 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) {
private void bufferMailboxMessages(JSONArray result, MailboxID mbID) {
if ((_context == null)) {
return;
}
@ -85,14 +64,30 @@ public class AndroidSmsFetcher {
}
// Reading mailbox
readMailBox(c, smsBuffer, mbID);
do {
JSONObject entry = new JSONObject();
try {
for (int idx = 0; idx < c.getColumnCount(); idx++) {
handleProviderColumn(c, idx, entry);
}
// Mailbox ID is required by server
entry.put("mbox", mbID.ordinal());
result.put(entry);
} catch (JSONException e) {
Log.e(AndroidSmsFetcher.TAG, "JSON Exception when reading SMS Mailbox", e);
}
}
while (c.moveToNext());
Log.i(AndroidSmsFetcher.TAG, c.getCount() + " messages read from " + mbID.getURI());
c.close();
}
// Used by Content Observer
public SmsBuffer getLastMessage(MailboxID mbID) {
public JSONArray getLastMessage(MailboxID mbID) {
if ((_context == null)) {
return null;
}
@ -104,31 +99,29 @@ public class AndroidSmsFetcher {
}
// We create a list of strings to store results
SmsEntry entry = new SmsEntry();
SmsBuffer results = new SmsBuffer();
JSONArray results = new JSONArray();
JSONObject entry = new JSONObject();
Integer mboxId = -1;
for (int idx = 0; idx < c.getColumnCount(); idx++) {
Integer rid = handleProviderColumn(c, idx, entry);
if (rid != -1) {
mboxId = rid;
try {
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");
/*
* 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(AndroidSmsFetcher.TAG, "JSON Exception when reading SMS Mailbox", e);
}
c.close();
@ -136,14 +129,14 @@ public class AndroidSmsFetcher {
}
// 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);
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
private void bufferMessagesSinceDate(SmsBuffer smsBuffer, MailboxID mbID, Long sinceDate) {
private void bufferMessagesSinceDate(JSONArray result, MailboxID mbID, Long sinceDate) {
Log.i(AndroidSmsFetcher.TAG, "bufferMessagesSinceDate for " + mbID.toString() + " sinceDate " + sinceDate.toString());
if ((_context == null)) {
return;
@ -157,23 +150,37 @@ public class AndroidSmsFetcher {
return;
}
// Read Mailbox
readMailBox(c, smsBuffer, mbID);
do {
JSONObject entry = new JSONObject();
try {
for (int idx = 0; idx < c.getColumnCount(); idx++) {
handleProviderColumn(c, idx, entry);
}
// Mailbox ID is required by server
entry.put("mbox", mbID.ordinal());
result.put(entry);
} catch (JSONException e) {
Log.e(AndroidSmsFetcher.TAG, "JSON Exception when reading SMS Mailbox", e);
}
}
while (c.moveToNext());
Log.i(AndroidSmsFetcher.TAG, c.getCount() + " messages read from " + mbID.getURI());
c.close();
}
private Integer handleProviderColumn(Cursor c, int idx, SmsEntry entry) {
private Integer handleProviderColumn(Cursor c, int idx, JSONObject entry) throws JSONException {
String colName = c.getColumnName(idx);
// Id column is must be an integer
switch (colName) {
case "_id":
entry.id = c.getInt(idx);
entry.put(colName, c.getInt(idx));
break;
case "type":
entry.type = c.getInt(idx);
entry.put(colName, c.getInt(idx));
return c.getInt(idx);
/* For debug purpose
case "length(address)":
@ -181,22 +188,19 @@ public class AndroidSmsFetcher {
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);
entry.put(colName, (c.getInt(idx) > 0) ? "true" : "false");
break;
case "date":
entry.date = c.getLong(idx);
break;
case "address":
entry.address = c.getString(idx);
break;
case "body":
entry.body = c.getString(idx);
// Special case for date, we need to record last without searching
Long tmpDate = c.getLong(idx);
if (tmpDate > _lastMsgDate) {
_lastMsgDate = tmpDate;
}
entry.put(colName, c.getString(idx));
break;
default:
// Unhandled column
entry.put(colName, c.getString(idx));
break;
}
@ -245,10 +249,16 @@ public class AndroidSmsFetcher {
_existingDraftsMessages = draftMessages;
}
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 = AndroidSmsFetcher.class.getSimpleName();
}

View File

@ -0,0 +1,93 @@
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.net.Uri;
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.HttpMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import java.io.IOException;
class HTTPRequestBuilder {
private final OwnCloudClient _ocClient;
// API v1 calls
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_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";
// API v2 calls
private static final String OC_V2_GET_PHONELIST = "/index.php/apps/ocsms/api/v2/phones/list?format=json";
private static final String OC_V2_GET_MESSAGES ="/index.php/apps/ocsms/api/v2/messages/[START]/[LIMIT]?format=json";
private static final String OC_V2_GET_MESSAGES_PHONE ="/index.php/apps/ocsms/api/v2/messages/[PHONENUMBER]/[START]/[LIMIT]?format=json";
private static final String OC_V2_GET_MESSAGES_SENDQUEUE = "/index.php/apps/ocsms/api/v2/messages/sendqueue?format=json";
HTTPRequestBuilder(Context context, Uri serverURI, String accountName, String accountPassword) {
_ocClient = OwnCloudClientFactory.createOwnCloudClient(
serverURI, context, true);
// Set basic credentials
_ocClient.setCredentials(
OwnCloudCredentialsFactory.newBasicCredentials(accountName, accountPassword)
);
}
private GetMethod get(String oc_call) {
GetMethod get = new GetMethod(_ocClient.getBaseUri() + oc_call);
get.addRequestHeader("OCS-APIREQUEST", "true");
return get;
}
GetMethod getAllSmsIds() {
return get(HTTPRequestBuilder.OC_GET_ALL_SMS_IDS);
}
public GetMethod getVersion() {
return get(HTTPRequestBuilder.OC_GET_VERSION);
}
PostMethod pushSms(StringRequestEntity ent) {
PostMethod post = new PostMethod(_ocClient.getBaseUri() + HTTPRequestBuilder.OC_PUSH_ROUTE);
post.addRequestHeader("OCS-APIREQUEST", "true");
post.setRequestEntity(ent);
return post;
}
GetMethod getPhoneList() {
return get(HTTPRequestBuilder.OC_V2_GET_PHONELIST);
}
GetMethod getMessages(Long start, Integer limit) {
return get(HTTPRequestBuilder.OC_V2_GET_MESSAGES.
replace("[START]", start.toString()).replace("[LIMIT]", limit.toString()));
}
int execute(HttpMethod req) throws IOException {
return _ocClient.executeMethod(req);
}
}

View File

@ -1,114 +0,0 @@
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

@ -20,21 +20,26 @@ package fr.unix_experience.owncloud_sms.engine;
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 java.net.MalformedURLException;
import java.net.URL;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
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 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 {
@ -51,104 +56,327 @@ public class OCSMSOwnCloudClient {
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));
}
Uri serverURI = Uri.parse(ocURI);
_http = new HTTPRequestBuilder(context, serverURI,
accountManager.getUserData(account, "ocLogin"),
accountManager.getPassword(account));
_connectivityMonitor = new ConnectivityMonitor(_context);
}
public Integer getServerAPIVersion() throws OCSyncException {
Pair<Integer, Integer> vPair = _http.getVersion();
_serverAPIVersion = vPair.second;
if (vPair.first == 200 && _serverAPIVersion > 0) {
return _serverAPIVersion;
doHttpRequest(_http.getVersion(), true);
if (_jsonQueryBuffer == null) {
// Return default version
return 1;
}
return 0;
try {
_serverAPIVersion = _jsonQueryBuffer.getInt("version");
}
catch (JSONException e) {
Log.e(OCSMSOwnCloudClient.TAG, "No version received from server, assuming version 1", e);
_serverAPIVersion = 1;
}
return _serverAPIVersion;
}
SmsPhoneListResponse getServerPhoneNumbers() throws OCSyncException {
Pair<Integer, SmsPhoneListResponse> response = _http.getPhoneList();
if (response.second == null || response.first != 200) {
JSONArray getServerPhoneNumbers() throws OCSyncException {
doHttpRequest(_http.getPhoneList(), true);
if (_jsonQueryBuffer == null) {
return null;
}
return response.second;
try {
return _jsonQueryBuffer.getJSONArray("phoneList");
} catch (JSONException e) {
Log.e(OCSMSOwnCloudClient.TAG, "No phonelist received from server, empty it", e);
return null;
}
}
public void doPushRequest(SmsBuffer smsBuffer) throws OCSyncException {
/*
public void doPushRequest(JSONArray smsList) throws OCSyncException {
/**
* If we need other API push, set it here
*/
switch (_serverAPIVersion) {
case 1:
default: doPushRequestV1(smsBuffer); break;
default: doPushRequestV1(smsList); break;
}
}
private void doPushRequestV1(SmsBuffer smsBuffer) throws OCSyncException {
if (smsBuffer == null) {
Pair<Integer, SmsIDListResponse> response = _http.getAllSmsIds();
if (response.second == null) {
private 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) {
doHttpRequest(_http.getAllSmsIds());
if (_jsonQueryBuffer == null) {
return;
}
// Create new SmsBuffer to get results
smsBuffer = new SmsBuffer();
JSONObject smsBoxes = new JSONObject();
JSONArray inboxSmsList = null, sentSmsList = null, draftsSmsList = null;
try {
smsBoxes = _jsonQueryBuffer.getJSONObject("smslist");
} catch (JSONException e) {
try {
_jsonQueryBuffer.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.i(OCSMSOwnCloudClient.TAG, "No inbox Sms received from server (doPushRequest, get SMS list)");
}
try {
sentSmsList = smsBoxes.getJSONArray("sent");
} catch (JSONException e) {
Log.i(OCSMSOwnCloudClient.TAG, "No sent Sms received from server (doPushRequest, get SMS list)");
}
try {
draftsSmsList = smsBoxes.getJSONArray("drafts");
} catch (JSONException e) {
Log.i(OCSMSOwnCloudClient.TAG, "No drafts Sms received from server (doPushRequest, get SMS list)");
}
AndroidSmsFetcher fetcher = new AndroidSmsFetcher(_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();
}
if (smsBuffer.empty()) {
if (smsList.length() == 0) {
Log.i(OCSMSOwnCloudClient.TAG, "No new SMS to sync, sync done");
return;
}
Pair<Integer, SmsPushResponse> response = _http.pushSms(smsBuffer);
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);
}
if (response.second == null) {
Log.e(OCSMSOwnCloudClient.TAG,"Push request failed. GoLang response is empty.");
doHttpRequest(post);
if (_jsonQueryBuffer == null) {
Log.e(OCSMSOwnCloudClient.TAG,"Request failed. It doesn't return a valid JSON Object");
throw new OCSyncException(R.string.err_sync_push_request, OCSyncErrorType.IO);
}
// Push was OK, we can save the lastMessageDate which was saved to server
(new OCSMSSharedPrefs(_context)).setLastMessageDate(smsBuffer.getLastMessageDate());
Boolean pushStatus;
String pushMessage;
try {
pushStatus = _jsonQueryBuffer.getBoolean("status");
pushMessage = _jsonQueryBuffer.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);
}
Log.i(OCSMSOwnCloudClient.TAG, "SMS Push request said: status " +
response.second.getStatus() + " - " + response.second.getMessage());
Log.i(OCSMSOwnCloudClient.TAG, "LastMessageDate set to: " + smsBuffer.getLastMessageDate());
// Push was OK, we can save the lastMessageDate which was saved to server
(new OCSMSSharedPrefs(_context)).setLastMessageDate(lastMsgDate);
Log.i(OCSMSOwnCloudClient.TAG, "SMS Push request said: status " + pushStatus + " - " + pushMessage);
}
SmsMessagesResponse retrieveSomeMessages(Long start, Integer limit) {
private PostMethod createPushRequest(JSONArray smsList) throws OCSyncException {
JSONObject obj = createPushJSONObject(smsList);
if (obj == null) {
return null;
}
StringRequestEntity ent = createJSONRequestEntity(obj);
if (ent == null) {
return null;
}
return _http.pushSms(ent);
}
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);
}
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;
}
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;
}
JSONObject 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;
}
Pair<Integer, SmsMessagesResponse> response;
try {
response = _http.getMessages(start, limit);
doHttpRequest(_http.getMessages(start, limit));
} catch (OCSyncException e) {
_jsonQueryBuffer = null;
Log.e(OCSMSOwnCloudClient.TAG, "Request failed.");
return null;
}
if (response.second == null) {
if (!_jsonQueryBuffer.has("messages") || !_jsonQueryBuffer.has("last_id")) {
Log.e(OCSMSOwnCloudClient.TAG,
"Invalid response received from server, either messages or last_id field is missing.");
return null;
}
return response.second;
return _jsonQueryBuffer;
}
private final OCHttpClient _http;
private void doHttpRequest(HttpMethod req) throws OCSyncException {
doHttpRequest(req, false);
}
// skipError permit to skip invalid JSON datas
private void doHttpRequest(HttpMethod req, Boolean skipError) throws OCSyncException {
// Reinit the queryBuffer
_jsonQueryBuffer = null;
int status = 0;
// We try maximumHttpReqTries because sometimes network is slow or unstable
int tryNb = 0;
while (tryNb < OCSMSOwnCloudClient.maximumHttpReqTries) {
tryNb++;
if (!_connectivityMonitor.isValid()) {
if (tryNb == OCSMSOwnCloudClient.maximumHttpReqTries) {
req.releaseConnection();
throw new OCSyncException(R.string.err_sync_no_connection_available, OCSyncErrorType.IO);
}
continue;
}
try {
status = _http.execute(req);
Log.i(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);
}
// Parse the response
try {
_jsonQueryBuffer = 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);
}
}
}
} 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);
}
}
private static final int maximumHttpReqTries = 3;
private final HTTPRequestBuilder _http;
private final Context _context;
private final ConnectivityMonitor _connectivityMonitor;
private Integer _serverAPIVersion;
private JSONObject _jsonQueryBuffer;
private static final String TAG = OCSMSOwnCloudClient.class.getSimpleName();

View File

@ -1,29 +0,0 @@
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,48 +0,0 @@
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,23 +18,8 @@ package fr.unix_experience.owncloud_sms.enums;
*/
public enum OCSMSNotificationType {
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;
}
SYNC,
SYNC_FAILED,
DEBUG,
PERMISSION,
}

View File

@ -1,33 +1,19 @@
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.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
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
@ -42,92 +28,80 @@ public class OCSMSNotificationUI {
*/
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)
* @see #cancel(Context)
*/
public static void notify(Context context, String titleString, String contentString,
String channelId, int notificationId) {
public static void notify(Context context, String titleString,
String contentString, int number) {
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);
Bitmap picture = BitmapFactory.decodeResource(res, R.drawable.ic_launcher);
// String ticker = (titleString.length() > 20) ? titleString.substring(0, 20) : titleString;
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)
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
// Set appropriate defaults for the notification light, sound,
// and vibration.
.setSmallIcon(R.drawable.notification_icon)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(title)
.setContentText(contentString)
// All fields below this line are optional.
// All fields below this line are optional.
// Use a default priority (recognized on devices running Android
// 4.1 or later)
// 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.
// Provide a large icon, shown with the notification in the
// notification drawer on devices running Android 3.0 or later.
.setLargeIcon(picture)
// Set ticker text (preview) information for this notification.
//.setTicker(ticker)
// Show a number. This is useful when stacking notifications of
// a single type.
.setNumber(number)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(contentString)
.setBigContentTitle(title)
.setSummaryText(titleString))
.setAutoCancel(true)
.setColor(context.getResources().getColor(R.color.oc_primary));
.setAutoCancel(true);
notify(context, builder.build(), notificationId);
OCSMSNotificationUI.notify(context, builder.build());
}
private static void notify(Context context, Notification notification, int notificationId) {
@TargetApi(Build.VERSION_CODES.ECLAIR)
private static void notify(Context context, Notification notification) {
NotificationManager nm = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
createNotificationChannels(context, nm);
nm.notify(OCSMSNotificationUI.NOTIFICATION_TAG, notificationId, notification);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) {
nm.notify(OCSMSNotificationUI.NOTIFICATION_TAG, 0, notification);
} else {
nm.notify(OCSMSNotificationUI.NOTIFICATION_TAG.hashCode(), notification);
}
}
/**
* Cancels any notifications of this type previously shown using
* {@link #notify(Context, String, String, OCSMSNotificationType)}.
* {@link #notify(Context, String, int)}.
*/
public static void cancel(Context context, OCSMSNotificationType type) {
cancel(context, type.getNotificationId());
}
public static void cancel(Context context, int notificationId) {
@TargetApi(Build.VERSION_CODES.ECLAIR)
public static void cancel(Context context) {
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);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) {
nm.cancel(OCSMSNotificationUI.NOTIFICATION_TAG, 0);
} else {
nm.cancel(OCSMSNotificationUI.NOTIFICATION_TAG.hashCode());
}
}
}

View File

@ -25,6 +25,8 @@ 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;
@ -33,7 +35,6 @@ import fr.unix_experience.owncloud_sms.engine.OCSMSOwnCloudClient;
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 {
@ -59,13 +60,13 @@ public class SmsObserver extends ContentObserver implements ASyncSMSSync {
}
AndroidSmsFetcher fetcher = new AndroidSmsFetcher(_context);
SmsBuffer smsBuffer = fetcher.getLastMessage(MailboxID.ALL);
JSONArray smsList = fetcher.getLastMessage(MailboxID.ALL);
ConnectivityMonitor cMon = new ConnectivityMonitor(_context);
// Synchronize if network is valid and there are SMS
if (cMon.isValid() && (smsBuffer != null)) {
new SyncTask(_context, smsBuffer).execute();
if (cMon.isValid() && (smsList != null)) {
new SyncTask(_context, smsList).execute();
}
}

View File

@ -43,10 +43,6 @@ 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);
}

View File

@ -67,7 +67,7 @@ public class PermissionChecker {
// 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);
OCSMSNotificationType.PERMISSION.ordinal());
return false;
}

View File

@ -1,47 +0,0 @@
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

@ -29,17 +29,6 @@ import fr.unix_experience.owncloud_sms.enums.MailboxID;
import fr.unix_experience.owncloud_sms.prefs.OCSMSSharedPrefs;
public class SmsDataProvider extends ContentProvider {
static String[] messageFields = {
"read",
"date",
"address",
"seen",
"body",
"_id",
"type",
//"length(address)" // For debug purposes
};
// WARNING: mandatory
public SmsDataProvider() {}
public SmsDataProvider (Context ct) {
@ -54,7 +43,7 @@ public class SmsDataProvider extends ContentProvider {
public Cursor query(String mailBox) {
return query(Uri.parse(mailBox),
SmsDataProvider.messageFields,
new String[] { "read", "date", "address", "seen", "body", "_id", "type", },
null, null, null
);
}
@ -63,7 +52,16 @@ public class SmsDataProvider extends ContentProvider {
public Cursor queryNonExistingMessages(String mailBox, String existingIds) {
Log.i(SmsDataProvider.TAG, "queryNonExistingMessages !");
if (!existingIds.isEmpty()) {
return query(Uri.parse(mailBox), SmsDataProvider.messageFields,
return query(Uri.parse(mailBox),
new String[] {
"read",
"date",
"address",
"seen",
"body",
"_id",
"type",
},
"_id NOT IN (" + existingIds + ")", null, null
);
}
@ -72,8 +70,18 @@ public class SmsDataProvider extends ContentProvider {
}
public Cursor queryMessagesSinceDate(String mailBox, Long sinceDate) {
return query(Uri.parse(mailBox), SmsDataProvider.messageFields,
"date > ?", new String[] { sinceDate.toString() }, "date ASC"
return query(Uri.parse(mailBox),
new String[] {
"read",
"date",
"address",
"seen",
"body",
"_id",
"type",
//"length(address)" // For debug purposes
},
"date > ?", new String[] { sinceDate.toString() }, null
);
}

View File

@ -31,7 +31,6 @@ 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.OCSMSNotificationUI;
import fr.unix_experience.owncloud_sms.prefs.OCSMSSharedPrefs;
class SmsSyncAdapter extends AbstractThreadedSyncAdapter {
@ -42,11 +41,8 @@ class SmsSyncAdapter extends AbstractThreadedSyncAdapter {
@Override
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {
if (new OCSMSSharedPrefs(getContext()).showSyncNotifications()) {
OCSMSNotificationUI.notify(getContext(), getContext().getString(R.string.sync_title),
getContext().getString(R.string.sync_inprogress), OCSMSNotificationType.SYNC);
}
OCSMSNotificationUI.notify(getContext(), getContext().getString(R.string.sync_title),
getContext().getString(R.string.sync_inprogress), OCSMSNotificationType.SYNC.ordinal());
try {
OCSMSOwnCloudClient _client = new OCSMSOwnCloudClient(getContext(), account);
@ -56,13 +52,14 @@ class SmsSyncAdapter extends AbstractThreadedSyncAdapter {
// and push datas
_client.doPushRequest(null);
OCSMSNotificationUI.cancel(getContext(), OCSMSNotificationType.SYNC_FAILED);
OCSMSNotificationUI.cancel(getContext());
} catch (IllegalStateException e) {
OCSMSNotificationUI.notify(getContext(), getContext().getString(R.string.fatal_error),
e.getMessage(), OCSMSNotificationType.SYNC_FAILED);
e.getMessage(), OCSMSNotificationType.SYNC_FAILED.ordinal());
} catch (OCSyncException e) {
OCSMSNotificationUI.cancel(getContext());
OCSMSNotificationUI.notify(getContext(), getContext().getString(R.string.fatal_error),
getContext().getString(e.getErrorId()), OCSMSNotificationType.SYNC_FAILED);
getContext().getString(e.getErrorId()), OCSMSNotificationType.SYNC_FAILED.ordinal());
if (e.getErrorType() == OCSyncErrorType.IO) {
syncResult.stats.numIoExceptions++;
}
@ -75,8 +72,6 @@ class SmsSyncAdapter extends AbstractThreadedSyncAdapter {
else {
Log.w(SmsSyncAdapter.TAG, "onPerformSync: unhandled response");
}
} finally {
OCSMSNotificationUI.cancel(getContext(), OCSMSNotificationType.SYNC);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 657 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 558 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 748 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1004 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 873 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 646 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 446 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 677 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 686 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 900 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 604 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 910 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,28 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:autoMirrored="true"
android:viewportWidth="1344"
android:viewportHeight="1344">
<path
android:fillType="evenOdd"
android:pathData="M0,0h1344v1344h-1344z"
android:strokeLineJoin="round">
<aapt:attr name="android:fillColor">
<gradient
android:endX="1344"
android:endY="1.2959057E-4"
android:startX="163.34073"
android:startY="1344"
android:type="linear">
<item
android:color="#FF0082C9"
android:offset="0" />
<item
android:color="#FF1CAFFF"
android:offset="1" />
</gradient>
</aapt:attr>
</path>
</vector>

View File

@ -1,22 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="816.5869"
android:viewportHeight="816.5869">
<group android:translateX="20.414673"
android:translateY="20.414673">
<group android:translateX="131.87878"
android:translateY="131.87878">
<path
android:pathData="m122.444,89.125c-18.459,0 -33.319,14.115 -33.319,31.648l0,143.74c0,17.533 14.86,31.648 33.319,31.648l38.249,0 152.528,126.434 -49.393,-126.434 125.728,0c18.459,0 33.319,-14.115 33.319,-31.648l0,-143.74c0,-17.533 -14.86,-31.648 -33.319,-31.648zM131.499,116.333 L380.501,116.333c9.089,0 16.405,5.854 16.405,13.126l0,8.539c0,7.271 -7.317,13.124 -16.405,13.124l-249.003,0c-9.089,0 -16.406,-5.852 -16.406,-13.124l0,-8.539c0,-7.271 7.317,-13.126 16.406,-13.126zM131.499,176.841 L380.501,176.841c9.089,0 16.405,5.854 16.405,13.126l0,8.537c0,7.271 -7.317,13.126 -16.405,13.126l-249.003,0c-9.089,0 -16.406,-5.854 -16.406,-13.126l0,-8.537c0,-7.271 7.317,-13.126 16.406,-13.126zM131.499,236.851 L221.453,236.851c9.089,0 16.405,7.988 16.405,15.259l0,6.404c0,7.271 -7.317,13.126 -16.405,13.126l-89.954,0c-9.089,0 -16.406,-5.854 -16.406,-13.126l0,-6.404c0,-7.271 7.317,-15.259 16.406,-15.259z"
android:strokeAlpha="0"
android:strokeLineJoin="miter"
android:strokeWidth="0.25007507"
android:fillColor="#ffffff"
android:strokeColor="#000000"
android:fillType="evenOdd"
android:fillAlpha="1"
android:strokeLineCap="butt"/>
</group>
</group>
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -61,9 +61,7 @@
android:id="@+id/ocsms_logo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="24dp"
android:paddingBottom="36dp"
android:src="@drawable/logo"
android:src="@drawable/login_logo"
android:contentDescription="@string/login_logo" />
<LinearLayout
@ -75,9 +73,8 @@
android:id="@+id/oc_protocol"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:popupBackground="@color/oc_primary"
android:entries="@array/protocol_array" />
android:entries="@array/protocol_array">
</Spinner>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
@ -87,7 +84,6 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="@string/prompt_serverURI"
android:textColor="@color/oc_white"
android:inputType="textUri" >
<requestFocus />
</EditText>
@ -103,9 +99,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/prompt_login"
android:textColor="@color/oc_white"
android:inputType="textEmailAddress"
android:maxLines="1" />
android:maxLines="1"
android:singleLine="true" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
@ -121,7 +117,7 @@
android:imeOptions="actionUnspecified"
android:inputType="textPassword"
android:maxLines="1"
android:textColor="@color/oc_white" />
android:singleLine="true" />
</android.support.design.widget.TextInputLayout>
<com.dd.processbutton.iml.ActionProcessButton

View File

@ -34,14 +34,9 @@
android:fitsSystemWindows="true"
tools:openDrawer="start"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
layout="@layout/toolbar" />
android:layout_height="match_parent">
<TextView
android:id="@+id/main_title_welcome"
@ -49,6 +44,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/ma_title_welcome"
android:gravity="top"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
@ -57,16 +53,33 @@
android:paddingRight="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_below="@+id/main_title_welcome"
android:text="@string/ma_content_welcome"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
<TextView
android:id="@+id/main_tv_swipe"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_below="@+id/main_tv_welcome"
android:text="@string/ma_content_swipeaction"
android:textAppearance="?android:attr/textAppearanceMedium" />
</RelativeLayout>
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_main"
app:menu="@menu/activity_main_drawer"
android:fitsSystemWindows="true"/>
app:menu="@menu/activity_main_drawer"/>
</android.support.v4.widget.DrawerLayout>

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