Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #1106 (take two) #1112

Merged
merged 2 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions onebusaway-android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
<!--<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>-->
<!-- For reminders - SCHEDULE_EXACT_ALARM is required when targeting Android 12 or higher-->
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
<!-- For notifications, POST_NOTIFICATIONS is required when targeting Android 12 or higher-->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

<application
android:allowBackup="false"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.Log;

import androidx.core.app.NotificationCompat;
Expand Down Expand Up @@ -286,28 +287,86 @@ public static void scheduleAll(Context context, boolean startForeground) {
}

public static void pollTrip(Context context, Uri alertUri, long triggerTime) {
Intent intent = new Intent(TripService.ACTION_POLL, alertUri,
context, AlarmReceiver.class);
PendingIntent alarmIntent = createAlarmIntent(context, alertUri);
scheduleAlarm(context, triggerTime, alarmIntent);
}

/**
* Informs the caller as to whether alarms can be scheduled, or if permission
* must be granted first.
*
* @apiNote On Android pre-Tiramisu, you could schedule an exact alarm without requesting
* any additional permissions. Newer versions of the OS, on the other hand,
* require the user to grant explicit permission.
*
* @param context
* @return True if an alarm can be scheduled and false if permission is required first.
*/
public static boolean canScheduleExactAlarms(Context context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
return true;
}

AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
return alarmManager.canScheduleExactAlarms();
}

/**
* Requests the SCHEDULE_EXACT_ALARM permission on Android Tiramisu and newer.
* Does nothing on older versions.
* @param context
*/
public static void requestScheduleExactAlarmsPermission(Context context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
return;
}

Intent intent = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);
intent.setData(Uri.parse("package:" + context.getPackageName()));
context.startActivity(intent);
}

private static void scheduleAlarm(Context context, long triggerTime, PendingIntent alarmIntent) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);

// Android 5.1 and earlier just uses `set()`.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
alarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, alarmIntent);
return;
}

// Android 6 - 12 can directly call `setExactAndAllowWhileIdle()`.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
setExactAlarm(alarmManager, triggerTime, alarmIntent);
return;
}

if (canScheduleExactAlarms(context)) {
setExactAlarm(alarmManager, triggerTime, alarmIntent);
}
}

private static void setExactAlarm(AlarmManager alarmManager, long triggerTime, PendingIntent alarmIntent) {
// Try to cut through Doze so alarm still triggers - See #558
// Note that we intentionally do NOT use alarm.setAlarmClock() because this creates
// an alarm in the user's status bar and notification drawer which can be annoying - see
// https://stackoverflow.com/questions/34699662/how-does-alarmmanager-alarmclockinfos-pendingintent-work
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, alarmIntent);
}
}

private static PendingIntent createAlarmIntent(Context context, Uri alertUri) {
Intent intent = new Intent(TripService.ACTION_POLL, alertUri, context, AlarmReceiver.class);

int flags;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
flags = PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE;
} else {
flags = PendingIntent.FLAG_ONE_SHOT;
}
PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 0,
intent, flags);

AlarmManager alarm =
(AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Try to cut through Doze so alarm still triggers - See #558
// Note that we intentionally do NOT use alarm.setAlarmClock() because this creates
// an alarm in the user's status bar and notification drawer which can be annoying - see
// https://stackoverflow.com/questions/34699662/how-does-alarmmanager-alarmclockinfos-pendingintent-work
alarm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, alarmIntent);
} else {
alarm.set(AlarmManager.RTC_WAKEUP, triggerTime, alarmIntent);
}
return PendingIntent.getBroadcast(context, 0, intent, flags);
}

public static void notifyTrip(Context context, Uri alertUri, String notifyTitle, String notifyText) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package org.onebusaway.android.ui;

import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ContentResolver;
Expand All @@ -25,7 +27,9 @@
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.LayoutInflater;
Expand All @@ -50,13 +54,16 @@
import java.util.List;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.CursorLoader;
import androidx.loader.content.Loader;

import static org.onebusaway.android.util.PermissionUtils.NOTIFICATION_PERMISSION_REQUEST;

public class TripInfoActivity extends AppCompatActivity {

private static final String TAG = "TripInfoActivity";
Expand Down Expand Up @@ -415,6 +422,19 @@ public void saveTrip() {
// Reminder time
// Repeats
//

// Make sure that the user has granted permission for notifications
// and exact alarms.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (!TripService.canScheduleExactAlarms(getActivity())) {
showRequestAlarmsPermissionDialog(getActivity());
return;
}
ActivityCompat.requestPermissions(this.getActivity(),
new String[] {Manifest.permission.POST_NOTIFICATIONS},
NOTIFICATION_PERMISSION_REQUEST);
}

