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 @@
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:name=".core.MyApplication"
......@@ -132,16 +133,21 @@
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"
android:enabled="false">
android:enabled="false" android:process=":remote">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
</intent-filter>
</receiver>
<!-- Alarm service which runs in background -->
<service android:name=".core.AlarmService" android:process=":remote"/>
</application>
</manifest>
\ No newline at end of file
......@@ -3,12 +3,15 @@ package ch.amiv.android_app.core;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class AlarmBootReceiver extends BroadcastReceiver {
// resets alarm if device is rebooted
@Override
public void onReceive(Context context, Intent intent) {
Log.e("boot", "boot completed");
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
Notifications.set_Alarm(context);
}
......
......@@ -7,12 +7,11 @@ import android.content.Intent;
public class AlarmReceiver extends BroadcastReceiver {
int notification_id=0;
// when alarm is received this function is executed
@Override
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() {
@Override
public void OnDataReceived() {
......@@ -24,11 +23,20 @@ public class AlarmReceiver extends BroadcastReceiver {
}
}, "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;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
......@@ -290,7 +289,8 @@ public class IntroActivity extends AppCompatActivity {
Settings.SetBoolPref(Settings.introDoneKey, true, this);
// 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));
finish();
......
......@@ -94,7 +94,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
NotificationChannel channel = new NotificationChannel("1", name, importance);
channel.setDescription(description);
// Register the channel in the system
NotificationManager notificationManager = getSystemService(NotificationManager.class);
NotificationManager notificationManager = getSystemService( NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
......
......@@ -16,6 +16,8 @@ import java.util.Calendar;
import ch.amiv.android_app.R;
import ch.amiv.android_app.events.EventDetailActivity;
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 static AlarmManager alarm;
......@@ -25,14 +27,14 @@ public final class Notifications {
/**
*
* @param context
* @return sets daily alarm to XX:XX
* output: sets daily alarm to XX:XX
*/
static void set_Alarm (Context context){
// set alarm time in calendar
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 8);
calendar.set(Calendar.MINUTE, 23);
calendar.set(Calendar.MINUTE, 10);
calendar.set(Calendar.SECOND, 0);
// pending intent to activate activity when notification is clicked
......@@ -47,7 +49,7 @@ public final class Notifications {
.getSystemService(context.ALARM_SERVICE);
alarm.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
AlarmManager.INTERVAL_FIFTEEN_MINUTES, pendingIntent);
AlarmManager.INTERVAL_DAY, pendingIntent);
}
/**
......@@ -56,7 +58,7 @@ public final class Notifications {
* @param title title of notification
* @param text text 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){
......@@ -70,7 +72,7 @@ public final class Notifications {
.setContentText(text)
.setAutoCancel(true);
notificationManager.notify(0, mNotifyBuilder.build());
notificationManager.notify(2, mNotifyBuilder.build()); // id 2
}
......@@ -81,9 +83,10 @@ public final class Notifications {
* @param text text of notification
* @param icon icon
* @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
.getSystemService(Context.NOTIFICATION_SERVICE);
......@@ -95,9 +98,10 @@ public final class Notifications {
.setContentText(text)
.setColor(context.getResources().getColor(R.color.primary)).setColorized(true)
.setAutoCancel(true)
.setStyle(new NotificationCompat.BigTextStyle().bigText(text))
.setContentIntent(pendingIntent);
notificationManager.notify(0, mNotifyBuilder.build());
notificationManager.notify(id, mNotifyBuilder.build());
}
......@@ -105,93 +109,161 @@ public final class Notifications {
*
* @param context
* @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){
// TODO catch when projections are used
try{
JSONArray items = json.getJSONArray("_items");
// if length == 0 -> change last change time and don't send notifiactions
if(items.length()!=0) {
// 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);
// multiple new events -> show all titles and onClick show events main page
//else{
// get event id's from new events
String [] event_id = new String[items.length()];
int [] event_index = new int[items.length()];
for(int i = 0; i<items.length();i++){
JSONObject event = items.getJSONObject(i);
event_index[i]=Events.AddEvent(event, context); // TODO efficiency !
event_id[i]= (String) event.get("_id");
}
// add new events to list
// TODO how -> new function?
Request.FetchEventList(context, null, null,event_id[0]); // TODO not null !
// generate pending intent to get EventDetails onClick
NotificationManager notificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
Intent notificationIntent = new Intent(context, EventDetailActivity.class);
// generate Intent based on how many changes there are
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,
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);
// build string to display all event titles
StringBuilder titles_list = new StringBuilder();
titles_list.append (Events.eventInfos.get(event_index[0]).GetTitle(context.getResources()));
for(int j = 1; j<items.length();j++){
titles_list.append(", ");
titles_list.append(Events.eventInfos.get(event_index[j]).GetTitle(context.getResources()));
}
// set notification title depending on how many changes there are
String title;
if(items.length()>1){
title = context.getString(R.string.multipleEventsadded);
}
// multiple new events -> show all titles and onClick show events main page
else{
// get event id's from new events
String [] event_id = new String[items.length()];
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
String dat = Request.dateFormat.format(Calendar.getInstance().getTime());
Settings.SetPref(Settings.last_change_check_event_dateKey,dat,context);
}catch(JSONException ex){
// TODO
}
}
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 event = items.getJSONObject(i);
Events.AddEvent(event, context); // TODO efficiency !
event_id[i]= (String) event.get("_id");
JSONObject job = items.getJSONObject(i);
job_id[i]= (String) job.get("_id");
}
Jobs.UpdateJobInfos(context, items); // TODO efficiency !
// add new events to list
// add new jobs to list
// TODO how -> new function?
Request.FetchEventList(context, null, null,event_id[0]); // TODO not null !
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 = new Intent(context, MainActivity.class);
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, 0,
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("title_de"));
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"));
}
Notifications.notify_pending(context, "Many new Events added", titles_list.toString(), R.drawable.ic_amiv_logo_icon, pendingIntent);
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_dateKey,dat,context);
Settings.SetPref(Settings.last_change_check_job_dateKey,dat,context);
}catch(JSONException ex){
// TODO
}
}
......
......@@ -115,7 +115,6 @@ public final class Request {
Runnable runnable = new Runnable() {
@Override
public void run() {
// TODO alarmreceiver
Notifications.event_notifier(context,json);
if(callback != null)
callback.OnDataReceived();
......@@ -392,6 +391,84 @@ public final class Request {
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);
headers.put("Authorization", auth);
return headers;
}
return super.getHeaders();
}
};
//send the request and check if it failed
if(!Request.SendRequest(request, context))
RunCallback(errorCallback);
}
public static void FetchJobListChanges(final Context context, final OnDataReceivedCallback callback, final OnDataReceivedCallback errorCallback, @NonNull final String last_update_time)
{
if(!CheckConnection(context)) {
RunCallback(errorCallback);
return;
}
String url = Settings.API_URL + "joboffers?" + "&where={\"_created\":{\"$gt\":\"" + last_update_time + "\"}, \"show_website\": true}";
Log.e("request", "url: " + url);
StringRequest request = new StringRequest(com.android.volley.Request.Method.GET, url,null, null)
{
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) { //Note: the parseNetworkResponse is only called if the response was successful (codes 2xx), else parseNetworkError is called.
if(response != null) {
Log.e("request", "fetch jobs status Code: " + response.statusCode);
try {
final JSONObject json = new JSONObject(new String(response.data));
//Update on main thread
Runnable runnable = new Runnable() {
@Override
public void run() {
Notifications.jobs_notifier(context,json);
if(callback != null)
callback.OnDataReceived();
}
};
callbackHandler.post(runnable);
Log.e("request", new JSONObject(new String(response.data)).toString());
} catch (JSONException e) {
RunCallback(errorCallback);
e.printStackTrace();
}
}
else {
RunCallback(errorCallback);
Log.e("request", "Request returned null response. fetch jobs");
}
return super.parseNetworkResponse(response);
}
@Override
protected VolleyError parseNetworkError(final VolleyError volleyError) {
if(volleyError != null && volleyError.networkResponse != null)
......
......@@ -60,7 +60,8 @@ public class Settings {
//endregion
//last changes check
public static final String[] last_change_check_dateKey = {"core.notifications_date", "1979-02-19T10:00:00Z"};
public static final String[] last_change_check_event_dateKey = {"core.notifications_date", "1979-02-19T10:00:00Z"};
public static final String[] last_change_check_job_dateKey = {"core.notifications_date", "1979-02-19T10:00:00Z"};
//region ---SharedPrefs---
/**
......
......@@ -185,7 +185,7 @@ public class EventDetailActivity extends AppCompatActivity {
event = Events.GetEventById(intent.getStringExtra(LauncherExtras.EVENT_ID));
if(event == null)
Log.e("events", "No event found from eventId=" + intent.getStringExtra(LauncherExtras.EVENT_ID) + " in intent, have you used intent.putStringExtra. Returning to calling activity...");
Log.e("events", "No event found from eventId=" + intent.getStringExtra(LauncherExtras.EVENT_ID) + " in intent, have you used intent.putStringExtra. Returning to calling activity..." + Events.eventInfos.size());
}
if(event == null)
......
......@@ -191,5 +191,10 @@
<string name="other">Anderes</string>
<string name="tap_to_set">Setzen</string>
<string name="new_jobs_notification">Neue Stellenangebote</string>
<string name="multipleEventsadded">Neue Events</string>
<string name="singleEventadded">Neues Event</string>
<string name="new_job_notification">Neues Stellenangebot</string>
</resources>
......@@ -213,4 +213,9 @@
<string name="other">Other</string>
<string name="tap_to_set">Tap to set</string>
<string name="new_jobs_notification">New Joboffers</string>
<string name="multipleEventsadded">New Events</string>
<string name="singleEventadded">New Event</string>
<string name="new_job_notification">New joboffer</string>
</resources>
Supports Markdown
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