Commit ff00deb7 authored by Lucas Braz Cunha's avatar Lucas Braz Cunha

AGILE#307: [WP] Implementing service to update local list of notifications

Signed-off-by: Lucas Braz Cunha's avatarLucas B. Cunha <lbc16@inf.ufpr.br>
parent 0f5cdf58
......@@ -58,6 +58,15 @@
</intent-filter>
</service>
<service
android:name=".services.notification.NotificationsListService"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE">
<intent-filter>
<action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE" />
</intent-filter>
</service>
<receiver android:name=".services.BootReceiver">
<intent-filter>
......
......@@ -12,6 +12,7 @@ import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextWatcher;
import android.text.style.StyleSpan;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
......@@ -19,6 +20,14 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.firebase.jobdispatcher.Constraint;
import com.firebase.jobdispatcher.FirebaseJobDispatcher;
import com.firebase.jobdispatcher.GooglePlayDriver;
import com.firebase.jobdispatcher.Job;
import com.firebase.jobdispatcher.Lifetime;
import com.firebase.jobdispatcher.RetryStrategy;
import com.firebase.jobdispatcher.Trigger;
import java.io.IOException;
import br.ufpr.c3sl.agendador.agendador.helpers.ConnectionChecker;
......@@ -28,6 +37,7 @@ import br.ufpr.c3sl.agendador.agendador.helpers.UserImgHelper;
import br.ufpr.c3sl.agendador.agendador.helpers.Utils;
import br.ufpr.c3sl.agendador.agendador.presenters.LoginPresenter;
import br.ufpr.c3sl.agendador.agendador.presenters.PresenterManager;
import br.ufpr.c3sl.agendador.agendador.services.notification.NotificationsListService;
import br.ufpr.c3sl.agendador.agendador.views.LoginView;
/**
......@@ -205,7 +215,29 @@ public class LoginActivity extends AppCompatActivity implements LoginView {
@Override
public void afterSuccessfulLogin() {
// TODO 22/12/2017: instanciar job que verifica notificações no back-end.
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(
new GooglePlayDriver(this)
);
Job job = dispatcher.newJobBuilder()
.setService(NotificationsListService.class)
.setTag(Utils.NOTIFICATION_UPDATE_SERVICE)
//time the function gets is in seconds, 60s = 1 minutes, 1 hour = 3600
//will be repeated every 10 minutes
.setTrigger(Trigger.executionWindow(/*600, 720*/1, 29))
//will be retried if it fails.
.setRecurring(true)
.setRetryStrategy(RetryStrategy.DEFAULT_LINEAR)
.setConstraints(
// only run on network
Constraint.ON_ANY_NETWORK
)
//if device is rebooted the job will be restarted
.setLifetime(Lifetime.FOREVER)
.build();
dispatcher.mustSchedule(job);
Log.d("AGNDDR-NotifiUpdate", "Job agendado para atualizar lista de coisas");
Intent intent = new Intent(LoginActivity.this, HomeActivity.class);
startActivity(intent);
finish();
......
......@@ -383,9 +383,8 @@ public class ScheduleConfirmationActivity extends AppCompatActivity implements S
notification.scheduleId = schedule.getId();
notification.emailSent = false;
notification.description = getResources().getString(R.string.notification_content, schedule.getServiceTypeName(),
schedule.getLocationName(), schedule.getAddressStreet(), schedule.getAddressNumber(),
sdfDate.format(schedule.getStartTime()), sdfHour.format(schedule.getStartTime()));
notification.description = getResources().getString(R.string.notification_content, sdfDate.format(schedule.getStartTime()), sdfHour.format(schedule.getStartTime()), schedule.getServiceTypeName(),
schedule.getLocationName(), schedule.getAddressStreet(), schedule.getAddressNumber());
// TODO: 15/01/18 Apagar aqui o tempo a mais que está sendo usado no calculo da notificaçõa, está em dobro.
notification.reminderTime = sdfServer.format(schedule.getStartTime().getTime() - (reminderTimeArray[sp_notification.getSelectedItemPosition()]) - (reminderTimeArray[sp_notification.getSelectedItemPosition()]));
......
......@@ -322,9 +322,8 @@ public class ScheduleInfoActivity extends AppCompatActivity implements Navigatio
builder.setMessage(R.string.signout_confirmation)
.setPositiveButton(getString(R.string.yes), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
SchedulesActivity activity = (SchedulesActivity) getActivity();
// TODO: 05/10/17 fix here
//activity.presenter.onSignOutClicked();
ScheduleInfoActivity activity = (ScheduleInfoActivity) getActivity();
activity.presenter.onSignOutClicked();
}
})
.setNegativeButton(getString(R.string.no), new DialogInterface.OnClickListener() {
......
......@@ -122,6 +122,7 @@ public abstract class Utils {
public static final String NOTIFICATION = "notification";
public static final String NOTIFICATION_SERVICE = "br.c3sl.ufpr.notification_service";
public static final String NOTIFICATION_UPDATE_SERVICE = "br.c3sl.ufpr.notification_update_service";
public static final String NOTIFICATION_LIST = "notification_list";
......
......@@ -98,4 +98,8 @@ public interface ApiEndpoints {
@GET("schedules/{id_schedule}?permission=citizen")
Call<ScheduleInfo> requestScheduleInfo(@Path("id_schedule") long id);
@GET("notifications?permission=citizen")
Call<List<NotificationReturn>> requestNotifications();
}
\ No newline at end of file
......@@ -77,13 +77,13 @@ public abstract class BasePresenter<V> {
osb.edit().putString(Utils.UID, headers.get(Utils.UID)).apply();
if(headers.get(Utils.EXPIRY) != null)
osb.edit().putString(Utils.EXPIRY, headers.get(Utils.EXPIRY)).apply();
}
public void onSignOutClicked() {
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context));
dispatcher.cancel(Utils.SERVICE_UPDATE_IMAGE);
dispatcher.cancel(Utils.NOTIFICATION_UPDATE_SERVICE);
if(osb == null)
osb = ObscuredSharedPreferences.getPrefs(context, "Agendador", Context.MODE_PRIVATE);
......
......@@ -113,7 +113,7 @@ public class ScheduleConfirmationPresenter extends BasePresenter<ScheduleConfirm
final ApiEndpoints service = ApiUtils.request(header);
final Call<NotificationReturn> listCall = service.requestCreateNotification(notification);
scheduleNotificationJob(notification);
scheduleConfirmationPresenter.view().setProgressBar(true);
listCall.enqueue(new Callback<NotificationReturn>() {
......@@ -162,11 +162,10 @@ public class ScheduleConfirmationPresenter extends BasePresenter<ScheduleConfirm
Job job = dispatcher.newJobBuilder()
.setService(NotificationCreateService.class)
.setTag(Utils.NOTIFICATION_SERVICE)
.setTag(Utils.NOTIFICATION_SERVICE + notification.scheduleId)
//time the function gets is in seconds, 60s = 1 minutes, 1 hour = 3600
.setTrigger(Trigger.executionWindow(10, 3600))
.setTrigger(Trigger.NOW)
//will be retried if it fails.
.setRecurring(true)
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
.setConstraints(
// only run on network
......
......@@ -9,6 +9,7 @@ import com.firebase.jobdispatcher.JobService;
import com.google.gson.Gson;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import br.ufpr.c3sl.agendador.agendador.helpers.ObscuredSharedPreferences;
......@@ -32,54 +33,58 @@ public class NotificationCreateService extends JobService {
@Override
public boolean onStartJob(final JobParameters job) {
Log.v("AGNDDR-NotifiCreate", "Rodando " + NotificationCreateService.class.getName());
ObscuredSharedPreferences osb = ObscuredSharedPreferences.getPrefs(getBaseContext(), "Agendador", Context.MODE_PRIVATE);
final Bundle extras = job.getExtras();
Map<String, String> header = new HashMap<>();
final String uid = osb.getString(Utils.UID, null);
header.put("Content-Type", "application/json");
header.put(Utils.ACCESS_TOKEN, osb.getString(Utils.ACCESS_TOKEN, null));
header.put(Utils.CLIENT, osb.getString(Utils.CLIENT, null));
header.put(Utils.ID, uid);
new Thread(new Runnable() {
public void run() {
Notification notification = null;
final Bundle extras = job.getExtras();
Map<String, String> header = new HashMap<>();
ObscuredSharedPreferences osb = ObscuredSharedPreferences.getPrefs(getBaseContext(), "Agendador", Context.MODE_PRIVATE);
header.put("Content-Type", "application/json");
header.put(Utils.ACCESS_TOKEN, osb.getString(Utils.ACCESS_TOKEN, null));
header.put(Utils.CLIENT, osb.getString(Utils.CLIENT, null));
header.put(Utils.UID, osb.getString(Utils.UID, null));
if (extras != null) {
notification = new Gson().fromJson(extras.getString(Utils.NOTIFICATION), Notification.class);
}else{
Log.e("AGNDDR-NotifiCreate", "Job não recebeu os extras.");
jobFinished(job, true);
}
Notification notification = null;
ApiEndpoints service = ApiUtils.request(header);
if (extras != null) {
notification = new Gson().fromJson(extras.getString(Utils.NOTIFICATION), Notification.class);
} else {
Log.e("AGNDDR-NotifiCreate", "Job não recebeu os extras.");
jobFinished(job, true);
}
requestCreateNotification(header, job, osb, notification);
}}).start();
return true;
}
private void requestCreateNotification(final Map<String, String> header, final JobParameters job, final ObscuredSharedPreferences osb, final Notification notification){
final ApiEndpoints service = ApiUtils.request(header);
Call<NotificationReturn> listCall = service.requestCreateNotification(notification);
final Notification finalNotification = notification;
listCall.enqueue(new Callback<NotificationReturn>() {
@Override
public void onResponse(Call<NotificationReturn> call, Response<NotificationReturn> response) {
int status = response.code();
Headers headers = response.headers();
ObscuredSharedPreferences osb = ObscuredSharedPreferences.getPrefs(getBaseContext(), "Agendador", Context.MODE_PRIVATE);
if(headers.get(Utils.ACCESS_TOKEN) != null)
osb.edit().putString(Utils.ACCESS_TOKEN, headers.get(Utils.ACCESS_TOKEN)).apply();
if(headers.get(Utils.CLIENT) != null)
osb.edit().putString(Utils.CLIENT, headers.get(Utils.CLIENT)).apply();
if(headers.get(Utils.UID) != null)
osb.edit().putString(Utils.UID, headers.get(Utils.UID)).apply();
if(headers.get(Utils.EXPIRY) != null)
osb.edit().putString(Utils.EXPIRY, headers.get(Utils.EXPIRY)).apply();
int status = response.code();
updateHeaders(headers, osb);
switch (status) {
case 200:
LocalNotificationManager.createLocalNotification(getApplicationContext(), finalNotification, osb, new Gson());
case 201:
Log.d("Server response", getClass().getName() + ": 200 - Sucesso!");
LocalNotificationManager.createLocalNotification(getApplicationContext(), notification, osb, new Gson());
jobFinished(job, false);
break;
default:
Log.v("AGNDDR-NotifiCreate", "Job foi executado mas recebeu retorno " + status);
Log.e("Server response", getClass().getName() + ": ERRO:" + status);
jobFinished(job, true);
break;
}
......@@ -88,17 +93,28 @@ public class NotificationCreateService extends JobService {
@Override
public void onFailure(Call<NotificationReturn> call, Throwable t) {
Log.v("AGNDDR-NotifiCreate", "Job falhou e será remarcado!!");
Log.e("Server response", getClass().getName() + ": Requisição falhou!!");
t.printStackTrace();
jobFinished(job, true);
}
});
}
private void updateHeaders(Headers headers, ObscuredSharedPreferences osb){
if (headers.get(Utils.ACCESS_TOKEN) != null)
osb.edit().putString(Utils.ACCESS_TOKEN, headers.get(Utils.ACCESS_TOKEN)).apply();
if (headers.get(Utils.CLIENT) != null)
osb.edit().putString(Utils.CLIENT, headers.get(Utils.CLIENT)).apply();
if (headers.get(Utils.UID) != null)
osb.edit().putString(Utils.UID, headers.get(Utils.UID)).apply();
if (headers.get(Utils.EXPIRY) != null)
osb.edit().putString(Utils.EXPIRY, headers.get(Utils.EXPIRY)).apply();
return true;
}
@Override
public boolean onStopJob(JobParameters job) {
return true;
......
......@@ -44,6 +44,7 @@ public class NotificationReceiver extends BroadcastReceiver{
break;
}
}
// TODO: 19/01/18 só remover a notificação depois de enviar para o servidor que ela foi mostrada.
LocalNotificationManager.removeNotification(id, osb, gson);
NotificationManager notificationManager = (NotificationManager)
context.getSystemService(NOTIFICATION_SERVICE);
......
package br.ufpr.c3sl.agendador.agendador.services.notification;
import android.content.Context;
import android.os.Looper;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.SparseArray;
import android.util.SparseLongArray;
import com.firebase.jobdispatcher.JobParameters;
import com.firebase.jobdispatcher.JobService;
import com.google.gson.Gson;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import br.ufpr.c3sl.agendador.agendador.helpers.ObscuredSharedPreferences;
import br.ufpr.c3sl.agendador.agendador.helpers.Utils;
import br.ufpr.c3sl.agendador.agendador.models.Notification;
import br.ufpr.c3sl.agendador.agendador.models.NotificationReturn;
import br.ufpr.c3sl.agendador.agendador.network.ApiEndpoints;
import br.ufpr.c3sl.agendador.agendador.network.ApiUtils;
import okhttp3.Headers;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
/**
* Created by Lucas B. Cunha on 17/01/18.
*
* Service to keep local list of notifications synchronized.
*/
public class NotificationsListService extends JobService {
/**
* The entry point to your Job. Implementations should offload work to another thread of
* execution as soon as possible.
*
* @param job
* @return whether there is more work remaining.
*/
@Override
public boolean onStartJob(final JobParameters job) {
Log.d("AGNDDR-NotifiUpdate", "Estou no Job");
new Thread(new Runnable() {
public void run() {
Log.v("AGNDDR-NotifiUpdate", "Rodando " + NotificationsListService.class.getName());
Map<String, String> header = new HashMap<>();
ObscuredSharedPreferences osb = ObscuredSharedPreferences.getPrefs(getBaseContext(), "Agendador", Context.MODE_PRIVATE);
header.put("Content-Type", "application/json");
header.put(Utils.ACCESS_TOKEN, osb.getString(Utils.ACCESS_TOKEN, null));
header.put(Utils.CLIENT, osb.getString(Utils.CLIENT, null));
header.put(Utils.UID, osb.getString(Utils.UID, null));
requestNotificationUpdate(header, job, osb);
}
}).start();
return true;
}
private void requestNotificationUpdate(final Map<String, String> header, final JobParameters job, final ObscuredSharedPreferences osb){
final ApiEndpoints service = ApiUtils.request(header);
Log.v("AGNDDR-NotifiUpdate", Utils.ACCESS_TOKEN + osb.getString(Utils.ACCESS_TOKEN, "NADA"));
Log.v("AGNDDR-NotifiUpdate", Utils.CLIENT + osb.getString(Utils.CLIENT, "NADA"));
Log.v("AGNDDR-NotifiUpdate", Utils.UID + osb.getString(Utils.UID, "NADA"));
Call<List<NotificationReturn>> listCall = service.requestNotifications();
listCall.enqueue(new Callback<List<NotificationReturn>>() {
@Override
public void onResponse(Call<List<NotificationReturn>> call, Response<List<NotificationReturn>> response) {
Headers headers = response.headers();
int status = response.code();
updateHeaders(headers, osb);
final List<NotificationReturn> responseList = response.body();
switch (status) {
case 200:
case 201:
Log.d("Server response", getClass().getName() + ": 200 - Sucesso!");
new Thread(new Runnable() {
public void run() {
Gson gson = new Gson();
LongSparseArray<Notification> localNotifications = new LongSparseArray<>();
List<Notification> list = LocalNotificationManager.getList(osb, gson);
for(Notification n : list)
localNotifications.append(n.scheduleId, n);
for(NotificationReturn nr : responseList){
if(localNotifications.get(nr.scheduleId) == null){
LocalNotificationManager.createLocalNotification(getApplicationContext(),
new Notification(nr.scheduleId, nr.reminderTime, nr.hasRead == 0,
nr.description, nr.emailAddres, nr.emailSent == 0),
osb, gson);
}
}
jobFinished(job, false);
}}).start();
break;
default:
Log.e("Server response", getClass().getName() + ": ERRO:" + status);
jobFinished(job, false);
break;
}
}
@Override
public void onFailure(Call<List<NotificationReturn>> call, Throwable t) {
Log.e("Server response", getClass().getName() + ": Requisição falhou!!");
t.printStackTrace();
jobFinished(job, false);
}
});
}
private void updateHeaders(Headers headers, ObscuredSharedPreferences osb){
if (headers.get(Utils.ACCESS_TOKEN) != null)
osb.edit().putString(Utils.ACCESS_TOKEN, headers.get(Utils.ACCESS_TOKEN)).apply();
if (headers.get(Utils.CLIENT) != null)
osb.edit().putString(Utils.CLIENT, headers.get(Utils.CLIENT)).apply();
if (headers.get(Utils.UID) != null)
osb.edit().putString(Utils.UID, headers.get(Utils.UID)).apply();
if (headers.get(Utils.EXPIRY) != null)
osb.edit().putString(Utils.EXPIRY, headers.get(Utils.EXPIRY)).apply();
}
/**
* Called when the scheduling engine has decided to interrupt the execution of a running job,
* most likely because the runtime constraints associated with the job are no longer satisfied.
* The job must stop execution.
*
* @param job
* @return true if the job should be retried
*
*/
@Override
public boolean onStopJob(JobParameters job) {
return true;
}
}
......@@ -222,7 +222,7 @@
<string name="not_valid_email">Digite um e-mail válido.</string>
<string name="notification_content">%s em %s (%s, %d) %s às %s</string>
<string name="notification_content">%s às %s, %s em %s (%s, %d)</string>
<string name="notification_note_text">Observações (Máximo de 140 caracteres):</string>
<string name="notification_alert">Atendimento está próximo</string>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment