Migrate synchronization of notifications to a Worker process
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing

This commit is contained in:
Amab 2024-08-15 17:31:30 +02:00
parent b8337b00f6
commit a344934961
18 changed files with 255 additions and 888 deletions

View File

@ -93,6 +93,7 @@ android {
dependencies {
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation "androidx.work:work-runtime:2.9.1"
implementation 'com.google.code.ksoap2-android:ksoap2-android:3.6.4'
implementation 'commons-io:commons-io:2.13.0'
implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'

View File

@ -17,9 +17,6 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-feature
@ -196,8 +193,6 @@
android:name="android.support.PARENT_ACTIVITY"
android:value="es.ugr.swad.swadroid.SWADMain" />
</activity>
<activity android:name="es.ugr.swad.swadroid.sync.AccountAuthenticator" >
</activity>
<activity
android:name="es.ugr.swad.swadroid.modules.downloads.DirectoryTreeDownload"
android:label="@string/documentsDownloadModuleLabel"
@ -337,56 +332,6 @@
android:windowSoftInputMode="adjustResize|stateVisible" >
</activity>
<provider
android:name="es.ugr.swad.swadroid.sync.DummyProvider"
android:authorities="es.ugr.swad.swadroid.content"
android:label="SWADroid"
android:syncable="true"
android:exported="false" />
<service
android:name="es.ugr.swad.swadroid.sync.AccountAuthenticatorService"
android:exported="false" >
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
<service
android:name="es.ugr.swad.swadroid.modules.notifications.NotificationsSyncAdapterService"
android:exported="false"
android:foregroundServiceType="dataSync" >
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/sync_notifications" />
</service>
<receiver
android:name="es.ugr.swad.swadroid.modules.notifications.RestarterNotificationsReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.REBOOT" />
<action android:name="android.intent.action.SCREEN_ON" />
<action android:name="android.intent.action.SCREEN_OFF" />
<action android:name="android.intent.action.USER_PRESENT" />
<action android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<action android:name="android.intent.action.PACKAGE_RESTARTED" />
<action android:name="android.intent.action.MY_PACKAGE_SUSPENDED" />
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="es.ugr.swad.swadroid.fileprovider"

View File

@ -51,12 +51,16 @@ import android.widget.Toast;
import androidx.annotation.RequiresApi;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.work.PeriodicWorkRequest;
import androidx.work.WorkManager;
import androidx.work.WorkRequest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import es.ugr.swad.swadroid.database.DataBaseHelper;
import es.ugr.swad.swadroid.gui.AlertNotificationFactory;
@ -76,12 +80,11 @@ import es.ugr.swad.swadroid.modules.login.LoginActivity;
import es.ugr.swad.swadroid.modules.messages.Messages;
import es.ugr.swad.swadroid.modules.notices.Notices;
import es.ugr.swad.swadroid.modules.notifications.Notifications;
import es.ugr.swad.swadroid.modules.notifications.NotificationsWorker;
import es.ugr.swad.swadroid.modules.qr.GenerateQR;
import es.ugr.swad.swadroid.modules.rollcall.Rollcall;
import es.ugr.swad.swadroid.modules.tests.Tests;
import es.ugr.swad.swadroid.preferences.Preferences;
import es.ugr.swad.swadroid.sync.AccountAuthenticator;
import es.ugr.swad.swadroid.sync.SyncUtils;
import es.ugr.swad.swadroid.utils.DateTimeUtils;
import es.ugr.swad.swadroid.utils.Utils;
@ -140,6 +143,11 @@ public class SWADMain extends MenuExpandableListActivity {
private ProgressScreen mProgressScreen;
/**
* WorkManager for synchronization of notifications
*/
private WorkManager workManager;
/**
* Gets the database helper
*
@ -204,6 +212,8 @@ public class SWADMain extends MenuExpandableListActivity {
View view = findViewById(R.id.main_view);
setOnApplyWindowInsetsListener(view);
workManager = WorkManager.getInstance(this);
} catch (Exception ex) {
error(ex.getMessage(), ex);
}
@ -275,11 +285,8 @@ public class SWADMain extends MenuExpandableListActivity {
private void firstRun(int currentVersion) {
Log.i(TAG, "Running application for first time. Setting current version to " + currentVersion);
Intent activity = new Intent(this, AccountAuthenticator.class);
//Configure automatic synchronization
Preferences.setSyncTime(String.valueOf(Constants.DEFAULT_SYNC_TIME));
startActivity(activity);
configAutoSync();
Preferences.setLastVersion(currentVersion);
firstRun = true;
@ -309,13 +316,9 @@ public class SWADMain extends MenuExpandableListActivity {
Preferences.setSyncTime(String.valueOf(Constants.DEFAULT_SYNC_TIME));
}
if(lastVersion < 57) {
if(lastVersion < 1005011) {
//Reconfigure automatic synchronization
SyncUtils.removePeriodicSync(Constants.AUTHORITY, Bundle.EMPTY, this);
if(!Preferences.getSyncTime().equals("0") && Preferences.isSyncEnabled()) {
SyncUtils.addPeriodicSync(Constants.AUTHORITY, Bundle.EMPTY,
Long.parseLong(Preferences.getSyncTime()), this);
}
configAutoSync();
}
Preferences.setLastVersion(currentVersion);
@ -325,7 +328,10 @@ public class SWADMain extends MenuExpandableListActivity {
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
WorkManager workManager = WorkManager.getInstance(this);
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
switch (requestCode) {
//After get the list of courses, a spinner is showed to choice the course
@ -333,11 +339,7 @@ public class SWADMain extends MenuExpandableListActivity {
loadCourses();
//User credentials are correct. Set periodic synchronization if enabled
if (!"0".equals(Preferences.getSyncTime())
&& Preferences.isSyncEnabled() && SyncUtils.isPeriodicSynced(this)) {
SyncUtils.addPeriodicSync(Constants.AUTHORITY, Bundle.EMPTY,
Long.parseLong(Preferences.getSyncTime()), this);
}
configAutoSync();
mProgressScreen.hide();
@ -354,7 +356,7 @@ public class SWADMain extends MenuExpandableListActivity {
//After get the list of courses, a dialog is launched to choice the course
case Constants.COURSES_REQUEST_CODE:
//User credentials are wrong. Remove periodic synchronization
SyncUtils.removePeriodicSync(Constants.AUTHORITY, Bundle.EMPTY, this);
cancelAllNotifSyncWorks();
mProgressScreen.hide();
break;
case Constants.LOGIN_REQUEST_CODE:
@ -847,4 +849,21 @@ public class SWADMain extends MenuExpandableListActivity {
}
private void cancelAllNotifSyncWorks() {
workManager.cancelAllWorkByTag(Preferences.NOTIF_SYNC_WORKER_TAG);
}
private void configAutoSync() {
Preferences.setSyncTime(String.valueOf(Constants.DEFAULT_SYNC_TIME));
String syncTime = Preferences.getSyncTime();
cancelAllNotifSyncWorks();
if (!syncTime.equals("0") && Preferences.isSyncEnabled()) {
WorkRequest notifRequest =
new PeriodicWorkRequest.Builder(NotificationsWorker.class,
Long.parseLong(syncTime), TimeUnit.MILLISECONDS)
.build();
workManager.enqueue(notifRequest);
}
}
}

View File

@ -63,13 +63,13 @@ public class NotificationItem extends MenuActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.single_notification_view);
senderTextView = (TextView) this.findViewById(R.id.senderNameText);
courseTextView = (TextView) this.findViewById(R.id.courseNameText);
summaryTextView = (TextView) this.findViewById(R.id.summaryText);
userPhotoView = (ImageView) this.findViewById(R.id.notifUserPhoto);
webview = (WebView) this.findViewById(R.id.contentWebView);
dateTextView = (TextView) this.findViewById(R.id.notifDate);
timeTextView = (TextView) this.findViewById(R.id.notifTime);
senderTextView = this.findViewById(R.id.senderNameText);
courseTextView = this.findViewById(R.id.courseNameText);
summaryTextView = this.findViewById(R.id.summaryText);
userPhotoView = this.findViewById(R.id.notifUserPhoto);
webview = this.findViewById(R.id.contentWebView);
dateTextView = this.findViewById(R.id.notifDate);
timeTextView = this.findViewById(R.id.notifTime);
notifCode = Long.valueOf(this.getIntent().getStringExtra("notifCode"));
eventCode = Long.valueOf(this.getIntent().getStringExtra("eventCode"));
@ -91,7 +91,7 @@ public class NotificationItem extends MenuActivity {
//If the user photo exists and is public, download and show it
if (Utils.connectionAvailable(this)
&& (userPhoto != null) && !userPhoto.equals("")
&& (userPhoto != null) && !userPhoto.isEmpty()
&& !userPhoto.equals(Constants.NULL_VALUE)) {
ImageFactory.displayImage(getApplicationContext(), userPhoto, userPhotoView, true, true,
R.drawable.usr_bl, R.drawable.usr_bl, R.drawable.usr_bl);
@ -132,7 +132,7 @@ public class NotificationItem extends MenuActivity {
if(requestCode == Constants.NOTIFMARKALLASREAD_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
Log.i(TAG, "Notification " + notifCode + " marked as readed in SWAD");
Log.i(TAG, "Notification " + notifCode + " marked as read in SWAD");
} else {
Log.e(TAG, "Error marking notification " + notifCode + " as read in SWAD");
}

View File

@ -25,7 +25,6 @@ import android.app.Activity;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@ -43,25 +42,22 @@ import android.widget.Toast;
import androidx.core.content.ContextCompat;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import org.ksoap2.serialization.SoapObject;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;
import androidx.work.WorkRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import es.ugr.swad.swadroid.Constants;
import es.ugr.swad.swadroid.R;
import es.ugr.swad.swadroid.database.DataBaseHelper;
import es.ugr.swad.swadroid.gui.AlertNotificationFactory;
import es.ugr.swad.swadroid.model.Model;
import es.ugr.swad.swadroid.model.SWADNotification;
import es.ugr.swad.swadroid.modules.Module;
import es.ugr.swad.swadroid.modules.login.Login;
import es.ugr.swad.swadroid.sync.SyncUtils;
import es.ugr.swad.swadroid.utils.DateTimeUtils;
import es.ugr.swad.swadroid.utils.Utils;
import es.ugr.swad.swadroid.webservices.SOAPClient;
/**
* Notifications module for get user's notifications
@ -184,6 +180,11 @@ public class Notifications extends Module implements
return true;
};
/**
* WorkManager for synchronization of notifications
*/
private WorkManager workManager;
/**
* Refreshes data on screen
*/
@ -286,10 +287,10 @@ public class Notifications extends Module implements
childItem = new ArrayList<>();
//Init ExpandableListView
initSwipeOptions();
initSwipeOptions();
//Set ExpandableListView data
setGroupData();
setGroupData();
setChildGroupData();
/*
@ -317,6 +318,8 @@ public class Notifications extends Module implements
View view = findViewById(R.id.expandablelist_items_pulltorefresh);
setOnApplyWindowInsetsListener(view);
workManager = WorkManager.getInstance(this);
}
/**
@ -343,8 +346,8 @@ public class Notifications extends Module implements
super.onResume();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(NotificationsSyncAdapterService.START_SYNC);
intentFilter.addAction(NotificationsSyncAdapterService.STOP_SYNC);
intentFilter.addAction(NotificationsWorker.START_SYNC);
intentFilter.addAction(NotificationsWorker.STOP_SYNC);
intentFilter.addAction(Intent.CATEGORY_DEFAULT);
ContextCompat.registerReceiver(this, receiver, intentFilter, ContextCompat.RECEIVER_NOT_EXPORTED);
@ -372,97 +375,10 @@ public class Notifications extends Module implements
*/
@Override
protected void requestService() throws Exception {
int numDeletedNotif;
if (SyncUtils.isSyncAutomatically(getApplicationContext())) {
Log.i(TAG,
"Automatic synchronization is enabled. Requesting asynchronous sync operation");
// Call synchronization service
ContentResolver.requestSync(account, authority, new Bundle());
} else {
Log.i(TAG,
"Automatic synchronization is disabled. Requesting manual sync operation");
// Calculates next timestamp to be requested
Long timestamp = Long.valueOf(dbHelper
.getFieldOfLastNotification("eventTime"));
timestamp++;
// Creates webservice request, adds required params and sends
// request to webservice
createRequest(SOAPClient.CLIENT_TYPE);
addParam("wsKey", Login.getLoggedUser().getWsKey());
addParam("beginTime", timestamp);
sendRequest(SWADNotification.class, false);
if (result != null) {
dbHelper.beginTransaction();
// Stores notifications data returned by webservice response
ArrayList<?> res = new ArrayList<Object>((Vector<?>) result);
SoapObject soap = (SoapObject) res.get(1);
int numNotif = soap.getPropertyCount();
notifCount = 0;
for (int i = 0; i < numNotif; i++) {
SoapObject pii = (SoapObject) soap.getProperty(i);
long notifCode = Long.parseLong(pii.getProperty("notifCode")
.toString());
long eventCode = Long.parseLong(pii.getProperty(
"eventCode").toString());
String eventType = pii.getProperty("eventType").toString();
long eventTime = Long.parseLong(pii.getProperty("eventTime")
.toString());
String userNickname = pii.getProperty("userNickname")
.toString();
String userSurname1 = pii.getProperty("userSurname1")
.toString();
String userSurname2 = pii.getProperty("userSurname2")
.toString();
String userFirstName = pii.getProperty("userFirstname")
.toString();
String userPhoto = pii.getProperty("userPhoto").toString();
String location = pii.getProperty("location").toString();
String summary = pii.getProperty("summary").toString();
int status = Integer.parseInt(pii.getProperty("status")
.toString());
String content = pii.getProperty("content").toString();
boolean notifReadSWAD = (status >= 4);
boolean notifCancelled = (status >= 8);
// Add not cancelled notifications only
if(!notifCancelled) {
SWADNotification n = new SWADNotification(notifCode,
eventCode, eventType, eventTime, userNickname, userSurname1,
userSurname2, userFirstName, userPhoto, location,
summary, status, content, notifReadSWAD,
notifReadSWAD);
dbHelper.insertNotification(n);
// Count unread notifications only
if (!notifReadSWAD) {
notifCount++;
}
if (isDebuggable)
Log.d(TAG, n.toString());
}
}
// Request finalized without errors
setResult(RESULT_OK);
Log.i(TAG, "Retrieved " + numNotif + " notifications ("
+ notifCount + " unread)");
// Clean old notifications to control database size
numDeletedNotif = dbHelper.cleanOldNotificationsByAge(Constants.CLEAN_NOTIFICATIONS_THRESHOLD);
Log.i(TAG, "Deleted " + numDeletedNotif + " notifications from database");
dbHelper.endTransaction(true);
}
}
WorkRequest notifRequest =
new OneTimeWorkRequest.Builder(NotificationsWorker.class)
.build();
workManager.enqueue(notifRequest);
}
/*
@ -497,31 +413,29 @@ public class Notifications extends Module implements
notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
if (!SyncUtils.isSyncAutomatically(getApplicationContext())) {
if (notifCount > 0) {
notif = AlertNotificationFactory.createAlertNotification(getApplicationContext(),
getString(R.string.app_name),
notifCount + " "
+ getString(R.string.notificationsAlertMsg),
getString(R.string.app_name),
pendingIntent,
R.drawable.ic_launcher_swadroid_notif,
R.drawable.ic_launcher_swadroid,
true,
false,
false);
if (notifCount > 0) {
notif = AlertNotificationFactory.createAlertNotification(getApplicationContext(),
getString(R.string.app_name),
notifCount + " "
+ getString(R.string.notificationsAlertMsg),
getString(R.string.app_name),
pendingIntent,
R.drawable.ic_launcher_swadroid_notif,
R.drawable.ic_launcher_swadroid,
true,
false,
false);
AlertNotificationFactory.showAlertNotification(getApplicationContext(), notif, NOTIF_ALERT_ID);
} else {
Toast.makeText(this, R.string.NoNotificationsMsg,
Toast.LENGTH_LONG).show();
}
// Sends to SWAD the "seen notifications" info
sendReadNotifications();
refreshScreen();
AlertNotificationFactory.showAlertNotification(getApplicationContext(), notif, NOTIF_ALERT_ID);
} else {
Toast.makeText(this, R.string.NoNotificationsMsg,
Toast.LENGTH_LONG).show();
}
// Sends to SWAD the "seen notifications" info
sendReadNotifications();
refreshScreen();
}
/*
@ -560,15 +474,15 @@ public class Notifications extends Module implements
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(
NotificationsSyncAdapterService.START_SYNC)) {
NotificationsWorker.START_SYNC)) {
Log.i(TAG, "Started sync");
} else if (intent.getAction().equals(
NotificationsSyncAdapterService.STOP_SYNC)) {
NotificationsWorker.STOP_SYNC)) {
Log.i(TAG, "Stopped sync");
notifCount = intent.getIntExtra("notifCount", 0);
errorMessage = intent.getStringExtra("errorMessage");
if ((errorMessage != null) && !errorMessage.equals("")) {
if ((errorMessage != null) && !errorMessage.isEmpty()) {
error(errorMessage, null);
} else if (notifCount == 0) {
Toast.makeText(context, R.string.NoNotificationsMsg,

View File

@ -82,7 +82,7 @@ public class NotificationsMarkAllAsRead extends Module {
numMarkedNotifications = Integer.parseInt(soap.toString());
}
Log.i(TAG, "Marked " + numMarkedNotifications + " notifications as readed");
Log.i(TAG, "Marked " + numMarkedNotifications + " notifications as read");
dbHelper.updateAllNotifications("seenRemote", Utils.parseBoolString(true));
if(numMarkedNotifications != numMarkedNotificationsList) {

View File

@ -19,22 +19,16 @@
package es.ugr.swad.swadroid.modules.notifications;
import android.accounts.Account;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
import android.content.SyncResult;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import org.ksoap2.SoapFault;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.transport.HttpResponseException;
@ -64,222 +58,126 @@ import es.ugr.swad.swadroid.webservices.IWebserviceClient;
import es.ugr.swad.swadroid.webservices.SOAPClient;
/**
* Service for notifications sync adapter.
* Worker for notifications synchronization.
* @see <a href="https://openswad.org/ws/#getNotifications">getNotifications</a>
*
* @author Juan Miguel Boyero Corral <juanmi1982@gmail.com>
*/
public class NotificationsSyncAdapterService extends Service {
private static final String TAG = "NotificationsSyncAdapterService";
private static SyncAdapterImpl sSyncAdapter = null;
public class NotificationsWorker extends Worker {
private static final String TAG = "NotificationsWorker";
private static int notifCount;
private static DataBaseHelper dbHelper;
private static IWebserviceClient webserviceClient;
private static String METHOD_NAME = "";
private static Object result;
private static String errorMessage = "";
private static boolean isDebuggable;
private final Context mContext;
public static final String START_SYNC = "es.ugr.swad.swadroid.sync.start";
public static final String STOP_SYNC = "es.ugr.swad.swadroid.sync.stop";
public NotificationsSyncAdapterService() {
super();
}
public NotificationsWorker(
@NonNull Context context,
@NonNull WorkerParameters params) {
private static class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
private final Context mContext;
public SyncAdapterImpl(Context context) {
super(context, true);
mContext = context;
}
@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
int httpStatusCode;
try {
NotificationsSyncAdapterService.performSync(mContext);
//If synchronization was successful, update last synchronization time in preferences
Preferences.setLastSyncTime(System.currentTimeMillis());
} catch (Exception e) {
if (e.getClass() == SoapFault.class) {
SoapFault es = (SoapFault) e;
switch (es.faultstring) {
case "Bad log in":
errorMessage = mContext.getString(R.string.errorBadLoginMsg);
break;
case "Bad web service key":
errorMessage = mContext.getString(R.string.errorBadLoginMsg);
// Force logout and reset password (this will show again
// the login screen)
Login.setLogged(false);
Preferences.setUserPassword("");
break;
case "Unknown application key":
errorMessage = mContext.getString(R.string.errorBadAppKeyMsg);
break;
default:
errorMessage = "Server error: " + es.getMessage();
break;
}
} else if ((e.getClass() == TimeoutException.class) || (e.getClass() == SocketTimeoutException.class)) {
errorMessage = mContext.getString(R.string.errorTimeoutMsg);
} else if ((e.getClass() == CertificateException.class) || (e .getClass() == SSLException.class)) {
errorMessage = mContext.getString(R.string.errorServerCertificateMsg);
} else if (e.getClass() == HttpResponseException.class) {
httpStatusCode = ((HttpResponseException) e).getStatusCode();
Log.e(TAG, "httpStatusCode=" + httpStatusCode);
switch(httpStatusCode) {
case 500: errorMessage = mContext.getString(R.string.errorInternalServerMsg);
break;
case 503: errorMessage = mContext.getString(R.string.errorServiceUnavailableMsg);
break;
default: errorMessage = e.getMessage();
if ((errorMessage == null) || errorMessage.equals("")) {
errorMessage = mContext.getString(R.string.errorConnectionMsg);
}
}
} else if (e.getClass() == XmlPullParserException.class) {
errorMessage = mContext.getString(R.string.errorServerResponseMsg);
} else {
errorMessage = e.getMessage();
if ((errorMessage == null) || errorMessage.equals("")) {
errorMessage = mContext.getString(R.string.errorConnectionMsg);
}
}
// Launch database rollback
if(dbHelper.isDbInTransaction()) {
dbHelper.endTransaction(false);
}
Log.e(TAG, errorMessage, e);
//Notify synchronization stop
Intent stopIntent = new Intent();
stopIntent.setAction(STOP_SYNC);
stopIntent.putExtra("notifCount", notifCount);
stopIntent.putExtra("errorMessage", errorMessage);
mContext.sendBroadcast(stopIntent);
}
}
}
/* (non-Javadoc)
* @see android.app.Service#onCreate()
*/
@Override
public void onCreate() {
// Check if debug mode is enabled
try {
getPackageManager().getApplicationInfo(
getPackageName(), 0);
isDebuggable = (ApplicationInfo.FLAG_DEBUGGABLE != 0);
} catch (NameNotFoundException e) {
Log.e(TAG, "Error getting debuggable flag", e);
}
super(context, params);
this.mContext = context;
try {
dbHelper = new DataBaseHelper(this);
dbHelper = new DataBaseHelper(context);
//Initialize webservices client
webserviceClient = null;
} catch (Exception e) {
Log.e(TAG, "Error initializing database and preferences", e);
}
}
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
Log.i(TAG, "Starting persistent notification in custom foreground mode");
startCustomForeground();
} else {
Log.i(TAG, "Starting persistent notification in standard foreground mode");
startForeground(1, new Notification());
@NonNull
@Override
public Result doWork() {
int httpStatusCode;
try {
performSync(mContext);
//If synchronization was successful, update last synchronization time in preferences
Preferences.setLastSyncTime(System.currentTimeMillis());
} catch (Exception e) {
if (e.getClass() == SoapFault.class) {
SoapFault es = (SoapFault) e;
switch (es.faultstring) {
case "Bad log in":
errorMessage = mContext.getString(R.string.errorBadLoginMsg);
break;
case "Bad web service key":
errorMessage = mContext.getString(R.string.errorBadLoginMsg);
// Force logout and reset password (this will show again
// the login screen)
Login.setLogged(false);
Preferences.setUserPassword("");
break;
case "Unknown application key":
errorMessage = mContext.getString(R.string.errorBadAppKeyMsg);
break;
default:
errorMessage = "Server error: " + es.getMessage();
break;
}
} else if ((e.getClass() == TimeoutException.class) || (e.getClass() == SocketTimeoutException.class)) {
errorMessage = mContext.getString(R.string.errorTimeoutMsg);
} else if ((e.getClass() == CertificateException.class) || (e .getClass() == SSLException.class)) {
errorMessage = mContext.getString(R.string.errorServerCertificateMsg);
} else if (e.getClass() == HttpResponseException.class) {
httpStatusCode = ((HttpResponseException) e).getStatusCode();
Log.e(TAG, "httpStatusCode=" + httpStatusCode);
switch(httpStatusCode) {
case 500: errorMessage = mContext.getString(R.string.errorInternalServerMsg);
break;
case 503: errorMessage = mContext.getString(R.string.errorServiceUnavailableMsg);
break;
default: errorMessage = e.getMessage();
if ((errorMessage == null) || errorMessage.isEmpty()) {
errorMessage = mContext.getString(R.string.errorConnectionMsg);
}
}
} else if (e.getClass() == XmlPullParserException.class) {
errorMessage = mContext.getString(R.string.errorServerResponseMsg);
} else {
errorMessage = e.getMessage();
if ((errorMessage == null) || errorMessage.isEmpty()) {
errorMessage = mContext.getString(R.string.errorConnectionMsg);
}
}
// Launch database rollback
if(dbHelper.isDbInTransaction()) {
dbHelper.endTransaction(false);
}
Log.e(TAG, errorMessage, e);
}
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("restartNotificationsService");
broadcastIntent.setClass(this, RestarterNotificationsReceiver.class);
this.sendBroadcast(broadcastIntent);
}
private void startCustomForeground() {
Intent notificationIntent = new Intent(this, Notifications.class);
PendingIntent pendingIntent;
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//single top to avoid creating many activity stacks queue
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
pendingIntent = PendingIntent.getActivity(this,
0,
notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
Notification notification = AlertNotificationFactory.createBackgroundNotification(
this,
getString(R.string.appRunningBackground),
R.drawable.ic_launcher_swadroid_notif,
R.drawable.ic_launcher_swadroid,
pendingIntent);
startForeground(2, notification);
}
/* (non-Javadoc)
* @see android.app.Service#onStartCommand(android.content.Intent, int, int)
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_REDELIVER_INTENT;
}
@Override
public IBinder onBind(Intent intent) {
return getSyncAdapter().getSyncAdapterBinder();
}
/* (non-Javadoc)
* @see android.app.Service#onUnbind(android.content.Intent)
*/
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
private SyncAdapterImpl getSyncAdapter() {
if (sSyncAdapter == null)
sSyncAdapter = new SyncAdapterImpl(this);
return sSyncAdapter;
return Result.success();
}
/**
* Creates webservice request.
*/
private static void createRequest(String clientType) {
if(webserviceClient == null) {
if(clientType.equals(SOAPClient.CLIENT_TYPE)) {
webserviceClient = new SOAPClient();
}
if(webserviceClient == null) {
if(clientType.equals(SOAPClient.CLIENT_TYPE)) {
webserviceClient = new SOAPClient();
}
}
}
webserviceClient.setMETHOD_NAME(METHOD_NAME);
webserviceClient.createRequest();
webserviceClient.createRequest();
}
/**
@ -289,7 +187,7 @@ public class NotificationsSyncAdapterService extends Service {
* @param value Parameter value.
*/
private static void addParam(String param, Object value) {
webserviceClient.addParam(param, value);
webserviceClient.addParam(param, value);
}
/**
@ -301,12 +199,12 @@ public class NotificationsSyncAdapterService extends Service {
* @throws Exception
*/
private static void sendRequest(Class<?> cl, boolean simple) throws Exception {
((SOAPClient) webserviceClient).sendRequest(cl, simple);
result = webserviceClient.getResult();
((SOAPClient) webserviceClient).sendRequest(cl, simple);
result = webserviceClient.getResult();
}
private static void logUser() throws Exception {
Log.d(TAG, "Not logged");
Log.d(TAG, "Not logged");
METHOD_NAME = "loginByUserPasswordKey";
@ -344,7 +242,7 @@ public class NotificationsSyncAdapterService extends Service {
private static void getNotifications() throws Exception {
int numDeletedNotif;
Log.d(TAG, "Logged");
Log.d(TAG, "Logged");
//Calculates next timestamp to be requested
Long timestamp = Long.valueOf(dbHelper.getFieldOfLastNotification("eventTime"));
@ -368,7 +266,7 @@ public class NotificationsSyncAdapterService extends Service {
notifCount = 0;
for (int i = 0; i < numNotif; i++) {
SoapObject pii = (SoapObject) soap.getProperty(i);
SoapObject pii = (SoapObject) soap.getProperty(i);
long notifCode = Long.parseLong(pii.getProperty("notifCode").toString());
long eventCode = Long.parseLong(pii.getProperty("eventCode").toString());
String eventType = pii.getProperty("eventType").toString();
@ -416,40 +314,38 @@ public class NotificationsSyncAdapterService extends Service {
/**
* Sends to SWAD the "seen notifications" info
*/
private static void sendReadedNotifications(Context context) {
private static void sendReadNotifications(Context context) {
List<Model> markedNotificationsList;
String seenNotifCodes;
Intent activity;
int numMarkedNotificationsList;
//Construct a list of seen notifications in state "pending to mark as read in SWAD"
//Construct a list of seen notifications in state "pending to mark as read in SWAD"
markedNotificationsList = dbHelper.getAllRows(DataBaseHelper.DB_TABLE_NOTIFICATIONS,
"seenLocal='" + Utils.parseBoolString(true)
+ "' AND seenRemote='" + Utils.parseBoolString(false) + "'", null);
"seenLocal='" + Utils.parseBoolString(true)
+ "' AND seenRemote='" + Utils.parseBoolString(false) + "'", null);
numMarkedNotificationsList = markedNotificationsList.size();
if(isDebuggable)
Log.d(TAG, "numMarkedNotificationsList=" + numMarkedNotificationsList);
Log.d(TAG, "numMarkedNotificationsList=" + numMarkedNotificationsList);
if(numMarkedNotificationsList > 0) {
//Creates a string of notification codes separated by commas from the previous list
seenNotifCodes = Utils.getSeenNotificationCodes(markedNotificationsList);
if(isDebuggable)
Log.d(TAG, "seenNotifCodes=" + seenNotifCodes);
Log.d(TAG, "seenNotifCodes=" + seenNotifCodes);
//Sends "seen notifications" info to the server
activity = new Intent(context, NotificationsMarkAllAsRead.class);
activity.putExtra("seenNotifCodes", seenNotifCodes);
activity.putExtra("numMarkedNotificationsList", numMarkedNotificationsList);
activity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(activity);
activity = new Intent(context, NotificationsMarkAllAsRead.class);
activity.putExtra("seenNotifCodes", seenNotifCodes);
activity.putExtra("numMarkedNotificationsList", numMarkedNotificationsList);
activity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(activity);
}
}
private static void performSync(Context context)
throws Exception {
Notification notif;
Notification notif;
Intent notificationIntent = new Intent(context, Notifications.class);
PendingIntent pendingIntent;
@ -469,35 +365,35 @@ public class NotificationsSyncAdapterService extends Service {
//If last login time > Global.RELOGIN_TIME, force login
if (Login.isLogged() &&
((System.currentTimeMillis() - Login.getLastLoginTime()) > Login.RELOGIN_TIME)) {
((System.currentTimeMillis() - Login.getLastLoginTime()) > Login.RELOGIN_TIME)) {
Login.setLogged(false);
}
if (!Login.isLogged()) {
logUser();
logUser();
}
if (Login.isLogged()) {
getNotifications();
getNotifications();
if (notifCount > 0) {
notif = AlertNotificationFactory.createAlertNotification(context,
context.getString(R.string.app_name),
notifCount + " "
+ context.getString(R.string.notificationsAlertMsg),
context.getString(R.string.app_name),
pendingIntent,
R.drawable.ic_launcher_swadroid_notif,
R.drawable.ic_launcher_swadroid,
true,
false,
false);
if (notifCount > 0) {
notif = AlertNotificationFactory.createAlertNotification(context,
context.getString(R.string.app_name),
notifCount + " "
+ context.getString(R.string.notificationsAlertMsg),
context.getString(R.string.app_name),
pendingIntent,
R.drawable.ic_launcher_swadroid_notif,
R.drawable.ic_launcher_swadroid,
true,
false,
false);
AlertNotificationFactory.showAlertNotification(context, notif, Notifications.NOTIF_ALERT_ID);
}
AlertNotificationFactory.showAlertNotification(context, notif, Notifications.NOTIF_ALERT_ID);
}
sendReadedNotifications(context);
sendReadNotifications(context);
}
//Notify synchronization stop

View File

@ -1,48 +0,0 @@
/*
* This file is part of SWADroid.
*
* Copyright (C) 2010 Juan Miguel Boyero Corral <juanmi1982@gmail.com>
*
* SWADroid is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SWADroid 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with SWADroid. If not, see <http://www.gnu.org/licenses/>.
*/
package es.ugr.swad.swadroid.modules.notifications;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
/**
* Restarter for NotificationsSyncAdapterService service.
*
* @author Juan Miguel Boyero Corral <juanmi1982@gmail.com>
*/
public class RestarterNotificationsReceiver extends BroadcastReceiver {
private static final String TAG = "RestarterNotificationsReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "Service tried to stop");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(new Intent(context, NotificationsSyncAdapterService.class));
} else {
context.startService(new Intent(context, NotificationsSyncAdapterService.class));
}
Log.i(TAG, "Service restarted");
}
}

View File

@ -21,9 +21,10 @@ package es.ugr.swad.swadroid.preferences;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.util.Log;
import androidx.work.WorkManager;
import com.google.gson.Gson;
import java.security.NoSuchAlgorithmException;
@ -34,7 +35,6 @@ import es.ugr.swad.swadroid.database.DataBaseHelper;
import es.ugr.swad.swadroid.model.LoginInfo;
import es.ugr.swad.swadroid.modules.courses.Courses;
import es.ugr.swad.swadroid.modules.login.Login;
import es.ugr.swad.swadroid.sync.SyncUtils;
import es.ugr.swad.swadroid.utils.Crypto;
/**
@ -143,6 +143,10 @@ public class Preferences {
* Sharing location preference
*/
public static String SHARELOCATION = "prefShareLocation";
/**
* Tag for WorkerManager
*/
public static final String NOTIF_SYNC_WORKER_TAG = "notifSync";
/**
* Gets application preferences
* @param ctx Application context
@ -162,13 +166,13 @@ public class Preferences {
public Preferences(Context ctx) {
getPreferences(ctx);
editor = prefs.edit();
editor = prefs.edit();
if(dbHelper == null) {
try {
dbHelper = new DataBaseHelper(ctx);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
Log.e(TAG, e.getMessage(), e);
}
}
}
@ -222,7 +226,7 @@ public class Preferences {
* @param server Server URL
*/
public static void setServer(String server) {
editor = editor.putString(SERVERPREF, server);
editor = editor.putString(SERVERPREF, server);
editor.commit();
}
@ -403,6 +407,8 @@ public class Preferences {
}
public static void logoutClean(Context context, String key) {
WorkManager workManager = WorkManager.getInstance(context);
Login.getLoginInfo().setLogged(false);
removeLoginInfo();
initializeSelectedCourse();
@ -411,7 +417,7 @@ public class Preferences {
setPreferencesChanged();
if(isSyncEnabled()) {
SyncUtils.removePeriodicSync(Constants.AUTHORITY, Bundle.EMPTY, context);
workManager.cancelAllWorkByTag(NOTIF_SYNC_WORKER_TAG);
}
Log.i(TAG, "Forced logout due to " + key + " change in preferences");

View File

@ -36,17 +36,22 @@ import android.preference.PreferenceScreen;
import android.util.Log;
import android.widget.Toast;
import androidx.work.PeriodicWorkRequest;
import androidx.work.WorkManager;
import androidx.work.WorkRequest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import es.ugr.swad.swadroid.Constants;
import es.ugr.swad.swadroid.R;
import es.ugr.swad.swadroid.gui.DialogFactory;
import es.ugr.swad.swadroid.modules.login.Login;
import es.ugr.swad.swadroid.modules.login.LoginActivity;
import es.ugr.swad.swadroid.sync.SyncUtils;
import es.ugr.swad.swadroid.modules.notifications.NotificationsWorker;
import es.ugr.swad.swadroid.utils.Crypto;
import es.ugr.swad.swadroid.utils.Utils;
@ -115,6 +120,11 @@ public class PreferencesActivity extends PreferenceActivity implements OnPrefere
*/
private static Preference syncTimeLocationPref;
/**
* WorkManager for synchronization of notifications
*/
private WorkManager workManager;
/**
* Shows an error message.
*
@ -228,6 +238,8 @@ public class PreferencesActivity extends PreferenceActivity implements OnPrefere
} catch (PackageManager.NameNotFoundException ex) {
error(ex.getMessage(), ex);
}
workManager = WorkManager.getInstance(ctx);
}
/* (non-Javadoc)
@ -347,6 +359,8 @@ public class PreferencesActivity extends PreferenceActivity implements OnPrefere
*/
@Override
protected void onPause() {
String syncTime = Preferences.getSyncTime();
super.onPause();
//Set final password
@ -356,10 +370,13 @@ public class PreferencesActivity extends PreferenceActivity implements OnPrefere
//Reconfigure automatic synchronization
if(syncPrefsChanged) {
SyncUtils.removePeriodicSync(Constants.AUTHORITY, Bundle.EMPTY, ctx);
if (!Preferences.getSyncTime().equals("0") && Preferences.isSyncEnabled()) {
SyncUtils.addPeriodicSync(Constants.AUTHORITY, Bundle.EMPTY,
Long.parseLong(Preferences.getSyncTime()), ctx);
workManager.cancelAllWorkByTag(Preferences.NOTIF_SYNC_WORKER_TAG);
if (!syncTime.equals("0") && Preferences.isSyncEnabled()) {
WorkRequest notifRequest =
new PeriodicWorkRequest.Builder(NotificationsWorker.class,
Long.parseLong(syncTime), TimeUnit.MILLISECONDS)
.build();
workManager.enqueue(notifRequest);
}
}
}

View File

@ -1,58 +0,0 @@
package es.ugr.swad.swadroid.sync;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorActivity;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.content.ContentResolver;
import android.os.Bundle;
import android.util.Log;
import es.ugr.swad.swadroid.Constants;
import es.ugr.swad.swadroid.R;
public class AccountAuthenticator extends AccountAuthenticatorActivity {
/**
* Login tag name for Logcat
*/
private static final String TAG = Constants.APP_TAG + " AccountAuthenticator";
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
Account account = new Account(getString(R.string.app_name), Constants.ACCOUNT_TYPE);
AccountManager am = AccountManager.get(this);
boolean accountCreated = am.addAccountExplicitly(account, getString(R.string.app_name), null);
Bundle extras = getIntent().getExtras();
Log.d(TAG, "accountCreated=" + accountCreated);
Log.d(TAG, "extras=" + extras);
if (accountCreated) { //Pass the new account back to the account manager
if (extras != null) {
AccountAuthenticatorResponse response = extras.getParcelable(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
response.onResult(result);
}
Log.i(TAG, "Account for automatic synchronization created successfully");
} else {
Log.w(TAG, "Account for automatic synchronization was not created");
}
//Configure automatic synchronization
ContentResolver.setIsSyncable(account, Constants.AUTHORITY, 1);
Log.i(TAG, "Account setted as syncable");
//ContentResolver.setMasterSyncAutomatically(true);
//Log.i(TAG, "Master auto-sync setting enabled");
//SyncUtils.addPeriodicSync(Constants.AUTHORITY, Bundle.EMPTY, Long.valueOf(Preferences.getSyncTime()), this);
finish();
}
}

View File

@ -1,89 +0,0 @@
package es.ugr.swad.swadroid.sync;
import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.accounts.NetworkErrorException;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
/**
* Authenticator service that returns a subclass of AbstractAccountAuthenticator in onBind()
*/
public class AccountAuthenticatorService extends Service {
private static AccountAuthenticatorImpl sAccountAuthenticator = null;
public AccountAuthenticatorService() {
super();
}
public IBinder onBind(Intent intent) {
IBinder ret = null;
if (intent.getAction().equals(android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT))
ret = getAuthenticator().getIBinder();
return ret;
}
private AccountAuthenticatorImpl getAuthenticator() {
if (sAccountAuthenticator == null)
sAccountAuthenticator = new AccountAuthenticatorImpl(this);
return sAccountAuthenticator;
}
private static class AccountAuthenticatorImpl extends AbstractAccountAuthenticator {
private final Context mContext;
public AccountAuthenticatorImpl(Context context) {
super(context);
mContext = context;
}
/*
* The user has requested to add a new account to the system. We return an intent that will launch our login screen if the user has not logged in yet,
* otherwise our activity will just pass the user's credentials on to the account manager.
*/
@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options)
throws NetworkErrorException {
final Intent intent = new Intent(mContext, AccountAuthenticator.class);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) {
return null;
}
@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
return null;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public String getAuthTokenLabel(String authTokenType) {
return null;
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
return null;
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) {
return null;
}
}
}

View File

@ -1,65 +0,0 @@
/*
* This file is part of SWADroid.
*
* Copyright (C) 2010 Juan Miguel Boyero Corral <juanmi1982@gmail.com>
*
* SWADroid is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SWADroid 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with SWADroid. If not, see <http://www.gnu.org/licenses/>.
*/
package es.ugr.swad.swadroid.sync;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
/**
* Dummy provider for sync adapter.
*
* @author Juan Miguel Boyero Corral <juanmi1982@gmail.com>
*/
public class DummyProvider extends ContentProvider {
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
@Override
public boolean onCreate() {
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
return 0;
}
}

View File

@ -1,56 +0,0 @@
/*
* This file is part of SWADroid.
*
* Copyright (C) 2010 Juan Miguel Boyero Corral <juanmi1982@gmail.com>
*
* SWADroid is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SWADroid 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with SWADroid. If not, see <http://www.gnu.org/licenses/>.
*/
package es.ugr.swad.swadroid.sync;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import es.ugr.swad.swadroid.Constants;
/**
* Class for launch a periodic sync request
*
* @author Juan Miguel Boyero Corral <juanmi1982@gmail.com>
*/
public final class PeriodicSyncReceiver extends BroadcastReceiver {
private static Intent createIntent(Context context, String authority) {
Intent intent = new Intent(context, PeriodicSyncReceiver.class);
intent.putExtra(Constants.AUTHORITY, authority);
return intent;
}
public static PendingIntent createPendingIntent(Context context, String authority, Bundle extras) {
int requestCode = 0;
Intent intent = createIntent(context, authority);
int flags = PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE;
return PendingIntent.getBroadcast(context, requestCode, intent, flags);
}
@Override
public void onReceive(Context context, Intent intent) {
String authority = intent.getStringExtra(Constants.AUTHORITY);
ContentResolver.requestSync(null, authority, new Bundle());
}
}

View File

@ -1,97 +0,0 @@
/*
* This file is part of SWADroid.
*
* Copyright (C) 2010 Juan Miguel Boyero Corral <juanmi1982@gmail.com>
*
* SWADroid is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SWADroid 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with SWADroid. If not, see <http://www.gnu.org/licenses/>.
*/
package es.ugr.swad.swadroid.sync;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.ContentResolver;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import es.ugr.swad.swadroid.Constants;
/**
* Class for manage a periodic sync request
*
* @author Juan Miguel Boyero Corral <juanmi1982@gmail.com>
*/
public class SyncUtils {
/**
* Login tag name for Logcat
*/
private static final String TAG = Constants.APP_TAG + " SyncUtils";
public static void addPeriodicSync(String authority, Bundle extras, long frequency, Context context) {
AccountManager am = AccountManager.get(context);
Account[] accounts = am.getAccountsByType(Constants.ACCOUNT_TYPE);
Log.d(TAG, "[addPeriodicSync] Number of accounts with type " + Constants.ACCOUNT_TYPE + " = " + accounts.length);
for (Account a : accounts) {
ContentResolver.setSyncAutomatically(a, Constants.AUTHORITY, true);
ContentResolver.addPeriodicSync(a, authority, extras, frequency * 60);
Log.i(TAG, "Added periodic synchronization with pollFrequency=" + (frequency * 60)
+ " for account " + a.toString());
}
}
public static void removePeriodicSync(String authority, Bundle extras, Context context) {
AccountManager am = AccountManager.get(context);
Account[] accounts = am.getAccountsByType(Constants.ACCOUNT_TYPE);
Log.d(TAG, "[removePeriodicSync] Number of accounts with type " + Constants.ACCOUNT_TYPE + " = " + accounts.length);
for (Account a : accounts) {
ContentResolver.setSyncAutomatically(a, Constants.AUTHORITY, false);
ContentResolver.removePeriodicSync(a, authority, extras);
Log.i(TAG, "Removed periodic synchronization for account " + a.toString());
}
}
public static boolean isSyncAutomatically(Context context) {
boolean isSyncAutomatically = true;
AccountManager am = AccountManager.get(context);
Account[] accounts = am.getAccountsByType(Constants.ACCOUNT_TYPE);
Log.d(TAG, "[isSyncAutomatically] Number of accounts with type " + Constants.ACCOUNT_TYPE + " = " + accounts.length);
for (Account a : accounts) {
if (!ContentResolver.getMasterSyncAutomatically()
|| !ContentResolver.getSyncAutomatically(a, Constants.AUTHORITY)) {
isSyncAutomatically = false;
}
}
return isSyncAutomatically;
}
public static boolean isPeriodicSynced(Context context) {
boolean isPeriodicSynced = false;
AccountManager am = AccountManager.get(context);
Account[] accounts = am.getAccountsByType(Constants.ACCOUNT_TYPE);
isPeriodicSynced = (accounts.length > 0);
Log.d(TAG, "[isPeriodicSynced] Number of accounts with type " + Constants.ACCOUNT_TYPE + " = " + accounts.length);
return isPeriodicSynced;
}
}

View File

@ -1,7 +0,0 @@
<html>
<head>
</head>
<body>
Syncronization package.
</body>
</html>

View File

@ -15,7 +15,6 @@
<item>Nº real</item>
</string-array>
<string-array name="prefSyncTimeEntries">
<item>5 minutos</item>
<item>15 minutos</item>
<item>30 minutos</item>
<item>1 hora</item>
@ -32,13 +31,5 @@
<item>30 minutos</item>
<item>1 hora</item>
</string-array>
<string-array name="prefSyncLocationTimeValues">
<item>1</item>
<item>2</item>
<item>5</item>
<item>15</item>
<item>30</item>
<item>60</item>
</string-array>
</resources>

View File

@ -24,7 +24,6 @@
<item>Float</item>
</string-array>
<string-array name="prefSyncTimeEntries">
<item>5 minutes</item>
<item>15 minutes</item>
<item>30 minutes</item>
<item>1 hour</item>
@ -34,7 +33,6 @@
<item>24 hours</item>
</string-array>
<string-array name="prefSyncTimeValues">
<item>5</item>
<item>15</item>
<item>30</item>
<item>60</item>