Merge branch 'feature/fix-notification' into develop

This commit is contained in:
Jacques Foucry 2020-09-11 16:47:38 +02:00
commit 5de270def2
8 changed files with 286 additions and 152 deletions

View file

@ -48,11 +48,12 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
testImplementation 'junit:junit:4.13'
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'com.google.android.material:material:1.0.0'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'com.google.android.material:material:1.2.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'com.journeyapps:zxing-android-embedded:4.1.0'
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation "com.android.support:support-compat:28.0.0"
}

View file

@ -3,6 +3,7 @@
package="net.foucry.pilldroid">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />
<application

View file

@ -7,6 +7,8 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
@ -34,6 +36,7 @@ class DBHelper extends SQLiteOpenHelper {
private static final String KEY_SEUIL_ALERT = "alerte";
private static DBHelper sInstance;
List<Medicament> medicaments = new LinkedList<Medicament>();
private static final String TAG = DBHelper.class.getName();
@ -213,16 +216,15 @@ class DBHelper extends SQLiteOpenHelper {
/**
*
* @return a List of All medicaments presents in database
* @return a Sorted and updated by dateEndOfStock List of All medicaments presents in database
*/
List<Medicament> getAllDrugs() {
List<Medicament> medicaments = new LinkedList<Medicament>();
// Build the query
String query = "SELECT * FROM " + TABLE_DRUG;
// Get reference to readable DB (tutorial parle de writable, mais bof... on verra)
// Get reference to readable DB
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(query, null);
@ -252,6 +254,21 @@ class DBHelper extends SQLiteOpenHelper {
}
cursor.close();
Medicament currentMedicament = null;
for (int position = 0 ; position < getCount() ; position++ ) {
currentMedicament = getItem(position);
currentMedicament.newStock(currentMedicament.getStock());
updateDrug(currentMedicament);
}
Collections.sort(medicaments, new Comparator<Medicament>() {
@Override
public int compare(Medicament lhs, Medicament rhs) {
return lhs.getDateEndOfStock().compareTo(rhs.getDateEndOfStock());
}
});
Log.d(TAG, "getAllDrugs " + medicaments.toString());
return medicaments;
@ -276,6 +293,7 @@ class DBHelper extends SQLiteOpenHelper {
values.put(KEY_ADMIN, medicament.getMode_administration());
values.put(KEY_PRES, medicament.getPresentation());
values.put(KEY_STOCK, medicament.getStock());
values.put(KEY_PRISE, medicament.getPrise());
Log.d(TAG, "values are " +values.toString());
// Update row
@ -326,6 +344,11 @@ class DBHelper extends SQLiteOpenHelper {
mCount.close();
return count;
}
public Medicament getItem(int position) {
return medicaments.get(position);
}
boolean isMedicamentExist(String cip13) {
boolean value = false;
try {

View file

@ -3,12 +3,12 @@ package net.foucry.pilldroid;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.BitmapFactory;
import android.icu.util.Calendar;
import android.os.Bundle;
import android.os.SystemClock;
import androidx.annotation.NonNull;
@ -29,6 +29,16 @@ import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.RecyclerView;
import com.google.zxing.client.android.Intents;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collections;
@ -55,7 +65,6 @@ import static net.foucry.pilldroid.Utils.intRandomExclusive;
*/
public class MedicamentListActivity extends AppCompatActivity {
private static final String CHANNEL_ID = "MedicamentCHANEL";
/**
* Whether or not the activity is in two-pane mode, i.e. running on a tablet
* device.
@ -92,17 +101,12 @@ public class MedicamentListActivity extends AppCompatActivity {
private SimpleItemRecyclerViewAdapter mAdapter;
public int getCount() {
return medicaments.size();
}
public Medicament getItem(int position) {
return medicaments.get(position);
}
public void constructMedsList()
{
Medicament currentMedicament;
dbHelper = new DBHelper(getApplicationContext());
if (!(medicaments == null)) {
@ -112,19 +116,6 @@ public class MedicamentListActivity extends AppCompatActivity {
}
medicaments = dbHelper.getAllDrugs();
Collections.sort(medicaments, new Comparator<Medicament>() {
@Override
public int compare(Medicament lhs, Medicament rhs) {
return lhs.getDateEndOfStock().compareTo(rhs.getDateEndOfStock());
}
});
for (int position = 0 ; position < this.getCount() ; position++ ) {
currentMedicament = this.getItem(position);
currentMedicament.newStock(currentMedicament.getStock());
dbHelper.updateDrug(currentMedicament);
}
View mRecyclerView = findViewById(R.id.medicament_list);
assert mRecyclerView != null;
setupRecyclerView((RecyclerView) mRecyclerView);
@ -227,8 +218,11 @@ public class MedicamentListActivity extends AppCompatActivity {
public void onPause() {
super.onPause();
scheduleJob();
}
newStockCalculation();
public void onResume() {
super.onResume();
}
/** scanNow
@ -240,40 +234,11 @@ public class MedicamentListActivity extends AppCompatActivity {
new IntentIntegrator(this).setOrientationLocked(false).setCaptureActivity(CustomScannerActivity.class).initiateScan();
}
/**
* Calculation of newStock
*/
public void newStockCalculation() {
Calendar calendar = Calendar.getInstance();
Date now = calendar.getTime();
long dateSchedule;
Medicament firstMedicament = null;
try {
firstMedicament = medicaments.get(0);
}
catch (Exception ignored){}
if (firstMedicament != null) {
Date dateAlert = UtilDate.removeDaysToDate(firstMedicament.getAlertThreshold(), firstMedicament.getDateEndOfStock());
if (dateAlert.getTime() < now.getTime()) {
dateSchedule = now.getTime() + 50000; // If dateAlert < now we schedule an alert for now + 5 seconds (3600000 pour 1 heure)[in prod define delay]
} else {
dateSchedule = dateAlert.getTime(); // If dateAlert > now we use dateAlert as scheduleDate
}
long delay = dateSchedule - now.getTime();
scheduleNotification(getNotification(getString(R.string.notification_text)), delay);
Log.d(TAG, "Notification scheduled for " + UtilDate.convertDate(dateSchedule));
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "REQUEST_CODE = " + requestCode + " RESULT_CODE = " + resultCode);
if (requestCode != CUSTOMIZED_REQUEST_CODE && requestCode != IntentIntegrator.REQUEST_CODE) {
// This is important, otherwise the result will not be passed to the fragment
super.onActivityResult(requestCode, resultCode, data);
@ -407,41 +372,27 @@ public class MedicamentListActivity extends AppCompatActivity {
recyclerView.setAdapter(mAdapter);
}
private void scheduleNotification(Notification notification, long delay) {
Log.d(TAG, "scheduleNotification delay == " + delay);
public void scheduleJob() {
Calendar calendar = Calendar.getInstance();
Date now = calendar.getTime();
Intent notificationIntent = new Intent(this, NotificationPublisher.class);
notificationIntent.putExtra(NOTIFICATION_ID, 1);
notificationIntent.putExtra(NotificationPublisher.NOTIFICATION, notification);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
long futureInMillis = SystemClock.elapsedRealtime() + 30000;
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
if (alarmManager != null) {
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, futureInMillis, pendingIntent);
ComponentName componentName = new ComponentName(this, PillDroidJobService.class);
JobInfo info = new JobInfo.Builder(24560, componentName)
.setPersisted(true)
.setPeriodic(15 *60 *1000)
.build();
JobScheduler scheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
int resultCode = scheduler.schedule(info);
if (resultCode == JobScheduler.RESULT_SUCCESS) {
Log.d(TAG, ("Job scheduled " + UtilDate.convertDate(now.getTime()+15 * 60*1000)));
} else {
Log.d(TAG, "Job scheduling failed");
}
}
private Notification getNotification(String content) {
Log.d(TAG, "content = " + content);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle(getAppName())
.setContentText(content)
.setSmallIcon(R.drawable.ic_pill)
.setAutoCancel(true)
.setLargeIcon(BitmapFactory.decodeResource(getApplicationContext().getResources(),
R.drawable.ic_launcher));
return builder.build();
}
private String getAppName() {
PackageManager packageManager = getApplicationContext().getPackageManager();
ApplicationInfo applicationInfo = null;
try {
applicationInfo = packageManager.getApplicationInfo(this.getPackageName(), 0);
} catch (final PackageManager.NameNotFoundException ignored) {}
return (String)((applicationInfo != null) ? packageManager.getApplicationLabel(applicationInfo) : "???");
public void cancelJob(View v) {
JobScheduler scheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
scheduler.cancel(24560);
Log.d(TAG, "Job cancelled");
}
/**
@ -462,7 +413,7 @@ public class MedicamentListActivity extends AppCompatActivity {
notifyDataSetChanged();
dbHelper.addDrug(scannedMedoc);
} else {
Toast.makeText(getApplicationContext(), "aleready in the database", Toast.LENGTH_SHORT).show();
Toast.makeText(getApplicationContext(), "already in the database", Toast.LENGTH_SHORT).show();
}
}
@ -524,8 +475,7 @@ public class MedicamentListActivity extends AppCompatActivity {
Context context = v.getContext();
Intent intent = new Intent(context, MedicamentDetailActivity.class);
intent.putExtra("medicament", medicamentCourant);
int requestCode =1;
startActivityForResult(intent, requestCode);
startActivityForResult(intent, CUSTOMIZED_REQUEST_CODE);
}
}
});
@ -577,4 +527,4 @@ editText.addTextChangeListener( new TextWatcher() {
}
});
*/
*/

View file

@ -1,35 +1,105 @@
package net.foucry.pilldroid;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.media.RingtoneManager;
import android.net.Uri;
import android.util.Log;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
/**
* Created by jfoucry on 6/23/16.
* Publish notification
*/
public class NotificationPublisher extends BroadcastReceiver {
private static String TAG = NotificationPublisher.class.getName();
public static String NOTIFICATION_ID = "notification-id";
public static String NOTIFICATION = "notification";
private static final String TAG = NotificationPublisher.class.getName();
public static String NOTIFICATION_ID = "notification_id";
public static String KEY_MESSAGE = "key_message";
public static String KEY_TITLE = "key_title";
public static String KEY_EXPAND = "key_expand";
public static String KEY_SOUND = "key_sound";
public static String KEY_MULTIPLE = "key_multiple";
public static String APP_NAME = "PillDroid";
/**
* onReceive notification
* @param Context context
* @param Intent intent
*/
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "Receive notification");
Log.d(TAG, "Receive notification");
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
int notificationId = intent.getIntExtra(NOTIFICATION_ID, 0);
String message = intent.getStringExtra(KEY_MESSAGE);
String title = intent.getStringExtra(KEY_TITLE);
boolean isEnabledExpand = intent.getBooleanExtra(KEY_EXPAND, false);
boolean isEnableSound = intent.getBooleanExtra(KEY_SOUND, false);
boolean isEnabledMultiple = intent.getBooleanExtra(KEY_MULTIPLE, false);
Notification notification = intent.getParcelableExtra(NOTIFICATION);
int id = intent.getIntExtra(NOTIFICATION_ID,0);
if (notificationManager != null) {
notificationManager.notify(id, notification);
String channel_id = createNotificationChannel(context);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channel_id)
.setSmallIcon(R.drawable.ic_pill)
.setContentTitle(context.getString(R.string.app_name))
.setContentText(context.getString(R.string.pharmacy))
.setChannelId(APP_NAME)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(context.getString(R.string.pharmacy)))
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
if (isEnableSound) {
Uri alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
builder.setSound(alarmSound);
}
Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
assert vibrator != null;
vibrator.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE));
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
// notificationId is a unique int for each notification that you must define
notificationManager.notify(notificationId, builder.build());
}
}
/**
* createNotificationChannelid for android API >= 28
* @param Context context
* @return String channel_id
*/
public static String createNotificationChannel(Context context) {
Log.d(TAG, "start create notification channel");
// The id of the channel.
String channelId = "Channel_id";
// The user-visible name of the channel.
CharSequence channelName = context.getString(R.string.app_name);
// The user-visible description of the channel.
String channelDescription = "Pilldroid Alert";
int channelImportance = NotificationManager.IMPORTANCE_DEFAULT;
// int channelLockscreenVisibility = Notification.;
// Initializes NotificationChannel.
NotificationChannel notificationChannel = new NotificationChannel(channelId, channelName,
channelImportance);
notificationChannel.setDescription(channelDescription);
notificationChannel.enableVibration(true);
// notificationChannel.setLockscreenVisibility(channelLockscreenVisibility);
// Adds NotificationChannel to system. Attempting to create an existing notification
// channel with its original values performs no operation, so it's safe to perform the
// below sequence.
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
assert notificationManager != null;
notificationManager.createNotificationChannel(notificationChannel);
return channelId;
}
}

View file

@ -1,43 +1,134 @@
package net.foucry.pilldroid;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.os.Message;
import android.os.Handler;
import android.icu.util.Calendar;
import android.util.Log;
import android.widget.Toast;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import java.util.Date;
import java.util.List;
/**
* Created by jacques on 17/09/16.
*/
public class PillDroidJobService extends JobService {
private static final String TAG = "JobService";
private static final String TAG = JobService.class.getName();
private boolean jobCancelled = false;
private String CHANNEL_ID = "pillDroid";
private DBHelper dbHelper = new DBHelper(this);
private Handler mJobHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// Toast.makeText( getApplicationContext(), "PillDroid - Calcul nouveau stocks", Toast.LENGTH_SHORT).show();
// MedicamentListActivity.newStockCalculation(getApplicationContext());
jobFinished( (JobParameters) msg.obj,false);
return true;
}
});
@Override
public boolean onStartJob (JobParameters params) {
Log.i(TAG, "on Start Job: " + params.getJobId());
mJobHandler.sendMessage(Message.obtain(mJobHandler, 1,params));
return false;
public boolean onStartJob(JobParameters params) {
Log.d(TAG, "Job started");
createNotificationChannel();
doBackgroundWork(params);
return true;
}
/**
* Grab sorted list of medicaments
* test dateAlert of the first of the list
* if dateAlert < now
* schedule notification
* @param JobParameters params
*/
private void doBackgroundWork(final JobParameters params) {
if (jobCancelled) {
return;
}
List<Medicament> medicaments = dbHelper.getAllDrugs();
Calendar calendar = Calendar.getInstance();
Date now = calendar.getTime();
long dateSchedule;
Medicament firstMedicament = null;
try {
firstMedicament = medicaments.get(0);
}
catch (Exception ignored){}
if (firstMedicament != null) {
Date dateAlert = UtilDate.removeDaysToDate(firstMedicament.getAlertThreshold(), firstMedicament.getDateEndOfStock());
if (dateAlert.getTime() < now.getTime()) {
dateSchedule = now.getTime() + 120000; // If dateAlert < now we schedule an alert for now + 120 seconds
} else {
dateSchedule = dateAlert.getTime(); // If dateAlert > now we use dateAlert as scheduleDate
}
long delay = dateSchedule - now.getTime();
scheduleNotification(delay);
}
Log.d(TAG, "Job finished");
jobFinished(params, false);
}
@Override
public boolean onStopJob(JobParameters params) {
mJobHandler.removeMessages(1);
return false;
Log.d(TAG, "Job cancelled before completion");
jobCancelled = true;
return true;
}
/**
* Schedule Notification for the delay
* @param Context context
* @param long delay - date for the notification in millisecond
*/
private void scheduleNotification(long delay) {
Log.d(TAG, "scheduleNotification delay == " + delay);
createNotificationChannel();
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_pill)
.setContentTitle(getString(R.string.app_name))
.setContentText(getString(R.string.pharmacy))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true);
}
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
int notificationId = 666;
notificationManager.notify(notificationId, builder.build());
}
/**
* createNotificationChannelid for android API >= 28
*/
private void createNotificationChannel() {
Log.d(TAG, "start create notification channel");
CharSequence name = getString(R.string.channel_name);
String description = getString(R.string.channel_description);
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
channel.setDescription(description);
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
NotificationManager notificationManager = getSystemService(NotificationManager.class);
try {
notificationManager.createNotificationChannel(channel);
} catch (Exception e) {
// This will catch any exception, because they are all descended from Exception
Log.e(TAG, e.toString());
//At the level Exception Class handle the error in Exception Table
// Exception Create That Error Object and throw it
//E.g: FileNotFoundException ,etc
e.printStackTrace();
}
}
}

