From a554462ff8cd7e2d73715b0bb48b9fca0ad79f81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Blot=20=28=40U-Exp=29?= Date: Sun, 30 Nov 2014 18:50:07 +0100 Subject: [PATCH] Publish many sources as AGPLv3. Also set Android 5.0 compat --- AndroidManifest.xml | 85 +++++ .../activities/GeneralSettingsActivity.java | 208 +++++++++++ .../activities/LoginActivity.java | 329 ++++++++++++++++ .../authenticators/OwnCloudAuthenticator.java | 196 ++++++++++ .../broadcast_receivers/IncomingSms.java | 47 +++ .../engine/OCSMSOwnCloudClient.java | 352 ++++++++++++++++++ .../owncloud_sms/engine/SmsFetcher.java | 248 ++++++++++++ .../enums/OCSMSNotificationType.java | 23 ++ .../exceptions/OCSyncException.java | 17 + .../owncloud_sms/observers/SmsObserver.java | 98 +++++ .../sync_adapters/SmsSyncAdapter.java | 83 +++++ 11 files changed, 1686 insertions(+) create mode 100644 AndroidManifest.xml create mode 100644 src/fr/unix_experience/owncloud_sms/activities/GeneralSettingsActivity.java create mode 100644 src/fr/unix_experience/owncloud_sms/activities/LoginActivity.java create mode 100644 src/fr/unix_experience/owncloud_sms/authenticators/OwnCloudAuthenticator.java create mode 100644 src/fr/unix_experience/owncloud_sms/broadcast_receivers/IncomingSms.java create mode 100644 src/fr/unix_experience/owncloud_sms/engine/OCSMSOwnCloudClient.java create mode 100644 src/fr/unix_experience/owncloud_sms/engine/SmsFetcher.java create mode 100644 src/fr/unix_experience/owncloud_sms/enums/OCSMSNotificationType.java create mode 100644 src/fr/unix_experience/owncloud_sms/observers/SmsObserver.java create mode 100644 src/fr/unix_experience/owncloud_sms/sync_adapters/SmsSyncAdapter.java 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(); +}