View view = getView();
final Spinner reminderView = (Spinner) view.findViewById(R.id.trip_info_reminder_time);
final TextView nameView = (TextView) view.findViewById(R.id.name);
Expand Down Expand Up @@ -455,6 +475,27 @@ public void saveTrip() {
finish();
}

private void showRequestAlarmsPermissionDialog(Context context) {
AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setTitle(R.string.trip_info_grant_exact_alarms_permission_title)
.setCancelable(true)
.setMessage(R.string.trip_info_grant_exact_alarms_permission_message)
.setPositiveButton(
R.string.trip_info_grant_exact_alarms_permission_positive_button,
(dialog, which) -> {
TripService.requestScheduleExactAlarmsPermission(context);
dialog.dismiss();
}
)
.setNegativeButton(
R.string.trip_info_grant_exact_alarms_permission_negative_button,
(dialog, which) -> {
dialog.dismiss();
});
AlertDialog alertDialog = builder.create();
alertDialog.show();
}

void showReminderDaysDialog() {
final boolean[] checks = ObaContract.Trips.daysToArray(mReminderDays);
Bundle args = new Bundle();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class PermissionUtils {
public static final int SAVE_BACKUP_PERMISSION_REQUEST = 2;
public static final int RESTORE_BACKUP_PERMISSION_REQUEST = 3;
public static final int BACKGROUND_LOCATION_PERMISSION_REQUEST = 4;
public static final int NOTIFICATION_PERMISSION_REQUEST = 5;

public static final String[] LOCATION_PERMISSIONS = {
Manifest.permission.ACCESS_FINE_LOCATION,
Expand Down
4 changes: 4 additions & 0 deletions onebusaway-android/src/main/res/values-es/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1047,4 +1047,8 @@
<string name="layers_speedial_bikeshare_label">Bicicleta Compartida</string>
<string name="bike_station_title">Toca para reservar una bicicleta</string>
<string name="floating_bike_title">Toca para reservar esta bicicleta</string>
<string name="trip_info_grant_exact_alarms_permission_title">Se requiere permiso</string>
<string name="trip_info_grant_exact_alarms_permission_message">Debes otorgar permiso a la aplicación para programar un recordatorio.</string>
<string name="trip_info_grant_exact_alarms_permission_positive_button">Otorgar permiso</string>
<string name="trip_info_grant_exact_alarms_permission_negative_button">Cancelar</string>
</resources>
4 changes: 4 additions & 0 deletions onebusaway-android/src/main/res/values-fi/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -631,4 +631,8 @@
(https://github.com/google/material-design-icons), "
"licensed under Apache v2.0 (https://www.apache.org/licenses/LICENSE-2.0).\n\n"
</string>
<string name="trip_info_grant_exact_alarms_permission_title">Lupa tarvitaan</string>
<string name="trip_info_grant_exact_alarms_permission_message">Sinun täytyy antaa sovellukselle lupa muistutuksen asettamiseen.</string>
<string name="trip_info_grant_exact_alarms_permission_positive_button">Myönnä lupa</string>
<string name="trip_info_grant_exact_alarms_permission_negative_button">Peruuta</string>
</resources>
4 changes: 4 additions & 0 deletions onebusaway-android/src/main/res/values-it/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -909,4 +909,8 @@
<string name="travel_behavior_dialog_opt_out_title">Rifiutare partecipazione alla ricerca?</string>
<string name="travel_behavior_dialog_opt_out_message">Se scegli di non partecipare alla ricerca non riceveremo più informazioni riguardo alle tue abitudini di viaggio per migliorare il servizio di trasporto pubblico.</string>
<string name="cancel">Annulla</string>
<string name="trip_info_grant_exact_alarms_permission_title">Permesso necessario</string>
<string name="trip_info_grant_exact_alarms_permission_message">Devi concedere all\'app il permesso di pianificare un promemoria.</string>
<string name="trip_info_grant_exact_alarms_permission_positive_button">Concedi il permesso</string>
<string name="trip_info_grant_exact_alarms_permission_negative_button">Annulla</string>
</resources>
4 changes: 4 additions & 0 deletions onebusaway-android/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,10 @@
<string name="trip_info_option_showroute">Show Route</string>
<string name="trip_info_option_showstop">Show Stop</string>
<string name="trip_info_separator">-</string>
<string name="trip_info_grant_exact_alarms_permission_title">Permission Needed</string>
<string name="trip_info_grant_exact_alarms_permission_message">You must grant the app permission to schedule a reminder.</string>
<string name="trip_info_grant_exact_alarms_permission_positive_button">Grant Permission</string>
<string name="trip_info_grant_exact_alarms_permission_negative_button">Cancel</string>

<!-- Occupancy -->
<string name="historically_full">Historically full</string>
Expand Down
Loading