Otter notter
- By Fox Mulder
- VIP Lounge
- 14 Replies
Yup the whole idea is when you drop it the outer rubber cushions the impact. Regardless of model they're all made that way.
Follow along with the video below to see how to install our site as a web app on your home screen.
Note: This feature may not be available in some browsers.
package com.example.sprite.half_lifetimer;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
public class BootServiceStart extends BroadcastReceiver {
public void onReceive(Context context, Intent arg1) {
Intent intent = new Intent(context , NotificationService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
if (GlobalMisc.Debugging) {
Log.i("Halflife.BootServiceStart", "Attempted to start NotificationService");
}
}
}
package com.example.sprite.half_lifetimer;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import androidx.work.PeriodicWorkRequest;
import androidx.work.WorkManager;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
public class NotificationService extends Service {
public static HashMap<Integer, Boolean> firedNotifications = new HashMap<>();
public static LocalDateTime lastNotificationLoopLDT = null;
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
/**
* Method handles creation of a NotificationChannel and database
* initialization (for this particular subset of the code), then passing
* control off to notificationLoop().
*/
public void onCreate() {
startForeground(31337, buildForegroundNotification());
if (GlobalMisc.Debugging) {
Log.i("Halflife.NotificationService.onCreate", "Started NotificationService");
}
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel chan = new NotificationChannel(
"taper-n-clearing-talk", "taper-n-clearing",
NotificationManager.IMPORTANCE_NONE);
chan.setDescription("Notifications for Taper dosages and Substance clearance");
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(chan);
}
//get the database ready
try {
Permanence.Misc.init(/*NotificationService.this*/ getApplicationContext());
} catch (Exception ex) {
Log.e("Halflife.notificationLoop", "Unable to init database: " +
ex.toString());
}
if (GlobalMisc.Debugging) {
Log.i("Halflife.onCreate", "all valid tapers: " +
Permanence.Tapers.loadAllValidTapers(getApplicationContext()).toString());
}
//notificationLoop();
PeriodicWorkRequest notificationsRequest =
new PeriodicWorkRequest.Builder(NotificationWorker.class, 15, TimeUnit.MINUTES)
.build();
WorkManager.getInstance()
.enqueue(notificationsRequest);
}
private Notification buildForegroundNotification() {
NotificationCompat.Builder b=new NotificationCompat.Builder(this);
b.setOngoing(true)
.setContentTitle("HLT Foreground Service")
.setContentText("Giving Half-life Timer foreground priority")
.setChannelId("taper-n-clearing-talk")
.setSmallIcon(getApplicationContext().getResources().getIdentifier(
"plus_medical_blue","drawable",
getApplicationContext().getPackageName()));
return(b.build());
}
}
package com.example.sprite.half_lifetimer;
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.util.Log;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
public class NotificationWorker extends Worker {
private boolean notificationDebugging = false;
public NotificationWorker(@NonNull Context context, @NonNull WorkerParameters params) {
super(context, params);
}
@Override
public Result doWork() {
LocalDateTime nextScheduledDosage;
long adminDurationMinutes;
if (!notificationDebugging) {
if (GlobalMisc.NotificationsEnabled) {
//taper notification loop
for (Taper taper : Permanence.Tapers.loadAllValidTapers(getApplicationContext())) {
//this will handle if any tapers have been added since inception
if (!NotificationService.firedNotifications.containsKey(taper.getId())) {
NotificationService.firedNotifications.put(taper.getId(), false);
}
//if this is a constrained taper, but we're outside of the window, just
//go on to the next taper
if (taper.isConstrained() && !taper.inConstraintHours()) {
Log.i("Halflife.notificationLoop",
"skipping " + taper.toString() +
" (outside of hourly constraints)");
continue;
}
if (!NotificationService.firedNotifications.get(taper.getId())) {
try {
nextScheduledDosage = taper.findNextScheduledDosageLDT();
if (!taper.isConstrained()) {
Log.i("Halflife.notificationLoop",
"working with unconstrained taper");
adminDurationMinutes = Duration.ofDays(1).dividedBy(
taper.getAdminsPerDay()).toMinutes();
} else {
Log.i("Halflife.notificationLoop",
"working with constrained taper");
//not sure if this is necessary or not, but might as well
//throw it in since the goddamned code is too complex for me
//to follow right now down below
LocalTime nextDosageTime =
nextScheduledDosage.toLocalTime();
if (nextDosageTime.isBefore(taper.getStartHour()) ||
nextDosageTime.isAfter(taper.getEndHour())) {
Log.i("notificationLoop",
"skipping " + taper.toString() +
" (outside of constraint hours)");
continue;
}
//this part, of course, is necessary
adminDurationMinutes =
Duration.between(taper.getStartHour(),
taper.getEndHour()).dividedBy(
taper.getAdminsPerDay())
.toMinutes();
}
if (GlobalMisc.Debugging) {
Log.i("Halflife.notificationLoop", "Checking taper: " +
taper.getName());
Log.i("Halflife.notificationLoop", "nextScheduledDosage " +
"contains: " + nextScheduledDosage.toString());
}
if (((NotificationService.lastNotificationLoopLDT != null) &&
nextScheduledDosage.isAfter(
NotificationService.lastNotificationLoopLDT) &&
nextScheduledDosage.isBefore(
LocalDateTime.now().plusMinutes(
(adminDurationMinutes / 5)))) ||
(nextScheduledDosage.isAfter(
LocalDateTime.now().minusMinutes(1)) &&
nextScheduledDosage.isBefore(
LocalDateTime.now().plusMinutes(
(adminDurationMinutes / 5))))) {
fireTaperNotification(taper);
//set firedNotifications to reflect that we sent this
//notification
NotificationService.firedNotifications.replace(taper.getId(), true);
} else if (GlobalMisc.Debugging) {
Log.i("Halflife.notificationLoop",
"not displaying notification as per " +
"datetime constraints");
}
} catch (Exception ex) {
Log.e("Halflife.notificationLoop",
"Issue finding next scheduled dosage: " +
ex.toString());
return Result.failure();
}
}
}
} else {
GlobalMisc.debugMsg("NotificationWorker:doWork",
"Would have just gone into substance taper notification loop");
}
if (GlobalMisc.NotificationsEnabled) {
//substance clearing notification loop
//LocalDateTime fiveMinAgo = LocalDateTime.now().minusMinutes(5);
for (Substance sub : Permanence.Subs.loadUnarchivedSubstances(
getApplicationContext())) {
if (GlobalMisc.Debugging) {
Log.i("Halflife.notificationLoop",
"Checking sub clearance: " + sub.getCommon_name());
}
//has this substance cleared within the last 5 minutes?
LocalDateTime clearedAt = sub.getFullEliminationLDT();
if (clearedAt != null) {
if (NotificationService.lastNotificationLoopLDT != null) {
if (clearedAt.isAfter(NotificationService.lastNotificationLoopLDT) &&
clearedAt.isBefore(LocalDateTime.now())) {
//fire the notification
try {
fireSubClearedNotification(sub);
} catch (Exception ex) {
Log.i("Halflife.doWork", ex.toString());
return Result.failure();
}
}
}
}
}
} else {
GlobalMisc.debugMsg("NotificationWorker:doWork",
"Would have just gone into substance clearing notification loop");
}
} else {
Log.i("Halflife.notificationLoop", "In notification debugging " +
"mode");
try {
fireTaperNotification(null);
} catch (Exception ex) {
Log.i("Halflife.doWork", ex.toString());
return Result.failure();
}
}
NotificationService.lastNotificationLoopLDT = LocalDateTime.now();
return Result.success();
}
/**
* Method handles the actual building of the notification regarding
* the applicable taper, and shows it unless our handy HashMap
* 'firedNotifications' shows that there is already a notification
* present for this particular taper.
*
* @param taper the taper to display notification for
*/
private void fireTaperNotification(Taper taper) throws Exception {
Context ctxt = getApplicationContext();
float currentDosageScheduled = taper.findCurrentScheduledDosageAmount();
//here's the legitimate meat 'n potatoes for firing a notification
try {
//if we've already blown the dosage required for the next administration, just skip this
//one
if (currentDosageScheduled <= 0) {
Log.d("fireTaperNotification", "More dosage taken than needs to be " +
"for the current taper step; skipping this taper administration.");
return;
}
Intent intent = new Intent(ctxt, AdminData.class);
intent.putExtra("SUB_NDX",
GlobalMisc.getSubListPositionBySid(taper.getSid()));
intent.putExtra("NOTIFICATION_BASED", true);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(ctxt);
stackBuilder.addParentStack(SubData.class);
stackBuilder.addNextIntentWithParentStack(intent);
Intent delIntent = new Intent(ctxt, NotificationDismissalReceiver.class);
delIntent.putExtra("TAPER", true);
delIntent.putExtra("SUB_ID", taper.getSid());
PendingIntent pendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent pendingDelIntent = PendingIntent.getBroadcast(ctxt, 0,
delIntent, PendingIntent.FLAG_UPDATE_CURRENT);
LocalDateTime latestUsageLDT;
LocalDateTime todaysOpeningConstraintLDT;
boolean beforeOpeningConstraint = false;
latestUsageLDT = Converters.toLocalDateTime(
Permanence.Admins.getLatestUsageTimestampBySid(taper.getSid()));
if (taper.isConstrained()) {
todaysOpeningConstraintLDT =
LocalDateTime.now().withHour(taper.getStartHour().getHour())
.withMinute(taper.getStartHour().getMinute())
.withSecond(0);
if (latestUsageLDT.plus(taper.getTotalConstraintDuration()).isBefore(
todaysOpeningConstraintLDT)) {
beforeOpeningConstraint = true;
}
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(
ctxt, "halflife")
.setContentTitle("Half-life Timer Taper " + taper.getName())
//note that the above line, right after "Due since: " +, will
//end up displaying the epoch start date for a taper on a
//substance that has no administrations whatsoever
.setSmallIcon(ctxt.getResources().getIdentifier("plus_medical_blue",
"drawable", ctxt.getPackageName()))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setDeleteIntent(pendingDelIntent)
.setAutoCancel(true);
long rawTimestamp = Permanence.Admins.getLatestUsageTimestampBySid(taper.getSid());
GlobalMisc.debugMsg("fireTaperNotification",
"Permanence.Admins.getLatestUsageTimestampBySid returns: " +
rawTimestamp);
if (Converters.toLocalDateTime(rawTimestamp).isBefore(
LocalDateTime.of(1980, 1, 1, 0, 0, 0))) {
builder.setContentText("Due: " +
String.format("%.2f", currentDosageScheduled) +
Permanence.Subs.getUnitsBySID(taper.getSid()) + "/" +
Permanence.Subs.loadSubstanceById(
taper.getSid()).getCommon_name() + "\n" +
"Due now");
} else if (beforeOpeningConstraint) {
builder.setContentText("Due:" +
currentDosageScheduled +
Permanence.Subs.getUnitsBySID(taper.getSid()) + " of " +
Permanence.Subs.loadSubstanceById(
taper.getSid()).getCommon_name() + "\n" +
"Due since: " +
LocalDateTime.now().withHour(taper.getStartHour().getHour())
.withMinute(taper.getStartHour().getMinute())
.withSecond(0));
} else {
builder.setContentText("Due:" +
currentDosageScheduled +
Permanence.Subs.getUnitsBySID(taper.getSid()) + " of " +
Permanence.Subs.loadSubstanceById(
taper.getSid()).getCommon_name() + "\n" +
"Due since: " +
Converters.toLocalDateTime(
Permanence.Admins.getLatestUsageTimestampBySid(
taper.getSid())).plus(
Duration.ofDays(1).dividedBy(
taper.getAdminsPerDay())));
}
NotificationManagerCompat notificationManager =
NotificationManagerCompat.from(ctxt);
notificationManager.notify(1, builder.build());
if (GlobalMisc.Debugging || notificationDebugging) {
Log.i("Halflife.fireNotification",
"attempted to send taper notification");
}
} catch (Exception ex) {
Log.e("Halflife.fireNotification",
"Something broke in taper notification: " + ex.toString());
throw new Exception("taper notification broke");
}
}
private void fireSubClearedNotification(Substance sub) throws Exception {
Context ctxt = getApplicationContext();
try {
Intent intent = new Intent(ctxt,
SubsRankedByLastUsage.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
ctxt, 1, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(
ctxt, "halflife")
.setContentTitle("Half-life Timer Cleared: " + sub.getCommon_name())
.setContentText(sub.getCommon_name() + " cleared at " +
sub.getFullEliminationLDT().toString())
.setSmallIcon(ctxt.getResources().getIdentifier("plus_medical_blue",
"drawable", ctxt.getPackageName()))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setAutoCancel(true);
NotificationManagerCompat notificationManager =
NotificationManagerCompat.from(ctxt);
notificationManager.notify(1, builder.build());
if (GlobalMisc.Debugging || notificationDebugging) {
Log.i("Halflife.fireNotification",
"attempted to send sub clearednotification");
}
} catch (Exception ex) {
Log.e("Halflife.fireNotification",
"Something broke in sub cleared notification: " + ex.toString());
throw new Exception("sub cleared notification broke");
}
}
}

Nope, the phone will not allow me to suppress this notification (other notifications, yes, not this one). I don't have a third party dialer, and it started at a time when I didn't have a third party Contacts app either. This is entirely Google: Google's Phone app, Google Play Services wanting me to give it permissions, a Pixel phone. I suspect I'm unusual in that I own a Pixel but don't use Assistant, so disallow Play Services permissions that others would grant it. It may also be that only Google's phone app interacts this way with Google Play Services. At least it's easier to ignore than when it first started, when it would beep during calls, but it does add to my impression of Google as a dishonest and self-serving corporation (though to be fair all corporations are self-serving and that usually leads them to dishonesty of some degree or other).You can also tick Never ask again, or similar. This would also only happen if you had a 3rd party contacts or dialer app installed. It is probable that this is the source of your issues, You should definitely go to apps, look at Contacts and Dialer. Remove the ones that can be removed. Reboot to be sure.
ok so looks like according @MrJavi 's link, there really is no solution.....at least in that thread.
so just to be sure, you can't get into recovery mode? if you can, then you can try a factory reset.
if not then you can try flashing a firmware update:
https://forum.xda-developers.com/Mi-9/how-to/firmware-xiaomi-mi-9-t3955313
https://flashxiaomi.com/download-install-miui-rom-for-xiaomi-mi-9-all-miui-firmwares/
i do not have your phone, so i'm not very familiar with the process, unfortunately




I wouldn't thought of that..that's a lovely way of fixing when forgetting deletion of pesquy data..nice advice..I always practice the deleting before uninstalling(except I've forgotten once or twice)..but have never reinstalled to delete what was left behind..except for the Busybox.
Very clever!