1
0
mirror of https://github.com/nerzhul/ownCloud-SMS-App.git synced 2025-06-12 10:26:20 +00:00

Compare commits

..

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

292 changed files with 2851 additions and 19250 deletions

4
.gitignore vendored
View File

@ -3,7 +3,3 @@ ownCloudSMS.iml
/build/
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,65 @@
# 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
Android download link: https://play.google.com/store/apps/details?id=fr.unix_experience.owncloud_sms
[![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
## Issue template
Server
- ownCloud version: X.X.X
- PHP version: X.X
- HTTPd server: <apache|nginx...>
- HTTPS: <yes|no>
Client
- Android version: X.X.X
- Phone: <phone-model>
- ownCloud SMS app version: X.X.X
Please create your issues for the client here:
https://github.com/nerzhul/ownCloud-SMS-App/issues
And for the server app here:
https://github.com/nerzhul/ocsms/issues
## Developpers
You can found our continuous integration here: http://jenkins.unix-experience.fr/job/ownCloud%20SMS%20%28Android%29/
### 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,44 +1,14 @@
buildscript {
repositories {
mavenCentral()
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
}
}
allprojects {
repositories {
jcenter()
google()
}
}
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
buildToolsVersion "28.0.3"
lintOptions {
abortOnError false
}
compileSdkVersion 23
buildToolsVersion "22.0.1"
useLibrary 'org.apache.http.legacy'
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'
}
minSdkVersion 14
targetSdkVersion 23
}
buildTypes {
@ -47,25 +17,20 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
packagingOptions {
exclude 'META-INF/LICENSE.txt'
}
}
repositories {
maven {
url "https://jitpack.io"
url 'https://oss.sonatype.org/content/repositories/snapshots'
}
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 project(':nrzandroidlib')
compile 'com.android.support:support-v13:23.+'
compile 'com.android.support:appcompat-v7:23.+'
compile 'com.android.support:design:23.+'
compile 'in.srain.cube:ultra-ptr:1.0.11'
compile 'com.github.dmytrodanylyk.android-process-button:library:1.0.4'
}

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

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

Binary file not shown.

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,15 +1,14 @@
<?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" />
package="fr.unix_experience.owncloud_sms"
android:versionCode="33"
android:versionName="0.20.0"> <!-- From Android 4.0 to 6.0 -->
<uses-sdk
android:maxSdkVersion="23"
android:minSdkVersion="14"
android:targetSdkVersion="23" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS" />
<!-- For SMS Restore & Sending -->
<uses-permission android:name="android.permission.SEND_SMS" />
<!-- For SMS Broadcaster -->
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.INTERNET" />
@ -30,21 +29,21 @@
<!-- For backup restauration -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/OcSmsTheme">
android:theme="@style/OcSmsTheme" >
<!-- Related to periodic sync -->
<service
android:name=".observers.SmsObserverService"
android:exported="false" />
<service
android:name=".sync_adapters.SmsSyncService"
android:exported="true"
android:process=":sync">
android:process=":sync" >
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
@ -57,10 +56,11 @@
<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">
<service android:name=".authenticators.OwnCloudAuthenticatorService" >
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
@ -70,60 +70,12 @@
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" >
<intent-filter>
<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">
<intent-filter>
<action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
<data android:mimeType="application/vnd.wap.mms-message" />
</intent-filter>
</receiver>
<!-- Activity that allows the user to send new SMS/MMS messages Note: useless class, used only for restoring SMS -->
<activity android:name=".misc.ComposeSmsActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.SENDTO" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="sms" />
<data android:scheme="smsto" />
<data android:scheme="mms" />
<data android:scheme="mmsto" />
</intent-filter>
</activity>
<!-- Service that delivers messages from the phone "quick response" Note: useless class, used only for restoring SMS -->
<service
android:name=".misc.HeadlessSmsSendService"
android:exported="true"
android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE">
<intent-filter>
<action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="sms" />
<data android:scheme="smsto" />
<data android:scheme="mms" />
<data android:scheme="mmsto" />
</intent-filter>
</service>
<receiver android:name=".broadcast_receivers.ConnectivityChanged">
<receiver android:name=".broadcast_receivers.ConnectivityChanged" >
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
@ -131,18 +83,19 @@
<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:name=".activities.GeneralSettingsActivity"
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" />
@ -151,17 +104,8 @@
</activity>
<activity
android:name=".activities.remote_account.ContactListActivity"
android:label="@string/title_activity_select_contact"></activity>
<activity
android:name=".activities.remote_account.AccountActionsActivity"
android:label="@string/account_actions"></activity>
<activity
android:name=".activities.remote_account.RestoreMessagesActivity"
android:label="@string/restore_all_messages"></activity>
<activity
android:name=".activities.PrivacyPolicyActivity"
android:label="@string/action_appinfo_privacy_policy"
android:theme="@style/OcSmsTheme.NoActionBar"></activity>
android:label="@string/title_activity_select_contact" >
</activity>
</application>
</manifest>
</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

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