View file

@ -15,11 +15,11 @@ import java.util.Locale;
class UtilDate {
private static final String TAG = UtilDate.class.getName();
/**
*
* @param aDate anydate
* @return date the same date as input but at noon (12:00:00)
*
* <p>
* set date time at Noon
*/
static Date dateAtNoon(Date aDate) {
@ -34,12 +34,12 @@ class UtilDate {
return calendar.getTime();
}
/**
*
* @param days number of days to remove to the ate
* @param date date before day removing
* @return date
*
* <p>
* Substract days to date and return a new date
*/
static Date removeDaysToDate(int days, Date date) {
@ -51,10 +51,9 @@ class UtilDate {
}
/**
*
* @param date Date to be converted
* @return String of the converted date
*
* <p>
* Convert a date to a String using a SimpleDateFormat
*/
static String date2String(Date date, DateFormat dateFormat) {
@ -68,23 +67,21 @@ class UtilDate {
}
/**
*
* @param dateString string representing a Date to be conveted
* @return date Date after convertion
*
* @param dateString string representing a Date to be converted
* @return date Date after conversion
* <p>
* Convert String date into Date
*/
static Date string2Date(String dateString) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.FRANCE);
ParsePosition pos = new ParsePosition(0);
return dateFormat.parse(dateString,pos);
return dateFormat.parse(dateString, pos);
}
/**
*
* @param date start date
* @return int numbers of days between date and today
*
* <p>
* Number of days between date (older than today) and today
*/
static int nbOfDaysBetweenDateAndToday(Date date) {

View file

@ -22,9 +22,10 @@
<string name="detail_view">Vue de détail</string>
<string name="scan_action">Scanner un code barre de médicament</string>
<string name="save_button">Save</string>
<string name="flashlighButton">Toggle Flash</string>
<string name="enter_cip_13_here">Enter cip 13 here..</string>
<string name="flashlighButton">Allumez/Eteindre le Flash</string>
<string name="enter_cip_13_here">Entrez le code CIP 13 ici…</string>
<string name="enter_cip_13">Enter CIP 13</string>
<string name="input_cip13">Saisissez le code CIP13 avec le clavier</string>
<string name="pharmacy">Vous devez passer à la pharmacie</string>
</resources>
</resources>