Migrate synchronization of notifications to a Worker process
This commit is contained in:
parent
b8337b00f6
commit
a344934961
|
@ -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'
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
Syncronization package.
|
||||
</body>
|
||||
</html>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue
Block a user