View File

@ -24,17 +24,14 @@ 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 +40,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 +64,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 +73,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 +92,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 +104,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 +126,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 +145,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 +162,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 +228,109 @@ 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) {
_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;
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 OK:
break;
case INVALID_ADDR:
_serverView.setError(getString(R.string.error_invalid_server_address));
_serverView.requestFocus();
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));
_serverView.requestFocus();
break;
case 401:
case 403:
case CONN_FAILED:
_serverView.setError(getString(R.string.error_connection_failed));
_serverView.requestFocus();
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 200:
default:
serverViewRequestFocus = false;
break;
case UNKNOWN_ERROR:
_serverView.setError("UNK");
_serverView.requestFocus();
break;
default:
break;
}
if (serverViewRequestFocus) {
_serverView.requestFocus();
}
// If not ok, reset the progress
if (_returnCode != 200) {
_signInButton.setProgress(0);
}
// If not ok, reset the progress
if (_returnCode != LoginReturnCode.OK) {
_signInButton.setProgress(0);
}
}
}
@ -374,14 +339,12 @@ 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";
}
}

View File

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

View File

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

View File

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

View File

@ -3,17 +3,17 @@ 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.nrz.androidlib.adapters.AndroidAccountAdapter;
import fr.unix_experience.android_lib.AppCompatListActivity;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.adapters.AndroidAccountAdapter;
public class AccountListActivity extends AppCompatListActivity {
ArrayList<Account> listItems = new ArrayList<>();
AndroidAccountAdapter adapter;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
@ -22,35 +22,19 @@ public class AccountListActivity extends AppCompatListActivity {
setContentView(R.layout.restore_activity_accountlist);
getSupportActionBar().setHomeButtonEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
ArrayList<Account> itemList = new ArrayList<>();
AndroidAccountAdapter adapter = new AndroidAccountAdapter(this,
adapter = new AndroidAccountAdapter(this,
android.R.layout.simple_list_item_1,
itemList,
listItems,
R.layout.account_list_item,
R.id.accountname, AccountActionsActivity.class);
R.id.accountname, ContactListActivity.class);
setListAdapter(adapter);
Account[] accountList =
_accountMgr.getAccountsByType(getString(R.string.account_type));
Collections.addAll(itemList, accountList);
Collections.addAll(listItems, accountList);
adapter.notifyDataSetChanged();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
break;
default:
return super.onOptionsItemSelected(item);
}
return true;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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,20 +30,20 @@ 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());
v.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i(TAG, "Clicked on phone " + l);
Log.d(TAG, "Clicked on phone " + l);
}
});
v.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
Log.i(TAG, "Long clicked on phone " + l);
Log.d(TAG, "Long clicked on phone " + l);
return false;
}
});

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,91 @@ public class OwnCloudAuthenticator extends AbstractAccountAuthenticator {
// TODO Auto-generated method stub
return null;
}
/*
* Return codes
* 1: invalid address
* 2: HTTP failed
* 3: connexion failed
* 4: invalid login
* 5: unknown error
*/
public LoginReturnCode testCredentials() {
LoginReturnCode bRet = LoginReturnCode.OK;
GetMethod get;
int status;
try {
get = new GetMethod(_client.getBaseUri() + "/index.php/ocs/cloud/user?format=json");
} catch (IllegalArgumentException e) {
return LoginReturnCode.INVALID_ADDR;
}
get.addRequestHeader("OCS-APIREQUEST", "true");
try {
status = _client.executeMethod(get);
} catch (IllegalArgumentException e) {
return LoginReturnCode.INVALID_ADDR;
} catch (HttpException e) {
return LoginReturnCode.HTTP_CONN_FAILED;
} catch (IOException e) {
return LoginReturnCode.CONN_FAILED;
}
try {
if(isSuccess(status)) {
String response = get.getResponseBodyAsString();
Log.d(OwnCloudAuthenticator.TAG, "Successful response: " + response);
// Parse the response
JSONObject respJSON = new JSONObject(response);
JSONObject respOCS = respJSON.getJSONObject(OwnCloudAuthenticator.NODE_OCS);
JSONObject respData = respOCS.getJSONObject(OwnCloudAuthenticator.NODE_DATA);
String id = respData.getString(OwnCloudAuthenticator.NODE_ID);
String displayName = respData.getString(OwnCloudAuthenticator.NODE_DISPLAY_NAME);
String email = respData.getString(OwnCloudAuthenticator.NODE_EMAIL);
Log.d(OwnCloudAuthenticator.TAG, "*** Parsed user information: " + id + " - " + displayName + " - " + email);
} else {
String response = get.getResponseBodyAsString();
Log.e(OwnCloudAuthenticator.TAG, "Failed response while getting user information ");
if (response != null) {
Log.e(OwnCloudAuthenticator.TAG, "*** status code: " + status + " ; response message: " + response);
} else {
Log.e(OwnCloudAuthenticator.TAG, "*** status code: " + status);
}
bRet = (status == 401) ? LoginReturnCode.INVALID_LOGIN : LoginReturnCode.UNKNOWN_ERROR;
}
} catch (Exception e) {
Log.e(OwnCloudAuthenticator.TAG, "Exception while getting OC user information", e);
bRet = LoginReturnCode.UNKNOWN_ERROR;
} finally {
get.releaseConnection();
}
return bRet;
}
private boolean isSuccess(int status) {
return (status == HttpStatus.SC_OK);
}
public void setClient(OwnCloudClient oc) {
_client = oc;
}
private final Context _context;
private OwnCloudClient _client;
private static final String TAG = OwnCloudAuthenticator.class.getSimpleName();
private static final String NODE_OCS = "ocs";
private static final String NODE_DATA = "data";
private static final String NODE_ID = "id";
private static final String NODE_DISPLAY_NAME= "display-name";
private static final String NODE_EMAIL= "email";
}

