diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644
index 0000000..f2e74c7
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/fr/unix_experience/owncloud_sms/activities/GeneralSettingsActivity.java b/src/fr/unix_experience/owncloud_sms/activities/GeneralSettingsActivity.java
new file mode 100644
index 0000000..3f62a6b
--- /dev/null
+++ b/src/fr/unix_experience/owncloud_sms/activities/GeneralSettingsActivity.java
@@ -0,0 +1,208 @@
+package fr.unix_experience.owncloud_sms.activities;
+
+/*
+ * Copyright (c) 2014, Loic Blot
+ *
+ * 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 .
+ */
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.annotation.TargetApi;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.PeriodicSync;
+import android.content.res.Configuration;
+import android.os.Build;
+import android.os.Bundle;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceManager;
+import java.util.List;
+
+import fr.unix_experience.owncloud_sms.R;
+
+public class GeneralSettingsActivity extends PreferenceActivity {
+ private static final boolean ALWAYS_SIMPLE_PREFS = false;
+ static AccountManager mAccountMgr;
+ static String mAccountAuthority;
+ static String mAccountType;
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+
+ mAccountMgr = AccountManager.get(getBaseContext());
+ mAccountAuthority = getString(R.string.account_authority);
+ mAccountType = getString(R.string.account_type);
+ setupSimplePreferencesScreen();
+ }
+
+ /**
+ * Shows the simplified settings UI if the device configuration if the
+ * device configuration dictates that a simplified, single-pane UI should be
+ * shown.
+ */
+ private void setupSimplePreferencesScreen() {
+ if (!isSimplePreferences(this)) {
+ return;
+ }
+
+ // In the simplified UI, fragments are not used at all and we instead
+ // use the older PreferenceActivity APIs.
+ addPreferencesFromResource(R.xml.pref_data_sync);
+
+ // 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.
+ bindPreferenceSummaryToValue(findPreference("sync_frequency"));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean onIsMultiPane() {
+ return isXLargeTablet(this) && !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 {@link #ALWAYS_SIMPLE_PREFS}, or 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.
+ */
+ private static boolean isSimplePreferences(Context context) {
+ return ALWAYS_SIMPLE_PREFS
+ || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB
+ || !isXLargeTablet(context);
+ }
+
+ /**
+ * A preference value change listener that updates the preference's summary
+ * to reflect its new value.
+ */
+ private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object value) {
+ String stringValue = value.toString();
+
+ if (preference instanceof ListPreference) {
+ // For list preferences, look up the correct display value in
+ // the preference's 'entries' list.
+ ListPreference listPreference = (ListPreference) preference;
+ int index = listPreference.findIndexOfValue(stringValue);
+
+ // Set the summary to reflect the new value.
+ preference
+ .setSummary(index >= 0 ? listPreference.getEntries()[index]
+ : null);
+
+
+ String prefKey = preference.getKey();
+
+ // Handle sync frequency change
+ if (prefKey.equals(new String("sync_frequency"))) {
+ long syncFreq = Long.parseLong((String)value);
+
+ // Get ownCloud SMS account list
+ Account[] myAccountList = mAccountMgr.getAccountsByType(mAccountType);
+ for (int i = 0; i < myAccountList.length; i++) {
+ // And get all authorities for this account
+ List syncList = ContentResolver.getPeriodicSyncs(myAccountList[i], mAccountAuthority);
+
+ boolean foundSameSyncCycle = false;
+ for (int j = 0; j < syncList.size(); j++) {
+ PeriodicSync ps = syncList.get(i);
+
+ if (ps.period == syncFreq && ps.extras.getInt("synctype") == 1) {
+ foundSameSyncCycle = true;
+ }
+ }
+
+ if (foundSameSyncCycle == false) {
+ Bundle b = new Bundle();
+ b.putInt("synctype", 1);
+
+ ContentResolver.removePeriodicSync(myAccountList[i],
+ mAccountAuthority, b);
+ ContentResolver.addPeriodicSync(myAccountList[i],
+ mAccountAuthority, b, syncFreq * 60);
+ }
+ }
+
+ }
+ } else {
+ // For all other preferences, set the summary to the value's
+ // simple string representation.
+ preference.setSummary(stringValue);
+ }
+ return true;
+ }
+ };
+
+ /**
+ * 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 #sBindPreferenceSummaryToValueListener
+ */
+ private static void bindPreferenceSummaryToValue(Preference preference) {
+ // Set the listener to watch for value changes.
+ preference
+ .setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
+
+ // Trigger the listener immediately with the preference's
+ // current value.
+ sBindPreferenceSummaryToValueListener.onPreferenceChange(
+ preference,
+ PreferenceManager.getDefaultSharedPreferences(
+ preference.getContext()).getString(
+ preference.getKey(),
+ ""
+ )
+ );
+ }
+
+ /**
+ * 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(R.xml.pref_data_sync);
+
+ // 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.
+ bindPreferenceSummaryToValue(findPreference("sync_frequency"));
+ }
+ }
+}
diff --git a/src/fr/unix_experience/owncloud_sms/activities/LoginActivity.java b/src/fr/unix_experience/owncloud_sms/activities/LoginActivity.java
new file mode 100644
index 0000000..291288e
--- /dev/null
+++ b/src/fr/unix_experience/owncloud_sms/activities/LoginActivity.java
@@ -0,0 +1,329 @@
+package fr.unix_experience.owncloud_sms.activities;
+
+/*
+ * Copyright (c) 2014, Loic Blot
+ *
+ * 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 .
+ */
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.inputmethod.EditorInfo;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.OwnCloudClientFactory;
+import com.owncloud.android.lib.common.OwnCloudCredentialsFactory;
+
+import fr.unix_experience.owncloud_sms.R;
+import fr.unix_experience.owncloud_sms.authenticators.OwnCloudAuthenticator;
+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 Activity {
+ /**
+ * Keep track of the login task to ensure we can cancel it if requested.
+ */
+ private UserLoginTask mAuthTask = null;
+
+ // UI references.
+ private Spinner _protocolView;
+ private EditText _loginView;
+ private EditText _passwordView;
+ private EditText _serverView;
+ private View mProgressView;
+ private View mLoginFormView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_login);
+
+ // Set up the login form.
+ _protocolView = (Spinner) findViewById(R.id.oc_protocol);
+ _serverView = (EditText) findViewById(R.id.oc_server);
+ _loginView = (EditText) findViewById(R.id.oc_login);
+
+ _passwordView = (EditText) findViewById(R.id.oc_password);
+ _passwordView
+ .setOnEditorActionListener(new TextView.OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView textView, int id,
+ KeyEvent keyEvent) {
+ if (id == R.id.oc_login || id == EditorInfo.IME_NULL) {
+ attemptLogin();
+ return true;
+ }
+ return false;
+ }
+ });
+
+ Button _signInButton = (Button) findViewById(R.id.oc_signin_button);
+ _signInButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ attemptLogin();
+ }
+ });
+
+ mLoginFormView = findViewById(R.id.login_form);
+ mProgressView = findViewById(R.id.login_progress);
+ }
+
+ /**
+ * Attempts to sign in or register the account specified by the login form.
+ * If there are form errors (invalid email, missing fields, etc.), the
+ * errors are presented and no actual login attempt is made.
+ */
+ public void attemptLogin() {
+ if (mAuthTask != null) {
+ return;
+ }
+
+ // Reset errors.
+ _loginView.setError(null);
+ _passwordView.setError(null);
+
+ // Store values at the time of the login attempt.
+ String protocol = _protocolView.getSelectedItem().toString();
+ String login = _loginView.getText().toString();
+ String password = _passwordView.getText().toString();
+ String serverAddr = _serverView.getText().toString();
+
+ 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));
+ focusView = _loginView;
+ cancel = true;
+ }
+
+ // Check for a valid login address.
+ if (TextUtils.isEmpty(login)) {
+ _loginView.setError(getString(R.string.error_field_required));
+ 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;
+ cancel = true;
+ }
+
+ if (cancel) {
+ // There was an error; don't attempt login and focus the first
+ // form field with an error.
+ focusView.requestFocus();
+ } else {
+ // Show a progress spinner, and kick off a background task to
+ // perform the user login attempt.
+ showProgress(true);
+ String serverURL = new String(protocol + serverAddr);
+ mAuthTask = new UserLoginTask(serverURL, login, password);
+ mAuthTask.execute((Void) null);
+ }
+ }
+
+ private boolean isPasswordValid(String password) {
+ // TODO: Replace this with your own logic
+ return password.length() > 4;
+ }
+
+ /**
+ * Shows the progress UI and hides the login form.
+ */
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
+ public void showProgress(final boolean show) {
+ // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
+ // for very easy animations. If available, use these APIs to fade-in
+ // the progress spinner.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
+ int shortAnimTime = getResources().getInteger(
+ android.R.integer.config_shortAnimTime);
+
+ mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
+ mLoginFormView.animate().setDuration(shortAnimTime)
+ .alpha(show ? 0 : 1)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mLoginFormView.setVisibility(show ? View.GONE
+ : View.VISIBLE);
+ }
+ });
+
+ mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
+ mProgressView.animate().setDuration(shortAnimTime)
+ .alpha(show ? 1 : 0)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mProgressView.setVisibility(show ? View.VISIBLE
+ : View.GONE);
+ }
+ });
+ } else {
+ // The ViewPropertyAnimator APIs are not available, so simply show
+ // and hide the relevant UI components.
+ mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
+ mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
+ }
+ }
+
+ /**
+ * Represents an asynchronous login/registration task used to authenticate
+ * the user.
+ */
+ public class UserLoginTask extends AsyncTask {
+
+ UserLoginTask(String serverURI, String login, String password) {
+ _serverURI = Uri.parse(serverURI);
+ _login = login;
+ _password = password;
+ }
+
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ // Create client object to perform remote operations
+ OwnCloudClient ocClient = OwnCloudClientFactory.createOwnCloudClient(
+ _serverURI, getBaseContext(),
+ // Activity or Service context
+ true
+ );
+
+ // Set basic credentials
+ ocClient.setCredentials(
+ OwnCloudCredentialsFactory.newBasicCredentials(_login, _password)
+ );
+
+ // Send an authentication test to ownCloud
+ OwnCloudAuthenticator at = new OwnCloudAuthenticator(getBaseContext());
+ at.setClient(ocClient);
+
+ _returnCode = at.testCredentials();
+
+ return (_returnCode == LoginReturnCode.OK);
+ }
+
+ @Override
+ protected void onPostExecute(final Boolean success) {
+ mAuthTask = null;
+ showProgress(false);
+
+ if (success) {
+ String accountType = getIntent().getStringExtra(PARAM_AUTHTOKEN_TYPE);
+ if (accountType == null) {
+ accountType = getString(R.string.account_type);
+ }
+
+ // Generate a label
+ String accountLabel = _login + "@" + _serverURI.getHost();
+
+ // We create the account
+ final Account account = new Account(accountLabel, accountType);
+ Bundle accountBundle = new Bundle();
+ accountBundle.putString("ocLogin", _login);
+ accountBundle.putString("ocURI", _serverURI.toString());
+
+ // And we push it to Android
+ AccountManager accMgr = AccountManager.get(getApplicationContext());
+ 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, 15 * 60);
+ // Then it's finished
+ finish();
+ } else {
+ switch (_returnCode) {
+ case INVALID_ADDR:
+ _serverView.setError(getString(R.string.error_invalid_server_address));
+ _serverView.requestFocus();
+ break;
+ case HTTP_CONN_FAILED:
+ _serverView.setError(getString(R.string.error_http_connection_failed));
+ _serverView.requestFocus();
+ break;
+ case CONN_FAILED:
+ _serverView.setError(getString(R.string.error_connection_failed));
+ _serverView.requestFocus();
+ break;
+ case INVALID_LOGIN:
+ _passwordView.setError(getString(R.string.error_invalid_login));
+ _passwordView.requestFocus();
+ break;
+ case UNKNOWN_ERROR:
+ _serverView.setError("UNK");
+ _serverView.requestFocus();
+ break;
+ default:
+ break;
+ }
+
+ }
+ }
+
+ @Override
+ protected void onCancelled() {
+ mAuthTask = null;
+ showProgress(false);
+ }
+
+ private final Uri _serverURI;
+ private final String _login;
+ private final String _password;
+ private LoginReturnCode _returnCode;
+
+ public static final String PARAM_AUTHTOKEN_TYPE = "auth.token";
+ public static final String PARAM_CREATE = "create";
+ }
+}
diff --git a/src/fr/unix_experience/owncloud_sms/authenticators/OwnCloudAuthenticator.java b/src/fr/unix_experience/owncloud_sms/authenticators/OwnCloudAuthenticator.java
new file mode 100644
index 0000000..ead069f
--- /dev/null
+++ b/src/fr/unix_experience/owncloud_sms/authenticators/OwnCloudAuthenticator.java
@@ -0,0 +1,196 @@
+package fr.unix_experience.owncloud_sms.authenticators;
+
+/*
+ * Copyright (c) 2014, Loic Blot
+ *
+ * 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 .
+ */
+
+import java.io.IOException;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.http.HttpStatus;
+import org.json.JSONObject;
+
+import com.owncloud.android.lib.common.OwnCloudClient;
+
+import fr.unix_experience.owncloud_sms.activities.LoginActivity;
+import fr.unix_experience.owncloud_sms.enums.LoginReturnCode;
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.accounts.NetworkErrorException;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+public class OwnCloudAuthenticator extends AbstractAccountAuthenticator {
+ // Simple constructor
+ public OwnCloudAuthenticator(Context context) {
+ super(context);
+ _context = context;
+ }
+
+ @Override
+ public Bundle editProperties(AccountAuthenticatorResponse response,
+ String accountType) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Bundle addAccount(AccountAuthenticatorResponse response,
+ String accountType, String authTokenType,
+ String[] requiredFeatures, Bundle options)
+ throws NetworkErrorException {
+ final Bundle result;
+ final Intent intent;
+
+ intent = new Intent(_context, LoginActivity.class);
+
+ result = new Bundle();
+ result.putParcelable(AccountManager.KEY_INTENT, intent);
+ return result;
+ }
+
+ @Override
+ public Bundle confirmCredentials(AccountAuthenticatorResponse response,
+ Account account, Bundle options) throws NetworkErrorException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Bundle getAuthToken(AccountAuthenticatorResponse response,
+ Account account, String authTokenType, Bundle options)
+ throws NetworkErrorException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getAuthTokenLabel(String authTokenType) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Bundle updateCredentials(AccountAuthenticatorResponse response,
+ Account account, String authTokenType, Bundle options)
+ throws NetworkErrorException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Bundle hasFeatures(AccountAuthenticatorResponse response,
+ Account account, String[] features) throws NetworkErrorException {
+ // 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 = null;
+ int status = -1;
+
+ 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(TAG, "Successful response: " + response);
+
+ // Parse the response
+ JSONObject respJSON = new JSONObject(response);
+ JSONObject respOCS = respJSON.getJSONObject(NODE_OCS);
+ JSONObject respData = respOCS.getJSONObject(NODE_DATA);
+ String id = respData.getString(NODE_ID);
+ String displayName = respData.getString(NODE_DISPLAY_NAME);
+ String email = respData.getString(NODE_EMAIL);
+
+ Log.d(TAG, "*** Parsed user information: " + id + " - " + displayName + " - " + email);
+
+ } else {
+ String response = get.getResponseBodyAsString();
+ Log.e(TAG, "Failed response while getting user information ");
+ if (response != null) {
+ Log.e(TAG, "*** status code: " + status + " ; response message: " + response);
+ } else {
+ Log.e(TAG, "*** status code: " + status);
+ }
+
+ if (status == 401) {
+ bRet = LoginReturnCode.INVALID_LOGIN;
+ }
+ else {
+ bRet = LoginReturnCode.UNKNOWN_ERROR;
+ }
+ }
+
+ } catch (Exception e) {
+ Log.e(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 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";
+}
\ No newline at end of file
diff --git a/src/fr/unix_experience/owncloud_sms/broadcast_receivers/IncomingSms.java b/src/fr/unix_experience/owncloud_sms/broadcast_receivers/IncomingSms.java
new file mode 100644
index 0000000..36029a4
--- /dev/null
+++ b/src/fr/unix_experience/owncloud_sms/broadcast_receivers/IncomingSms.java
@@ -0,0 +1,47 @@
+package fr.unix_experience.owncloud_sms.broadcast_receivers;
+
+/*
+ * Copyright (c) 2014, Loic Blot
+ *
+ * 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 .
+ */
+
+import fr.unix_experience.owncloud_sms.observers.SmsObserver;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Handler;
+import android.util.Log;
+
+public class IncomingSms extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ launchSmsObserver(context);
+ }
+
+ public static void launchSmsObserver(Context context) {
+ if (_mboxObserver == null) {
+ Log.d(TAG,"_mboxObserver == null");
+ _mboxObserver = new SmsObserver(new Handler(), context);
+ context.getContentResolver().
+ registerContentObserver(Uri.parse("content://sms"), true, _mboxObserver);
+ }
+ }
+
+ private static SmsObserver _mboxObserver;
+
+ private static final String TAG = IncomingSms.class.getSimpleName();
+}
diff --git a/src/fr/unix_experience/owncloud_sms/engine/OCSMSOwnCloudClient.java b/src/fr/unix_experience/owncloud_sms/engine/OCSMSOwnCloudClient.java
new file mode 100644
index 0000000..b5ae1a4
--- /dev/null
+++ b/src/fr/unix_experience/owncloud_sms/engine/OCSMSOwnCloudClient.java
@@ -0,0 +1,352 @@
+package fr.unix_experience.owncloud_sms.engine;
+
+/*
+ * Copyright (c) 2014, Loic Blot
+ *
+ * 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 .
+ */
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.ConnectException;
+
+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 android.content.Context;
+import android.net.Uri;
+import android.util.Log;
+
+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.enums.OCSyncErrorType;
+import fr.unix_experience.owncloud_sms.exceptions.OCSyncException;
+
+public class OCSMSOwnCloudClient {
+
+ 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;
+ }
+
+ public Integer getServerAPIVersion() throws OCSyncException {
+ GetMethod get = createGetVersionRequest();
+ JSONObject obj = doHttpRequest(get, true);
+ if (obj == null) {
+ // Return default version
+ return 1;
+ }
+
+ try {
+ _serverAPIVersion = obj.getInt("version");
+ }
+ catch (JSONException e) {
+ Log.e(TAG, "No version received from server, assuming version 1", e);
+ _serverAPIVersion = 1;
+ }
+
+ return _serverAPIVersion;
+ }
+
+ public void doPushRequest(JSONArray smsList) throws OCSyncException {
+ /**
+ * If we need other API push, set it here
+ */
+ switch (_serverAPIVersion) {
+ case 1:
+ default: doPushRequestV1(smsList); break;
+ }
+ }
+
+ public void doPushRequestV1(JSONArray smsList) throws OCSyncException {
+ if (smsList == null) {
+ GetMethod get = createGetSmsIdListRequest();
+ JSONObject smsGetObj = doHttpRequest(get);
+ if (smsGetObj == null) {
+ return;
+ }
+
+ JSONObject smsBoxes = new JSONObject();
+ JSONArray inboxSmsList = null, sentSmsList = null, draftsSmsList = null;
+ try {
+ smsBoxes = smsGetObj.getJSONObject("smslist");
+ } catch (JSONException e) {
+ try {
+ smsGetObj.getJSONArray("smslist");
+ } catch (JSONException e2) {
+ Log.e(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(TAG, "No inbox Sms received from server (doPushRequest, get SMS list)");
+ }
+
+ try {
+ sentSmsList = smsBoxes.getJSONArray("sent");
+ } catch (JSONException e) {
+ Log.d(TAG, "No sent Sms received from server (doPushRequest, get SMS list)");
+ }
+
+ try {
+ draftsSmsList = smsBoxes.getJSONArray("drafts");
+ } catch (JSONException e) {
+ Log.d(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 = fetcher.getJSONMessages();
+ }
+
+ PostMethod post = createPushRequest(smsList);
+ if (post == null) {
+ Log.e(TAG,"Push request for POST is null");
+ throw new OCSyncException(R.string.err_sync_craft_http_request, OCSyncErrorType.IO);
+ }
+
+ JSONObject obj = doHttpRequest(post);
+ if (obj == null) {
+ Log.e(TAG,"Request failed. It doesn't return a valid JSON Object");
+ throw new OCSyncException(R.string.err_sync_push_request, OCSyncErrorType.IO);
+ }
+
+ Boolean pushStatus;
+ String pushMessage;
+ try {
+ pushStatus = obj.getBoolean("status");
+ pushMessage = obj.getString("msg");
+ }
+ catch (JSONException e) {
+ Log.e(TAG, "Invalid datas received from server", e);
+ throw new OCSyncException(R.string.err_sync_push_request_resp, OCSyncErrorType.PARSE);
+ }
+
+ Log.d(TAG, "SMS Push request said: status " + pushStatus + " - " + pushMessage);
+ }
+
+ public GetMethod createGetVersionRequest() {
+ return createGetRequest(OC_GET_VERSION);
+ }
+
+ public GetMethod createGetSmsIdListRequest() {
+ return createGetRequest(OC_GET_ALL_SMS_IDS);
+ }
+
+ public GetMethod createGetSmsIdListWithStateRequest() {
+ return createGetRequest(OC_GET_ALL_SMS_IDS_WITH_STATUS);
+ }
+
+ private GetMethod createGetRequest(String oc_call) {
+ GetMethod get = new GetMethod(_ocClient.getBaseUri() + oc_call);
+ get.addRequestHeader("OCS-APIREQUEST", "true");
+ return get;
+ }
+
+ public PostMethod createPushRequest() throws OCSyncException {
+ SmsFetcher fetcher = new SmsFetcher(_context);
+ JSONArray smsList = fetcher.getJSONMessages();
+ return createPushRequest(smsList);
+ }
+
+ 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() + 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(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 == null ? 0 : smsList.length());
+ } catch (JSONException e) {
+ Log.e(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(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 = null;
+ int status = 0;
+
+ // We try maximumHttpReqTries because sometimes network is slow or unstable
+ int tryNb = 0;
+
+ while (tryNb < maximumHttpReqTries) {
+ tryNb++;
+
+ try {
+ status = _ocClient.executeMethod(req);
+
+ Log.d(TAG, "HTTP Request done at try " + tryNb);
+
+ // Force loop exit
+ tryNb = maximumHttpReqTries;
+ } catch (ConnectException e) {
+ Log.e(TAG, "Unable to perform a connection to ownCloud instance", e);
+
+ // If it's the last try
+ if (tryNb == maximumHttpReqTries) {
+ req.releaseConnection();
+ throw new OCSyncException(R.string.err_sync_http_request_connect, OCSyncErrorType.IO);
+ }
+ } catch (HttpException e) {
+ Log.e(TAG, "Unable to perform a connection to ownCloud instance", e);
+
+ // If it's the last try
+ if (tryNb == maximumHttpReqTries) {
+ req.releaseConnection();
+ throw new OCSyncException(R.string.err_sync_http_request_httpexception, OCSyncErrorType.IO);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to perform a connection to ownCloud instance", e);
+
+ // If it's the last try
+ if (tryNb == maximumHttpReqTries) {
+ req.releaseConnection();
+ throw new OCSyncException(R.string.err_sync_http_request_ioexception, OCSyncErrorType.IO);
+ }
+ }
+ }
+
+ if(status == HttpStatus.SC_OK) {
+ String response = null;
+ try {
+ response = req.getResponseBodyAsString();
+ } catch (IOException e) {
+ Log.e(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 == false) {
+ Log.e(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 = null;
+ try {
+ response = req.getResponseBodyAsString();
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to parse server response", e);
+ throw new OCSyncException(R.string.err_sync_http_request_resp, OCSyncErrorType.PARSE);
+ }
+
+ Log.e(TAG, "Server set unhandled HTTP return code " + status);
+ if (response != null) {
+ Log.e(TAG, "Status code: " + status + ". Response message: " + response);
+ } else {
+ Log.e(TAG, "Status code: " + status);
+ }
+ throw new OCSyncException(R.string.err_sync_http_request_returncode_unhandled, OCSyncErrorType.SERVER_ERROR);
+ }
+ return respJSON;
+ }
+
+ public OwnCloudClient getOCClient() { return _ocClient; }
+
+ private static int maximumHttpReqTries = 3;
+
+ private OwnCloudClient _ocClient;
+ private Context _context;
+
+ private Integer _serverAPIVersion;
+
+ private static String OC_GET_VERSION = "/index.php/apps/ocsms/get/apiversion?format=json";
+ private static String OC_GET_ALL_SMS_IDS = "/index.php/apps/ocsms/get/smsidlist?format=json";
+ private static String OC_GET_ALL_SMS_IDS_WITH_STATUS = "/index.php/apps/ocsms/get/smsidstate?format=json";
+ private static String OC_PUSH_ROUTE = "/index.php/apps/ocsms/push?format=json";
+
+ private static final String TAG = OCSMSOwnCloudClient.class.getSimpleName();
+
+
+}
diff --git a/src/fr/unix_experience/owncloud_sms/engine/SmsFetcher.java b/src/fr/unix_experience/owncloud_sms/engine/SmsFetcher.java
new file mode 100644
index 0000000..32dc946
--- /dev/null
+++ b/src/fr/unix_experience/owncloud_sms/engine/SmsFetcher.java
@@ -0,0 +1,248 @@
+package fr.unix_experience.owncloud_sms.engine;
+
+/*
+ * Copyright (c) 2014, Loic Blot
+ *
+ * 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 .
+ */
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import fr.unix_experience.owncloud_sms.enums.MailboxID;
+import fr.unix_experience.owncloud_sms.providers.SmsDataProvider;
+import android.content.Context;
+import android.database.Cursor;
+import android.util.Log;
+
+public class SmsFetcher {
+ public SmsFetcher(Context ct) {
+ _context = ct;
+
+ _existingInboxMessages = null;
+ _existingSentMessages = null;
+ _existingDraftsMessages = null;
+ }
+
+ public JSONArray getJSONMessages() {
+ _jsonTempDatas = new JSONArray();
+
+ getInboxMessages(true);
+ getSentMessages(true);
+ getDraftMessages(true);
+
+ JSONArray result = _jsonTempDatas;
+
+ // Empty the buffer
+ _jsonTempDatas = new JSONArray();
+ return result;
+ }
+
+ public JSONArray getInboxMessages(Boolean toTempBuffer) {
+ return getMailboxMessages("content://sms/inbox", toTempBuffer, MailboxID.INBOX);
+ }
+
+ public JSONArray getSentMessages(Boolean toTempBuffer) {
+ return getMailboxMessages("content://sms/sent", toTempBuffer, MailboxID.SENT);
+ }
+
+ public JSONArray getDraftMessages(Boolean toTempBuffer) {
+ return getMailboxMessages("content://sms/drafts", toTempBuffer, MailboxID.DRAFTS);
+ }
+
+ private JSONArray getMailboxMessages(String _mb, Boolean toTempBuffer, MailboxID _mbID) {
+ if (_context == null || _mb.length() == 0) {
+ return null;
+ }
+
+ // Fetch Sent SMS Message from Built-in Content Provider
+ Cursor c = (new SmsDataProvider(_context)).query(_mb);
+
+ // We create a list of strings to store results
+ JSONArray results = new JSONArray();
+
+ // Reading mailbox
+ if (c != null && c.getCount() > 0) {
+ c.moveToFirst();
+ do {
+ JSONObject entry = new JSONObject();
+
+ try {
+ // Reading each mail element
+ int msgId = -1;
+ for(int idx=0;idx 0 ? "true" : "false");
+ }
+ else {
+ entry.put(colName, c.getString(idx));
+ }
+ }
+
+ // Mailbox ID is required by server
+ entry.put("mbox", _mbID.ordinal());
+
+ /*
+ * Use the existing lists to verify if mail needs to be buffered
+ * It's useful to decrease data use
+ */
+ if (_mbID == MailboxID.INBOX && isAnExistingInboxMessage(msgId) == false ||
+ _mbID == MailboxID.SENT && isAnExistingSentMessage(msgId) == false ||
+ _mbID == MailboxID.DRAFTS && isAnExistingDraftsMessage(msgId) == false) {
+ if (toTempBuffer) {
+ _jsonTempDatas.put(entry);
+ }
+ else {
+ results.put(entry);
+ }
+ }
+
+ } catch (JSONException e) {
+ Log.e(TAG, "JSON Exception when reading SMS Mailbox", e);
+ c.close();
+ }
+ }
+ while(c.moveToNext());
+
+ Log.d(TAG, c.getCount() + " messages read from " +_mb);
+
+ c.close();
+ }
+
+ return results;
+ }
+
+ public JSONArray getLastMessage(String _mb) {
+ if (_context == null || _mb.length() == 0) {
+ return null;
+ }
+
+ // Fetch Sent SMS Message from Built-in Content Provider
+ Cursor c = (new SmsDataProvider(_context)).query(_mb);
+
+ c.moveToNext();
+
+ // We create a list of strings to store results
+ JSONArray results = new JSONArray();
+
+ JSONObject entry = new JSONObject();
+
+ try {
+ for(int idx=0;idx 0 ? "true" : "false");
+ }
+ else {
+ entry.put(colName, c.getString(idx));
+ }
+ }
+
+ // Mailbox ID is required by server
+ switch (entry.getInt("type")) {
+ case 1: entry.put("mbox", MailboxID.INBOX.ordinal()); break;
+ case 2: entry.put("mbox", MailboxID.SENT.ordinal()); break;
+ case 3: entry.put("mbox", MailboxID.DRAFTS.ordinal()); break;
+ }
+ results.put(entry);
+ } catch (JSONException e) {
+ Log.e(TAG, "JSON Exception when reading SMS Mailbox", e);
+ c.close();
+ }
+
+ c.close();
+
+ return results;
+ }
+
+ private boolean isAnExistingInboxMessage(int msgId) {
+ return isExistingMessage(_existingInboxMessages, msgId);
+ }
+
+ private boolean isAnExistingSentMessage(int msgId) {
+ return isExistingMessage(_existingSentMessages, msgId);
+ }
+
+ private boolean isAnExistingDraftsMessage(int msgId) {
+ return isExistingMessage(_existingDraftsMessages, msgId);
+ }
+
+ private boolean isExistingMessage(JSONArray msgList, int msgId) {
+ if (msgList == null) {
+ return false;
+ }
+
+ int len = msgList.length();
+ for (int i = 0; i < len; i++) {
+ try {
+ if (msgList.getInt(i) == msgId) {
+ return true;
+ }
+ } catch (JSONException e) {
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+ public void setExistingInboxMessages(JSONArray inboxMessages) {
+ _existingInboxMessages = inboxMessages;
+ }
+
+ public void setExistingSentMessages(JSONArray sentMessages) {
+ _existingSentMessages = sentMessages;
+ }
+
+ public void setExistingDraftsMessages(JSONArray draftMessages) {
+ _existingDraftsMessages = draftMessages;
+ }
+
+ private Context _context;
+ private JSONArray _jsonTempDatas;
+ private JSONArray _existingInboxMessages;
+ private JSONArray _existingSentMessages;
+ private JSONArray _existingDraftsMessages;
+
+ private static final String TAG = SmsFetcher.class.getSimpleName();
+
+
+}
diff --git a/src/fr/unix_experience/owncloud_sms/enums/OCSMSNotificationType.java b/src/fr/unix_experience/owncloud_sms/enums/OCSMSNotificationType.java
new file mode 100644
index 0000000..2207131
--- /dev/null
+++ b/src/fr/unix_experience/owncloud_sms/enums/OCSMSNotificationType.java
@@ -0,0 +1,23 @@
+package fr.unix_experience.owncloud_sms.enums;
+
+/*
+ * Copyright (c) 2014, Loic Blot
+ *
+ * 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 .
+ */
+
+public enum OCSMSNotificationType {
+ SYNC,
+ SYNC_FAILED,
+}
diff --git a/src/fr/unix_experience/owncloud_sms/exceptions/OCSyncException.java b/src/fr/unix_experience/owncloud_sms/exceptions/OCSyncException.java
index 8cce955..f6a6294 100644
--- a/src/fr/unix_experience/owncloud_sms/exceptions/OCSyncException.java
+++ b/src/fr/unix_experience/owncloud_sms/exceptions/OCSyncException.java
@@ -1,5 +1,22 @@
package fr.unix_experience.owncloud_sms.exceptions;
+/*
+ * Copyright (c) 2014, Loic Blot
+ *
+ * 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 .
+ */
+
import fr.unix_experience.owncloud_sms.enums.OCSyncErrorType;
public class OCSyncException extends Exception {
diff --git a/src/fr/unix_experience/owncloud_sms/observers/SmsObserver.java b/src/fr/unix_experience/owncloud_sms/observers/SmsObserver.java
new file mode 100644
index 0000000..6768b0d
--- /dev/null
+++ b/src/fr/unix_experience/owncloud_sms/observers/SmsObserver.java
@@ -0,0 +1,98 @@
+package fr.unix_experience.owncloud_sms.observers;
+
+/*
+ * Copyright (c) 2014, Loic Blot
+ *
+ * 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 .
+ */
+
+import org.json.JSONArray;
+
+import fr.unix_experience.owncloud_sms.R;
+import fr.unix_experience.owncloud_sms.engine.OCSMSOwnCloudClient;
+import fr.unix_experience.owncloud_sms.engine.SmsFetcher;
+import fr.unix_experience.owncloud_sms.exceptions.OCSyncException;
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.util.Log;
+
+public class SmsObserver extends ContentObserver {
+
+ public SmsObserver(Handler handler) {
+ super(handler);
+ }
+
+ public SmsObserver(Handler handler, Context ct) {
+ super(handler);
+ _context = ct;
+ }
+
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ Log.d(TAG, "onChange SmsObserver");
+
+ if (_accountMgr == null && _context != null) {
+ _accountMgr = AccountManager.get(_context);
+ }
+ String smsURI = "content://sms";
+
+ SmsFetcher sFetch = new SmsFetcher(_context);
+ JSONArray smsList = sFetch.getLastMessage(smsURI);
+
+ if (smsList != null) {
+ new SyncTask(smsList).execute();
+ }
+ }
+
+ private class SyncTask extends AsyncTask{
+ public SyncTask(JSONArray smsList) {
+ _smsList = smsList;
+ }
+ @Override
+ protected Void doInBackground(Void... params) {
+ // Get ownCloud SMS account list
+ Account[] myAccountList = _accountMgr.getAccountsByType(_context.getString(R.string.account_type));
+ for (int i = 0; i < myAccountList.length; i++) {
+ Log.d(TAG, "int i = 0; i < myAccountList.length; i++" + myAccountList[i] + " SmsObserver");
+ Uri serverURI = Uri.parse(_accountMgr.getUserData(myAccountList[i], "ocURI"));
+
+ OCSMSOwnCloudClient _client = new OCSMSOwnCloudClient(_context,
+ serverURI, _accountMgr.getUserData(myAccountList[i], "ocLogin"),
+ _accountMgr.getPassword(myAccountList[i]));
+
+ try {
+ _client.doPushRequest(_smsList);
+ } catch (OCSyncException e) {
+ Log.e(TAG, _context.getString(e.getErrorId()));
+ }
+ }
+ return null;
+ }
+ private JSONArray _smsList;
+ }
+
+ public void setContext(Context context) {
+ _context = context;
+ }
+
+ private Context _context;
+ private static AccountManager _accountMgr;
+
+ private static final String TAG = OCSMSOwnCloudClient.class.getSimpleName();
+}
diff --git a/src/fr/unix_experience/owncloud_sms/sync_adapters/SmsSyncAdapter.java b/src/fr/unix_experience/owncloud_sms/sync_adapters/SmsSyncAdapter.java
new file mode 100644
index 0000000..836afe4
--- /dev/null
+++ b/src/fr/unix_experience/owncloud_sms/sync_adapters/SmsSyncAdapter.java
@@ -0,0 +1,83 @@
+package fr.unix_experience.owncloud_sms.sync_adapters;
+
+/*
+ * Copyright (c) 2014, Loic Blot
+ *
+ * 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 .
+ */
+
+import fr.unix_experience.owncloud_sms.engine.OCSMSOwnCloudClient;
+import fr.unix_experience.owncloud_sms.enums.OCSyncErrorType;
+import fr.unix_experience.owncloud_sms.exceptions.OCSyncException;
+import fr.unix_experience.owncloud_sms.notifications.OCSMSNotificationManager;
+import 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;
+
+public class SmsSyncAdapter extends AbstractThreadedSyncAdapter {
+
+ public SmsSyncAdapter(Context context, boolean autoInitialize) {
+ super(context, autoInitialize);
+ _accountMgr = AccountManager.get(context);
+ }
+
+ @Override
+ public void onPerformSync(Account account, Bundle extras, String authority,
+ ContentProviderClient provider, SyncResult syncResult) {
+
+ OCSMSNotificationManager nMgr = new OCSMSNotificationManager(getContext());
+ nMgr.setSyncProcessMsg();
+
+ // Create client
+ Uri serverURI = Uri.parse(_accountMgr.getUserData(account, "ocURI"));
+
+ OCSMSOwnCloudClient _client = new OCSMSOwnCloudClient(getContext(),
+ serverURI, _accountMgr.getUserData(account, "ocLogin"),
+ _accountMgr.getPassword(account));
+
+ try {
+ // getServerAPI version
+ Log.d(TAG,"Server API version: " + _client.getServerAPIVersion());
+
+ // and push datas
+ _client.doPushRequest(null);
+ } catch (OCSyncException e) {
+ nMgr.setSyncErrorMsg(getContext().getString(e.getErrorId()));
+ if (e.getErrorType() == OCSyncErrorType.IO) {
+ syncResult.stats.numIoExceptions++;
+ }
+ else if (e.getErrorType() == OCSyncErrorType.PARSE) {
+ syncResult.stats.numParseExceptions++;
+ }
+ else if (e.getErrorType() == OCSyncErrorType.AUTH) {
+ syncResult.stats.numAuthExceptions++;
+ }
+ else {
+ // UNHANDLED
+ }
+ }
+
+ nMgr.dropSyncProcessMsg();
+ }
+
+ private AccountManager _accountMgr;
+
+ private static final String TAG = SmsSyncAdapter.class.getSimpleName();
+}