Commit f39fe968 authored by lvarano's avatar lvarano
Browse files

Extended notifications with jobs, fix alarm issues when app is killed

Fetch Job changes and notification generation, alarm service start is moved into intro to fix issue when app is killed, reboot permissions added but issue is still there
parent c887d453
Pipeline #8605 passed with stages
in 10 minutes and 29 seconds
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
<uses-permission android:name="android.permission.READ_PROFILE" /> <uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application <application
android:name=".core.MyApplication" android:name=".core.MyApplication"
...@@ -132,16 +133,21 @@ ...@@ -132,16 +133,21 @@
android:screenOrientation="portrait" /> --> android:screenOrientation="portrait" /> -->
<!-- Alarm Receiver receives alarm to get changes -->
<receiver android:name=".core.AlarmReceiver"
android:process=":remote"/>
<receiver android:name=".core.AlarmReceiver"/> <!-- Reset alarm if device is rebooted -->
<receiver android:name=".core.AlarmBootReceiver" <receiver android:name=".core.AlarmBootReceiver"
android:enabled="false"> android:enabled="false" android:process=":remote">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"></action> <action android:name="android.intent.action.BOOT_COMPLETED"></action>
</intent-filter> </intent-filter>
</receiver> </receiver>
<!-- Alarm service which runs in background -->
<service android:name=".core.AlarmService" android:process=":remote"/>
</application> </application>
</manifest> </manifest>
\ No newline at end of file
...@@ -3,12 +3,15 @@ package ch.amiv.android_app.core; ...@@ -3,12 +3,15 @@ package ch.amiv.android_app.core;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.util.Log;
public class AlarmBootReceiver extends BroadcastReceiver { public class AlarmBootReceiver extends BroadcastReceiver {
// resets alarm if device is rebooted
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Log.e("boot", "boot completed");
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) { if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
Notifications.set_Alarm(context); Notifications.set_Alarm(context);
} }
......
...@@ -7,12 +7,11 @@ import android.content.Intent; ...@@ -7,12 +7,11 @@ import android.content.Intent;
public class AlarmReceiver extends BroadcastReceiver { public class AlarmReceiver extends BroadcastReceiver {
int notification_id=0; // when alarm is received this function is executed
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
//Notifications.notify(context,"Alarm","Receiver called",R.drawable.ic_amiv_logo_icon); // event changes
Request.FetchEventListChanges(context, new Request.OnDataReceivedCallback() { Request.FetchEventListChanges(context, new Request.OnDataReceivedCallback() {
@Override @Override
public void OnDataReceived() { public void OnDataReceived() {
...@@ -24,11 +23,20 @@ public class AlarmReceiver extends BroadcastReceiver { ...@@ -24,11 +23,20 @@ public class AlarmReceiver extends BroadcastReceiver {
} }
}, "2018-05-06T10:00:00Z",false); // TODO change date here to use last checked date }, "2018-05-06T10:00:00Z",false); // TODO change date here to use last checked date
// use Settings.GetPref(Settings.last_change_check_dateKey,context) // use Settings.GetPref(Settings.last_change_event_check_dateKey,context)
// job changes
Request.FetchJobListChanges(context, new Request.OnDataReceivedCallback() {
@Override
public void OnDataReceived() {
}
}, new Request.OnDataReceivedCallback() {
@Override
public void OnDataReceived() {
} // TODO change date here to use last checked date
}, "2018-05-06T10:00:00Z"); // use Settings.GetPref(Settings.last_change_check_job_dateKey,context)
// TODO test if notification needed
// TODO enter event notifier here
notification_id++;
} }
} }
package ch.amiv.android_app.core;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
public class AlarmService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
public int onStartCommand (Intent intent, int flags,int start_id){
super.onStartCommand(intent,flags,start_id);
Notifications.set_Alarm(getApplicationContext());
return START_STICKY;
}
}
...@@ -2,7 +2,6 @@ package ch.amiv.android_app.core; ...@@ -2,7 +2,6 @@ package ch.amiv.android_app.core;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.Resources;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.support.v4.view.PagerAdapter; import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager;
...@@ -290,7 +289,8 @@ public class IntroActivity extends AppCompatActivity { ...@@ -290,7 +289,8 @@ public class IntroActivity extends AppCompatActivity {
Settings.SetBoolPref(Settings.introDoneKey, true, this); Settings.SetBoolPref(Settings.introDoneKey, true, this);
// TODO check if notifications are enabled in the settings // TODO check if notifications are enabled in the settings
Notifications.set_Alarm(this); // start alarm service
startService(new Intent(this,AlarmService.class));
startActivity(new Intent(this, MainActivity.class)); startActivity(new Intent(this, MainActivity.class));
finish(); finish();
......
...@@ -94,7 +94,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On ...@@ -94,7 +94,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
NotificationChannel channel = new NotificationChannel("1", name, importance); NotificationChannel channel = new NotificationChannel("1", name, importance);
channel.setDescription(description); channel.setDescription(description);
// Register the channel in the system // Register the channel in the system
NotificationManager notificationManager = getSystemService(NotificationManager.class); NotificationManager notificationManager = getSystemService( NotificationManager.class);
notificationManager.createNotificationChannel(channel); notificationManager.createNotificationChannel(channel);
} }
......
...@@ -16,6 +16,8 @@ import java.util.Calendar; ...@@ -16,6 +16,8 @@ import java.util.Calendar;
import ch.amiv.android_app.R; import ch.amiv.android_app.R;
import ch.amiv.android_app.events.EventDetailActivity; import ch.amiv.android_app.events.EventDetailActivity;
import ch.amiv.android_app.events.Events; import ch.amiv.android_app.events.Events;
import ch.amiv.android_app.jobs.JobDetailActivity;
import ch.amiv.android_app.jobs.Jobs;
public final class Notifications { public final class Notifications {
public static AlarmManager alarm; public static AlarmManager alarm;
...@@ -25,14 +27,14 @@ public final class Notifications { ...@@ -25,14 +27,14 @@ public final class Notifications {
/** /**
* *
* @param context * @param context
* @return sets daily alarm to XX:XX * output: sets daily alarm to XX:XX
*/ */
static void set_Alarm (Context context){ static void set_Alarm (Context context){
// set alarm time in calendar // set alarm time in calendar
Calendar calendar = Calendar.getInstance(); Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 8); calendar.set(Calendar.HOUR_OF_DAY, 8);
calendar.set(Calendar.MINUTE, 23); calendar.set(Calendar.MINUTE, 10);
calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.SECOND, 0);
// pending intent to activate activity when notification is clicked // pending intent to activate activity when notification is clicked
...@@ -47,7 +49,7 @@ public final class Notifications { ...@@ -47,7 +49,7 @@ public final class Notifications {
.getSystemService(context.ALARM_SERVICE); .getSystemService(context.ALARM_SERVICE);
alarm.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), alarm.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
AlarmManager.INTERVAL_FIFTEEN_MINUTES, pendingIntent); AlarmManager.INTERVAL_DAY, pendingIntent);
} }
/** /**
...@@ -56,7 +58,7 @@ public final class Notifications { ...@@ -56,7 +58,7 @@ public final class Notifications {
* @param title title of notification * @param title title of notification
* @param text text of notification * @param text text of notification
* @param icon icon of notification * @param icon icon of notification
* @return immediately generates notification on screen * output: immediately generates notification on screen
*/ */
public static void notify (Context context, String title, String text, int icon){ public static void notify (Context context, String title, String text, int icon){
...@@ -70,7 +72,7 @@ public final class Notifications { ...@@ -70,7 +72,7 @@ public final class Notifications {
.setContentText(text) .setContentText(text)
.setAutoCancel(true); .setAutoCancel(true);
notificationManager.notify(0, mNotifyBuilder.build()); notificationManager.notify(2, mNotifyBuilder.build()); // id 2
} }
...@@ -81,9 +83,10 @@ public final class Notifications { ...@@ -81,9 +83,10 @@ public final class Notifications {
* @param text text of notification * @param text text of notification
* @param icon icon * @param icon icon
* @param pendingIntent pending intent to activate if notification is clicked * @param pendingIntent pending intent to activate if notification is clicked
* @return generates notification on screen which starts the pending activity onClick * @param id 0 event notifications 1 job notifications 2 other
* output: generates notification on screen which starts the pending activity onClick
*/ */
public static void notify_pending (Context context, String title, String text, int icon, PendingIntent pendingIntent){ public static void notify_pending (Context context, String title, String text, int icon, PendingIntent pendingIntent, int id){
NotificationManager notificationManager = (NotificationManager) context NotificationManager notificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE); .getSystemService(Context.NOTIFICATION_SERVICE);
...@@ -95,9 +98,10 @@ public final class Notifications { ...@@ -95,9 +98,10 @@ public final class Notifications {
.setContentText(text) .setContentText(text)
.setColor(context.getResources().getColor(R.color.primary)).setColorized(true) .setColor(context.getResources().getColor(R.color.primary)).setColorized(true)
.setAutoCancel(true) .setAutoCancel(true)
.setStyle(new NotificationCompat.BigTextStyle().bigText(text))
.setContentIntent(pendingIntent); .setContentIntent(pendingIntent);
notificationManager.notify(0, mNotifyBuilder.build()); notificationManager.notify(id, mNotifyBuilder.build());
} }
...@@ -105,83 +109,75 @@ public final class Notifications { ...@@ -105,83 +109,75 @@ public final class Notifications {
* *
* @param context * @param context
* @param json JSON with all new events * @param json JSON with all new events
* @return event notificiation with appropriate onClick event * output: event notificiation with appropriate onClick event
*/ */
public static void event_notifier (Context context, JSONObject json){ public static void event_notifier (Context context, JSONObject json){
// TODO catch when projections are used // TODO catch when projections are used
try{ try{
JSONArray items = json.getJSONArray("_items"); JSONArray items = json.getJSONArray("_items");
// if length == 0 -> change last change time and don't send notifiactions
if(items.length()!=0) { if(items.length()!=0) {
// for one new event -> notify and onClick show details // for one new event -> notify and onClick show details
if(items.length()==1) {
// get event from JSONObject and add it to events list
JSONObject event = items.getJSONObject(0);
Events.AddEvent(event, context);
// refetch event list to add new event
String event_id = (String) event.get("_id");
Request.FetchEventList(context, null, null, event_id);
// generate pending intent to get EventDetails onClick
NotificationManager notificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
Intent notificationIntent = new Intent(context, EventDetailActivity.class);
notificationIntent.putExtra(EventDetailActivity.LauncherExtras.EVENT_ID, event_id);
notificationIntent.putExtra(EventDetailActivity.LauncherExtras.LOAD_EVENTS, true);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
// generate notification
String title = (String) event.get("title_de");
Notifications.notify_pending(context, "New Event added", title, R.drawable.ic_amiv_logo_icon, pendingIntent);
}
// multiple new events -> show all titles and onClick show events main page // multiple new events -> show all titles and onClick show events main page
else{ //else{
// get event id's from new events // get event id's from new events
String [] event_id = new String[items.length()]; String [] event_id = new String[items.length()];
int [] event_index = new int[items.length()];
for(int i = 0; i<items.length();i++){ for(int i = 0; i<items.length();i++){
JSONObject event = items.getJSONObject(i); JSONObject event = items.getJSONObject(i);
Events.AddEvent(event, context); // TODO efficiency ! event_index[i]=Events.AddEvent(event, context); // TODO efficiency !
event_id[i]= (String) event.get("_id"); event_id[i]= (String) event.get("_id");
} }
// add new events to list // add new events to list
// TODO how -> new function? // TODO how -> new function?
Request.FetchEventList(context, null, null,event_id[0]); // TODO not null ! Request.FetchEventList(context, null, null,event_id[0]); // TODO not null !
// onClick start main activity
NotificationManager notificationManager = (NotificationManager) context NotificationManager notificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE); .getSystemService(Context.NOTIFICATION_SERVICE);
Intent notificationIntent = new Intent(context, MainActivity.class); // generate Intent based on how many changes there are
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); Intent notificationIntent;
if(items.length()> 1) {
notificationIntent = new Intent(context, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
}
else{
notificationIntent = new Intent(context, EventDetailActivity.class);
notificationIntent.putExtra(EventDetailActivity.LauncherExtras.EVENT_ID, event_id);
notificationIntent.putExtra(EventDetailActivity.LauncherExtras.LOAD_EVENTS, true);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
}
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
// build string to display all event titles // build string to display all event titles
StringBuilder titles_list = new StringBuilder(); StringBuilder titles_list = new StringBuilder();
titles_list.append ((String)items.getJSONObject(0).get("title_de")); titles_list.append (Events.eventInfos.get(event_index[0]).GetTitle(context.getResources()));
for(int j = 1; j<items.length();j++){ for(int j = 1; j<items.length();j++){
titles_list.append(", "); titles_list.append(", ");
titles_list.append((String)items.getJSONObject(j).get("title_de")); titles_list.append(Events.eventInfos.get(event_index[j]).GetTitle(context.getResources()));
} }
Notifications.notify_pending(context, "Many new Events added", titles_list.toString(), R.drawable.ic_amiv_logo_icon, pendingIntent);
} // set notification title depending on how many changes there are
String title;
if(items.length()>1){
title = context.getString(R.string.multipleEventsadded);
}
else{
title = context.getString(R.string.singleEventadded);
}
Notifications.notify_pending(context, title, titles_list.toString(), R.drawable.ic_amiv_logo_icon, pendingIntent,0);
} }
// sets last change check to current time // sets last change check to current time
String dat = Request.dateFormat.format(Calendar.getInstance().getTime()); String dat = Request.dateFormat.format(Calendar.getInstance().getTime());
Settings.SetPref(Settings.last_change_check_dateKey,dat,context); Settings.SetPref(Settings.last_change_check_event_dateKey,dat,context);
}catch(JSONException ex){ }catch(JSONException ex){
// TODO // TODO
...@@ -192,6 +188,82 @@ public final class Notifications { ...@@ -192,6 +188,82 @@ public final class Notifications {
}
public static void jobs_notifier (Context context, JSONObject json){
// TODO catch when projections are used
try{
JSONArray items = json.getJSONArray("_items");
if(items.length()!=0) {
// for one new job -> notify and onClick show details
// multiple new jobs -> show all jobs and onClick show events main page
// get job id's from new events
String [] job_id = new String[items.length()];
// TODO add single job
for(int i = 0; i<items.length();i++){
JSONObject job = items.getJSONObject(i);
job_id[i]= (String) job.get("_id");
}
Jobs.UpdateJobInfos(context, items); // TODO efficiency !
// add new jobs to list
// TODO how -> new function?
Request.FetchJobList(context, null, null,job_id[0]); // TODO not null !
// onClick start main activity
NotificationManager notificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
Intent notificationIntent;
if(items.length()>1) {
notificationIntent = new Intent(context, MainActivity.class); // TODO go to jobs tab instead of
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
}
else{
notificationIntent = new Intent(context, JobDetailActivity.class);
notificationIntent.putExtra(EventDetailActivity.LauncherExtras.EVENT_ID, job_id);
notificationIntent.putExtra(EventDetailActivity.LauncherExtras.LOAD_EVENTS, true);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
}
PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, // TODO is this correct ?
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
// build string to display all event titles
StringBuilder titles_list = new StringBuilder();
titles_list.append((String)items.getJSONObject(0).get("company"));
titles_list.append(" - ");
titles_list.append ((String)items.getJSONObject(0).get("title_de")); // TODO access job via job index
for(int j = 1; j<items.length();j++){
titles_list.append(", ");
titles_list.append((String)items.getJSONObject(j).get("company"));
titles_list.append(" - ");
titles_list.append((String)items.getJSONObject(j).get("title_de"));
}
String title;
if(items.length()>1){
title = context.getString(R.string.new_jobs_notification);
}
else {
title = context.getString(R.string.new_job_notification);
}
Notifications.notify_pending(context,title , titles_list.toString(), R.drawable.ic_amiv_logo_icon, pendingIntent,1);
}
// sets last change check to current time
String dat = Request.dateFormat.format(Calendar.getInstance().getTime());
Settings.SetPref(Settings.last_change_check_job_dateKey,dat,context);
}catch(JSONException ex){
// TODO
}
} }
......
...@@ -115,7 +115,6 @@ public final class Request { ...@@ -115,7 +115,6 @@ public final class Request {
Runnable runnable = new Runnable() { Runnable runnable = new Runnable() {
@Override @Override
public void run() { public void run() {
// TODO alarmreceiver
Notifications.event_notifier(context,json); Notifications.event_notifier(context,json);
if(callback != null) if(callback != null)
callback.OnDataReceived(); callback.OnDataReceived();
...@@ -392,6 +391,84 @@ public final class Request { ...@@ -392,6 +391,84 @@ public final class Request {
return super.parseNetworkResponse(response); return super.parseNetworkResponse(response);
} }
@Override
protected VolleyError parseNetworkError(final VolleyError volleyError) {
if(volleyError != null && volleyError.networkResponse != null)
Log.e("request", "status code: " + volleyError.networkResponse.statusCode + "\n" + new String(volleyError.networkResponse.data));
else
Log.e("request", "Request returned null response. fetch jobs");
RunCallback(errorCallback);
return super.parseNetworkError(volleyError);
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
if(Settings.HasToken(context)) {
Map<String,String> headers = new HashMap<String, String>();
String credentials = Settings.GetToken(context) + ":";
String auth = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);