View File

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

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

View File

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

View File

@ -5,6 +5,7 @@ import android.accounts.AccountManager;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.ContactsContract;
import android.support.v4.widget.SwipeRefreshLayout;
@ -12,13 +13,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> {
@ -48,14 +51,19 @@ public interface ASyncContactLoad {
}
@Override
protected Boolean doInBackground(Void... params) {
OCSMSOwnCloudClient _client = null;
try {
_client = new OCSMSOwnCloudClient(_context, ContactLoadTask._account);
}
catch (IllegalStateException e) {
// Create client
String ocURI = ContactLoadTask._accountMgr.getUserData(ContactLoadTask._account, "ocURI");
if (ocURI == null) {
// @TODO: Handle the problem
return false;
}
Uri serverURI = Uri.parse(ocURI);
OCSMSOwnCloudClient _client = new OCSMSOwnCloudClient(_context,
serverURI, ContactLoadTask._accountMgr.getUserData(ContactLoadTask._account, "ocLogin"),
ContactLoadTask._accountMgr.getPassword(ContactLoadTask._account));
// Remove all objects, due to refreshing handling
_objects.clear();
try {
@ -66,24 +74,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 +134,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

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

View File

@ -1,7 +1,7 @@
package fr.unix_experience.owncloud_sms.engine;
/*
* Copyright (c) 2014-2016, 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
@ -19,121 +19,53 @@ package fr.unix_experience.owncloud_sms.engine;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Context;
import android.net.Uri;
import android.os.AsyncTask;
import android.util.Log;
import 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;
import fr.unix_experience.owncloud_sms.notifications.OCSMSNotificationManager;
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));
// Notify that we are syncing SMS
OCSMSNotificationManager nMgr = new OCSMSNotificationManager(_context);
for (Account element : myAccountList) {
Uri serverURI = Uri.parse(_accountMgr.getUserData(element, "ocURI"));
OCSMSOwnCloudClient _client = new OCSMSOwnCloudClient(_context,
serverURI, _accountMgr.getUserData(element, "ocLogin"),
_accountMgr.getPassword(element));
try {
OCSMSOwnCloudClient _client = new OCSMSOwnCloudClient(_context, element);
// Fetch API version first to do some early verifications
Log.i(ASyncSMSSync.TAG, "Server API version: " + _client.getServerAPIVersion());
_client.doPushRequest(smsBuffer);
OCSMSNotificationUI.cancel(_context, OCSMSNotificationType.SYNC_FAILED);
} catch (IllegalStateException e) { // Fail to read account data
OCSMSNotificationUI.notify(_context, _context.getString(R.string.fatal_error),
e.getMessage(), OCSMSNotificationType.SYNC_FAILED);
_client.doPushRequest(_smsList);
nMgr.dropSyncErrorMsg();
} 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);
nMgr.setSyncErrorMsg(_context.getString(e.getErrorId()));
}
}
smsBuffer.clear();
nMgr.dropSyncProcessMsg();
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

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

View File

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

View File

@ -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

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

View File

@ -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

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

View File

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

View File

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

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,7 @@ 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,
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

@ -19,7 +19,7 @@ package fr.unix_experience.owncloud_sms.prefs;
import android.content.Context;
import android.content.SharedPreferences;
import fr.nrz.androidlib.common.SharedPrefs;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.defines.DefaultPrefs;
@ -43,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);
}
@ -74,6 +70,4 @@ public class OCSMSSharedPrefs extends SharedPrefs {
public Integer getSyncBulkLimit() {
return _sPrefs.getInt("sync_bulk_messages", -1);
}
public Integer getMinPhoneNumberCharsToSync() { return _sPrefs.getInt("minimum_sync_chars", DefaultPrefs.minimumCharsForSync); }
}

View File

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

View File

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

View File

@ -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

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

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
package fr.unix_experience.owncloud_sms.sync_adapters;
/*
* Copyright (c) 2017, Andy Scherzinger
* Copyright (c) 2014-2015, Loic Blot <loic.blot@unix-experience.fr>
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
@ -12,7 +11,7 @@
* 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
@ -25,29 +24,45 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
-->
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways|snap"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
<ProgressBar
android:id="@+id/login_progress"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:visibility="gone" />
</android.support.design.widget.AppBarLayout>
public class SmsSlowSyncService extends Service {
// Storage for an instance of the sync adapter
private static SmsSyncAdapter _adapter = null;
// Object to use as a thread-safe lock
private static final Object sSyncAdapterLock = new Object();
/*
* Instantiate the sync adapter object.
*/
@Override
public void onCreate() {
/*
* Create the sync adapter as a singleton.
* Set the sync adapter as syncable
* Disallow parallel syncs
*/
synchronized (sSyncAdapterLock) {
if (_adapter == null) {
_adapter = new SmsSyncAdapter(getApplicationContext(), true);
}
}
}
/**
* Return an object that allows the system to invoke
* the sync adapter.
*
*/
@Override
public IBinder onBind(Intent intent) {
/*
* Get the object that allows external processes
* to call onPerformSync(). The object is created
* in the base class code when the SyncAdapter
* constructors call super()
*/
return _adapter.getSyncAdapterBinder();
}
}

View File

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

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

View File

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

View File

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

View File

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

Binary file not shown.

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

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