mirror of
https://github.com/nerzhul/ownCloud-SMS-App.git
synced 2025-06-07 07:56:14 +00:00
Publish many sources as AGPLv3. Also set Android 5.0 compat
This commit is contained in:
parent
40dd4c72e2
commit
a554462ff8
85
AndroidManifest.xml
Normal file
85
AndroidManifest.xml
Normal file
@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="fr.unix_experience.owncloud_sms"
|
||||
android:versionCode="15"
|
||||
android:versionName="0.15.5" >
|
||||
<!-- From Android 3.0 to 4.4w -->
|
||||
<uses-sdk
|
||||
android:minSdkVersion="11"
|
||||
android:maxSdkVersion="21"
|
||||
android:targetSdkVersion="19" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_SMS" />
|
||||
<!-- For SMS Broadcaster -->
|
||||
<uses-permission android:name="android.permission.RECEIVE_SMS" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<!-- For syncer -->
|
||||
<uses-permission android:name="android.permission.READ_SYNC_STATS" />
|
||||
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
|
||||
<!-- For account management -->
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme" >
|
||||
|
||||
<!-- Related to periodic sync -->
|
||||
<service android:name=".observers.SmsObserverService"
|
||||
android:exported="false" />
|
||||
|
||||
<service
|
||||
android:name=".sync_adapters.SmsSyncService"
|
||||
android:exported="true"
|
||||
android:process=":sync" >
|
||||
<intent-filter>
|
||||
<action android:name="android.content.SyncAdapter" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.content.SyncAdapter"
|
||||
android:resource="@xml/sync_adapter" />
|
||||
</service>
|
||||
|
||||
<provider
|
||||
android:name=".providers.SmsDataProvider"
|
||||
android:authorities="fr.unix_experience.owncloud_sms.datasync.provider">
|
||||
</provider>
|
||||
|
||||
<!-- Related to Login -->
|
||||
<service android:name=".authenticators.OwnCloudAuthenticatorService">
|
||||
<intent-filter>
|
||||
<action android:name="android.accounts.AccountAuthenticator" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.accounts.AccountAuthenticator"
|
||||
android:resource="@xml/owncloud_account_authenticator" />
|
||||
</service>
|
||||
|
||||
<receiver android:name=".broadcast_receivers.IncomingSms">
|
||||
<intent-filter>
|
||||
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<activity
|
||||
android:name=".activities.LoginActivity"
|
||||
android:label="@string/title_activity_login" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name="fr.unix_experience.owncloud_sms.activities.GeneralSettingsActivity"
|
||||
android:label="@string/title_activity_general_settings" >
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -0,0 +1,208 @@
|
||||
package fr.unix_experience.owncloud_sms.activities;
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014, Loic Blot <loic.blot@unix-experience.fr>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.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<PeriodicSync> 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"));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,329 @@
|
||||
package fr.unix_experience.owncloud_sms.activities;
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014, Loic Blot <loic.blot@unix-experience.fr>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.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<Void, Void, Boolean> {
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
@ -0,0 +1,196 @@
|
||||
package fr.unix_experience.owncloud_sms.authenticators;
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014, Loic Blot <loic.blot@unix-experience.fr>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 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";
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package fr.unix_experience.owncloud_sms.broadcast_receivers;
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014, Loic Blot <loic.blot@unix-experience.fr>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 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();
|
||||
}
|
@ -0,0 +1,352 @@
|
||||
package fr.unix_experience.owncloud_sms.engine;
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014, Loic Blot <loic.blot@unix-experience.fr>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 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();
|
||||
|
||||
|
||||
}
|
248
src/fr/unix_experience/owncloud_sms/engine/SmsFetcher.java
Normal file
248
src/fr/unix_experience/owncloud_sms/engine/SmsFetcher.java
Normal file
@ -0,0 +1,248 @@
|
||||
package fr.unix_experience.owncloud_sms.engine;
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014, Loic Blot <loic.blot@unix-experience.fr>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 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<c.getColumnCount();idx++) {
|
||||
String colName = c.getColumnName(idx);
|
||||
|
||||
// Id column is must be an integer
|
||||
if (colName.equals(new String("_id")) ||
|
||||
colName.equals(new String("type"))) {
|
||||
entry.put(colName, c.getInt(idx));
|
||||
|
||||
// bufferize Id for future use
|
||||
if (colName.equals(new String("_id"))) {
|
||||
msgId = c.getInt(idx);
|
||||
}
|
||||
}
|
||||
// Seen and read must be pseudo boolean
|
||||
else if (colName.equals(new String("read")) ||
|
||||
colName.equals(new String("seen"))) {
|
||||
entry.put(colName, c.getInt(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<c.getColumnCount();idx++) {
|
||||
String colName = c.getColumnName(idx);
|
||||
|
||||
// Id column is must be an integer
|
||||
if (colName.equals(new String("_id")) ||
|
||||
colName.equals(new String("type"))) {
|
||||
entry.put(colName, c.getInt(idx));
|
||||
|
||||
// bufferize Id for future use
|
||||
if (colName.equals(new String("_id"))) {
|
||||
}
|
||||
}
|
||||
// Seen and read must be pseudo boolean
|
||||
else if (colName.equals(new String("read")) ||
|
||||
colName.equals(new String("seen"))) {
|
||||
entry.put(colName, c.getInt(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();
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package fr.unix_experience.owncloud_sms.enums;
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014, Loic Blot <loic.blot@unix-experience.fr>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public enum OCSMSNotificationType {
|
||||
SYNC,
|
||||
SYNC_FAILED,
|
||||
}
|
@ -1,5 +1,22 @@
|
||||
package fr.unix_experience.owncloud_sms.exceptions;
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014, Loic Blot <loic.blot@unix-experience.fr>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import fr.unix_experience.owncloud_sms.enums.OCSyncErrorType;
|
||||
|
||||
public class OCSyncException extends Exception {
|
||||
|
@ -0,0 +1,98 @@
|
||||
package fr.unix_experience.owncloud_sms.observers;
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014, Loic Blot <loic.blot@unix-experience.fr>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 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<Void, Void, Void>{
|
||||
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();
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package fr.unix_experience.owncloud_sms.sync_adapters;
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014, Loic Blot <loic.blot@unix-experience.fr>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 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();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user