Bangle.js: Bump flavor targetSdkVersion to 31

This also touches parts of the app not only used for bangle.js.
E.g. pending intents gets new flags from SDK 23 inclusive.
Bluetooth permissions are updated to work on SDK 31.
Permission handling is updated to the new way for doing it with
introduction of a new function. This is called for newer sdk versions.

bump Bangle.js flavor targetSdkVersion to 31

update comments re SDK 31

set the 'exported=true' I introduced to false instead - except for three places

add uses-permission for handling bluetooth in order to work on api >30

add if-blocks adding FLAG_IMMUTABLE to PendingIntents on api >30

add link to bluetooth documentation

Add comment to banglejs manifest. Add requirement annotation to ControlCenterv

bump compileSdkVersion to 31

add "OpenAppSettings" permission popup while working out individual permission popups on android 13

if SDK < 31 do permissions one by one, else send user to app info page to switch permissions manually

working solution, but needs cleaning

do some cleaning, not done though

remove some logging

remove import Log

tweak and remove toasts in new permissions handling

Change conditions `> Build.VERSION_CODES.Q` to `>= Build.VERSION_CODES.R` matching the style used everywhere else

Revert "Change conditions `> Build.VERSION_CODES.Q` to `>= Build.VERSION_CODES.R` matching the style used everywhere else"

This reverts commit 2929629ff43fbb685eb3d15e42459f321f68fa11.

Revert "add if-blocks adding FLAG_IMMUTABLE to PendingIntents on api >30"

This reverts commit ed8e1df7bb8b71fee745fbf9d10747d47c8f6cb8.

Pending intents gets `PendingIntent.FLAG_IMMUTABLE` if `(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)`.

Bangle.js: undo `@RequiresApi` code R

... to remove error in Android Studio where declared required api was
higher then minSDK version.

Use FLAG_MUTABLE for reply to test notification

This should fix Gadgetbridge crashing when replying to the test
notification from the debug activity. As reported here:
https://codeberg.org/Freeyourgadget/Gadgetbridge/pulls/2924#issuecomment-917282

Change to use FLAG_IMMUTABLE/_MUTABLE from SDK 23

... as suggested by Android Studio. This is supposed to make the app
more secure by not allowing certain changes to pending intents where
they are not expected. If I understood correctly.

Add PendingIntentUtils class to manage mutability
This commit is contained in:
Ganblejs 2022-10-09 14:53:04 +02:00
parent 514f983299
commit 7d1de4a5e8
13 changed files with 230 additions and 61 deletions

View File

@ -46,7 +46,7 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
compileSdkVersion 29
compileSdkVersion 31
buildToolsVersion "31.0.0"
defaultConfig {
@ -101,8 +101,8 @@ android {
resValue "string", "about_activity_title", "@string/about_activity_title_banglejs_main"
resValue "string", "about_description", "@string/about_description_banglejs_main"
resValue "string", "gadgetbridge_running", "@string/gadgetbridge_running_banglejs_main"
targetSdkVersion 30 // Bangle.js flavor only - We need SDK 30 for play store
// Note: app/src/banglejs/AndroidManifest.xml contains some extra permissions we need to make SDK 30 work
targetSdkVersion 31 // Bangle.js flavor only - We need SDK 31 for updates pushed to Play Store from 2022-11-01
// Note: app/src/banglejs/AndroidManifest.xml contains some extra permissions we need to make SDK 30 and up work
}
}
@ -223,6 +223,8 @@ dependencies {
implementation "androidx.legacy:legacy-support-v4:1.0.0"
implementation "androidx.gridlayout:gridlayout:1.0.0"
implementation "androidx.palette:palette:1.0.0"
implementation "androidx.activity:activity:1.4.0"
implementation "androidx.fragment:fragment:1.4.0"
implementation "com.google.android.material:material:1.4.0"
implementation 'com.google.android.flexbox:flexbox:3.0.0'

View File

@ -12,4 +12,34 @@
-->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<!-- https://developer.android.com/guide/topics/connectivity/bluetooth/permissions -->
<!-- Request legacy Bluetooth permissions on older devices. -->
<!-- We do this in the main Android Manifest already, but without android:maxSdkVersion="30".
<uses-permission android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
-->
<!-- Needed only if your app looks for Bluetooth devices.
If your app doesn't use Bluetooth scan results to derive physical
location information, you can strongly assert that your app
doesn't derive physical location.
We need to ask the user for this permission, if it's not granted the app crashes when
clicking the plus-button. -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<!-- Needed only if your app makes the device discoverable to Bluetooth
devices. -->
<!-- <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> -->
<!-- Needed only if your app communicates with already-paired Bluetooth
devices. -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- Needed only if your app uses Bluetooth scan results to derive physical location. -->
<!-- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> -->
</manifest>

View File

@ -77,7 +77,8 @@
android:name=".activities.ControlCenterv2"
android:label="@string/title_activity_controlcenter"
android:theme="@style/SplashTheme"
android:launchMode="singleTop">
android:launchMode="singleTop"
android:exported="true">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
@ -134,7 +135,8 @@
<activity
android:name=".activities.FwAppInstallerActivity"
android:label="@string/title_activity_fw_app_insaller"
android:parentActivityName=".activities.ControlCenterv2">
android:parentActivityName=".activities.ControlCenterv2"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@ -355,7 +357,8 @@
<service
android:name=".externalevents.NotificationListener"
android:label="@string/app_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
android:exported="false">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
@ -368,7 +371,8 @@
<receiver
android:name=".externalevents.GenericWeatherReceiver"
android:enabled="true">
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="nodomain.freeyourgadget.gadgetbridge.ACTION_GENERIC_WEATHER" />
</intent-filter>
@ -376,13 +380,15 @@
<receiver
android:name=".externalevents.WeatherNotificationReceiver"
android:enabled="true">
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="ru.gelin.android.weather.notification.ACTION_WEATHER_UPDATE_2" />
</intent-filter>
</receiver>
<activity android:name=".externalevents.WeatherNotificationConfig">
<activity android:name=".externalevents.WeatherNotificationConfig"
android:exported="false">
<intent-filter>
<action android:name="ru.gelin.android.weather.notification.ACTION_WEATHER_SKIN_PREFERENCES" />
</intent-filter>
@ -390,7 +396,8 @@
<receiver
android:name=".externalevents.AutoStartReceiver"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
android:permission="android.permission.RECEIVE_BOOT_COMPLETED"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
@ -583,7 +590,8 @@
<receiver
android:name=".SleepAlarmWidget"
android:label="@string/appwidget_sleep_alarm_widget_label">
android:label="@string/appwidget_sleep_alarm_widget_label"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="nodomain.freeyourgadget.gadgetbridge.SLEEP_ALARM_WIDGET_CLICK" />
@ -595,7 +603,8 @@
</receiver>
<receiver
android:name=".Widget"
android:label="@string/widget_listing_label">
android:label="@string/widget_listing_label"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.appwidget.action.APPWIDGET_DELETED" />
@ -614,13 +623,15 @@
android:launchMode="singleInstance"
android:theme="@style/Theme.AppCompat.Light.Dialog" />
<activity android:name=".activities.WidgetConfigurationActivity">
<activity android:name=".activities.WidgetConfigurationActivity"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
</intent-filter>
</activity>
<activity android:name=".activities.SleepAlarmWidgetConfigurationActivity">
<activity android:name=".activities.SleepAlarmWidgetConfigurationActivity"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
</intent-filter>
@ -632,7 +643,8 @@
android:clearTaskOnLaunch="true"
android:label="@string/app_configure"
android:launchMode="singleTask"
android:parentActivityName=".activities.appmanager.AppManagerActivity">
android:parentActivityName=".activities.appmanager.AppManagerActivity"
android:exported="true">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2" />
@ -719,7 +731,8 @@
android:name=".activities.GpxReceiverActivity"
android:label="@string/gpx_receiver_activity_title"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden">
android:windowSoftInputMode="stateHidden"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.SEND_MULTIPLE" />

View File

@ -38,6 +38,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.PendingIntentUtils;
import nodomain.freeyourgadget.gadgetbridge.util.WidgetPreferenceStorage;
/**
@ -62,8 +63,8 @@ public class SleepAlarmWidget extends AppWidgetProvider {
Intent intent = new Intent(context, SleepAlarmWidget.class);
intent.setAction(ACTION_CLICK);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
PendingIntent clickPI = PendingIntent.getBroadcast(
context, appWidgetId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent clickPI = PendingIntentUtils.getBroadcast(
context, appWidgetId, intent, PendingIntent.FLAG_UPDATE_CURRENT, false);
views.setOnClickPendingIntent(R.id.sleepalarmwidget_text, clickPI);
// Instruct the widget manager to update the widget
@ -147,8 +148,8 @@ public class SleepAlarmWidget extends AppWidgetProvider {
AlarmManager am = (AlarmManager) packageContext.getSystemService(Context.ALARM_SERVICE);
// TODO: launch the alarm configuration activity when clicking the alarm in the status bar
Intent intent = new Intent(packageContext, ConfigureAlarms.class);
PendingIntent pi = PendingIntent.getBroadcast(packageContext, 0, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent pi = PendingIntentUtils.getBroadcast(packageContext, 0, intent,
PendingIntent.FLAG_CANCEL_CURRENT, false);
am.setAlarmClock(new AlarmManager.AlarmClockInfo(triggerTime, pi), pi);
}
}

View File

@ -42,6 +42,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.RemoteViews;
@ -67,6 +68,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.FormatUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.PendingIntentUtils;
import nodomain.freeyourgadget.gadgetbridge.util.WidgetPreferenceStorage;
public class Widget extends AppWidgetProvider {
@ -108,25 +110,25 @@ public class Widget extends AppWidgetProvider {
Intent intent = new Intent(context, Widget.class);
intent.setAction(WIDGET_CLICK);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
PendingIntent refreshDataIntent = PendingIntent.getBroadcast(
context, appWidgetId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent refreshDataIntent = PendingIntentUtils.getBroadcast(
context, appWidgetId, intent, PendingIntent.FLAG_UPDATE_CURRENT, false);
views.setOnClickPendingIntent(R.id.todaywidget_header_container, refreshDataIntent);
//open GB main window
Intent startMainIntent = new Intent(context, ControlCenterv2.class);
PendingIntent startMainPIntent = PendingIntent.getActivity(context, 0, startMainIntent, 0);
PendingIntent startMainPIntent = PendingIntentUtils.getActivity(context, 0, startMainIntent, 0, false);
views.setOnClickPendingIntent(R.id.todaywidget_header_icon, startMainPIntent);
//alarms popup menu
Intent startAlarmListIntent = new Intent(context, WidgetAlarmsActivity.class);
startAlarmListIntent.putExtra(GBDevice.EXTRA_DEVICE, deviceForWidget);
PendingIntent startAlarmListPIntent = PendingIntent.getActivity(context, appWidgetId, startAlarmListIntent, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent startAlarmListPIntent = PendingIntentUtils.getActivity(context, appWidgetId, startAlarmListIntent, PendingIntent.FLAG_UPDATE_CURRENT, false);
views.setOnClickPendingIntent(R.id.todaywidget_header_alarm_icon, startAlarmListPIntent);
//charts
Intent startChartsIntent = new Intent(context, ChartsActivity.class);
startChartsIntent.putExtra(GBDevice.EXTRA_DEVICE, deviceForWidget);
PendingIntent startChartsPIntent = PendingIntent.getActivity(context, appWidgetId, startChartsIntent, PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent startChartsPIntent = PendingIntentUtils.getActivity(context, appWidgetId, startChartsIntent, PendingIntent.FLAG_CANCEL_CURRENT, false);
views.setOnClickPendingIntent(R.id.todaywidget_bottom_layout, startChartsPIntent);
long[] dailyTotals = getSteps(deviceForWidget);

View File

@ -32,13 +32,17 @@ import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
@ -284,7 +288,7 @@ public class ControlCenterv2 extends AppCompatActivity
}
}
if (!android.provider.Settings.canDrawOverlays(getApplicationContext())) {
if (!Settings.canDrawOverlays(getApplicationContext())) {
// If diplay over other apps access hasn't been granted
// Put up a dialog explaining why we need permissions (Polite, but also Play Store policy)
// When accepted, we open the Activity for permission to display over other apps.
@ -294,6 +298,10 @@ public class ControlCenterv2 extends AppCompatActivity
}
}
// Put up a dialog explaining why we need permissions (Polite, but also Play Store policy)
// When accepted, we open the Activity for permission to display over other apps.
// Check all the other permissions that we need to for Android M + later
checkAndRequestPermissions(true);
}
@ -478,12 +486,27 @@ public class ControlCenterv2 extends AppCompatActivity
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && Build.VERSION.SDK_INT <= Build.VERSION_CODES.S) {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_DENIED) {
wantedPermissions.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.QUERY_ALL_PACKAGES) == PackageManager.PERMISSION_DENIED) {
wantedPermissions.add(Manifest.permission.QUERY_ALL_PACKAGES);
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_DENIED) {
wantedPermissions.add(Manifest.permission.BLUETOOTH_SCAN);
}
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_DENIED) {
wantedPermissions.add(Manifest.permission.BLUETOOTH_CONNECT);
}
}
if (BuildConfig.INTERNET_ACCESS) {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.INTERNET) == PackageManager.PERMISSION_DENIED) {
wantedPermissions.add(Manifest.permission.INTERNET);
@ -512,22 +535,30 @@ public class ControlCenterv2 extends AppCompatActivity
if (!wantedPermissions.isEmpty()) {
if (showDialogFirst) {
// Show a dialog - thus will then call checkAndRequestPermissions(false)
// Show a dialog - this will then call checkAndRequestPermissions(false)
DialogFragment dialog = new LocationPermissionsDialogFragment();
dialog.show(getSupportFragmentManager(), "LocationPermissionsDialogFragment");
//requestMultiplePermissionsLauncher.launch(wantedPermissions.toArray(new String[0]));
} else {
GB.toast(this, getString(R.string.permission_granting_mandatory), Toast.LENGTH_LONG, GB.ERROR);
ActivityCompat.requestPermissions(this, wantedPermissions.toArray(new String[0]), 0);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
ActivityCompat.requestPermissions(this, wantedPermissions.toArray(new String[0]), 0);
} else {
requestMultiplePermissionsLauncher.launch(wantedPermissions.toArray(new String[0]));
//ActivityCompat.requestPermissions(this, wantedPermissions.toArray(new String[0]), 0); //Actually this still works if I test it, not sure if the new way is more reliable or not...
}
}
}
}
// HACK: On Lineage we have to do this so that the permission dialog pops up
if (fakeStateListener == null) {
fakeStateListener = new PhoneStateListener();
TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
telephonyManager.listen(fakeStateListener, PhoneStateListener.LISTEN_CALL_STATE);
telephonyManager.listen(fakeStateListener, PhoneStateListener.LISTEN_NONE);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { // The enclosed hack in it's current state cause crash on Banglejs builds tarkgetSDK=31 on a Android 13 device.
// HACK: On Lineage we have to do this so that the permission dialog pops up
if (fakeStateListener == null) {
fakeStateListener = new PhoneStateListener();
TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
telephonyManager.listen(fakeStateListener, PhoneStateListener.LISTEN_CALL_STATE);
telephonyManager.listen(fakeStateListener, PhoneStateListener.LISTEN_NONE);
}
}
}
@ -583,6 +614,7 @@ public class ControlCenterv2 extends AppCompatActivity
getContext().getString(R.string.app_name),
getContext().getString(R.string.ok)))
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.M)
public void onClick(DialogInterface dialog, int id) {
try {
startActivity(new Intent(android.provider.Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS));
@ -629,6 +661,7 @@ public class ControlCenterv2 extends AppCompatActivity
getContext().getString(R.string.app_name),
getContext().getString(R.string.ok)))
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.M)
public void onClick(DialogInterface dialog, int id) {
Intent enableIntent = new Intent(android.provider.Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
startActivity(enableIntent);
@ -640,6 +673,7 @@ public class ControlCenterv2 extends AppCompatActivity
}
}
/// Called from checkAndRequestPermissions - this puts up a dialog explaining we need permissions, and then calls checkAndRequestPermissions (via an intent) when 'ok' pressed
public static class LocationPermissionsDialogFragment extends DialogFragment {
ControlCenterv2 controlCenter;
@ -661,4 +695,22 @@ public class ControlCenterv2 extends AppCompatActivity
return builder.create();
}
}
// Register the permissions callback, which handles the user's response to the
// system permissions dialog. Save the return value, an instance of
// ActivityResultLauncher, as an instance variable.
public ActivityResultLauncher<String[]> requestMultiplePermissionsLauncher =
registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), isGranted -> {
if (isGranted.containsValue(true)) {
// Permission is granted. Continue the action or workflow in your
// app.
} else {
// Explain to the user that the feature is unavailable because the
// feature requires a permission that the user has denied. At the
// same time, respect the user's decision. Don't link to system
// settings in an effort to convince the user to change their
// decision.
GB.toast(this, getString(R.string.permission_granting_mandatory), Toast.LENGTH_LONG, GB.ERROR);
}
});
}

View File

@ -116,6 +116,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.PendingIntentUtils;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
import nodomain.freeyourgadget.gadgetbridge.util.WidgetPreferenceStorage;
@ -915,15 +916,15 @@ public class DebugActivity extends AbstractGBActivity {
Intent notificationIntent = new Intent(getApplicationContext(), DebugActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0,
notificationIntent, 0);
PendingIntent pendingIntent = PendingIntentUtils.getActivity(getApplicationContext(), 0,
notificationIntent, 0, false);
RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_REPLY)
.build();
Intent replyIntent = new Intent(ACTION_REPLY);
PendingIntent replyPendingIntent = PendingIntent.getBroadcast(this, 0, replyIntent, 0);
PendingIntent replyPendingIntent = PendingIntentUtils.getBroadcast(this, 0, replyIntent, 0, true);
NotificationCompat.Action action =
new NotificationCompat.Action.Builder(android.R.drawable.ic_input_add, "Reply", replyPendingIntent)

View File

@ -22,6 +22,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.SystemClock;
import org.slf4j.Logger;
@ -33,6 +34,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
import nodomain.freeyourgadget.gadgetbridge.util.PendingIntentUtils;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
/**
@ -56,7 +58,7 @@ public class PeriodicExporter extends BroadcastReceiver {
public static void scheduleAlarm(Context context, Integer autoExportInterval, boolean autoExportEnabled) {
Intent i = new Intent(context, PeriodicExporter.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
PendingIntent pi = PendingIntentUtils.getBroadcast(context, 0, i, 0, false);
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
am.cancel(pi);
if (!autoExportEnabled) {

View File

@ -22,6 +22,7 @@ import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import net.e175.klaus.solarpositioning.DeltaT;
import net.e175.klaus.solarpositioning.SPA;
@ -37,6 +38,7 @@ import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
import nodomain.freeyourgadget.gadgetbridge.util.PendingIntentUtils;
public class AlarmReceiver extends BroadcastReceiver {
private static final Logger LOG = LoggerFactory.getLogger(AlarmReceiver.class);
@ -45,7 +47,7 @@ public class AlarmReceiver extends BroadcastReceiver {
Context context = GBApplication.getContext();
Intent intent = new Intent("DAILY_ALARM");
intent.setPackage(BuildConfig.APPLICATION_ID);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
PendingIntent pendingIntent = PendingIntentUtils.getBroadcast(context, 0, intent, 0, false);
AlarmManager am = (AlarmManager) (context.getSystemService(Context.ALARM_SERVICE));
if (am != null) {

View File

@ -95,6 +95,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NavigationInfoSpec;
import nodomain.freeyourgadget.gadgetbridge.service.receivers.GBCallControlReceiver;
import nodomain.freeyourgadget.gadgetbridge.service.receivers.GBMusicControlReceiver;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.PendingIntentUtils;
import static nodomain.freeyourgadget.gadgetbridge.util.GB.NOTIFICATION_CHANNEL_HIGH_PRIORITY_ID;
import static nodomain.freeyourgadget.gadgetbridge.util.GB.NOTIFICATION_CHANNEL_ID;
@ -253,7 +254,7 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
intent.putExtra(FindPhoneActivity.EXTRA_RING, ring);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pi = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent pi = PendingIntentUtils.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT, false);
NotificationCompat.Builder notification = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_HIGH_PRIORITY_ID )
.setSmallIcon(R.drawable.ic_notification)
@ -389,14 +390,14 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
Uri screenshotURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".screenshot_provider", new File(fullpath));
intent.setDataAndType(screenshotURI, "image/*");
PendingIntent pIntent = PendingIntent.getActivity(context, 0, intent, 0);
PendingIntent pIntent = PendingIntentUtils.getActivity(context, 0, intent, 0, false);
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("image/*");
shareIntent.putExtra(Intent.EXTRA_STREAM, screenshotURI);
PendingIntent pendingShareIntent = PendingIntent.getActivity(context, 0, Intent.createChooser(shareIntent, "share screenshot"),
PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent pendingShareIntent = PendingIntentUtils.getActivity(context, 0, Intent.createChooser(shareIntent, "share screenshot"),
PendingIntent.FLAG_UPDATE_CURRENT, false);
NotificationCompat.Action action = new NotificationCompat.Action.Builder(android.R.drawable.ic_menu_share, "share", pendingShareIntent).build();

View File

@ -36,6 +36,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
import nodomain.freeyourgadget.gadgetbridge.util.PendingIntentUtils;
public class AutoConnectIntervalReceiver extends BroadcastReceiver {
@ -101,7 +102,7 @@ public class AutoConnectIntervalReceiver extends BroadcastReceiver {
AlarmManager am = (AlarmManager) (GBApplication.getContext().getSystemService(Context.ALARM_SERVICE));
Intent intent = new Intent("GB_RECONNECT");
intent.setPackage(BuildConfig.APPLICATION_ID);
PendingIntent pendingIntent = PendingIntent.getBroadcast(GBApplication.getContext(), 0, intent, 0);
PendingIntent pendingIntent = PendingIntentUtils.getBroadcast(GBApplication.getContext(), 0, intent, 0, false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
am.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, Calendar.getInstance().
getTimeInMillis() + delay * 1000, pendingIntent);

View File

@ -27,6 +27,7 @@ import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.text.Html;
@ -146,8 +147,8 @@ public class GB {
Intent notificationIntent = new Intent(context, ControlCenterv2.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
notificationIntent, 0);
PendingIntent pendingIntent = PendingIntentUtils.getActivity(context, 0,
notificationIntent, 0, false);
return pendingIntent;
}
@ -188,18 +189,18 @@ public class GB {
Intent deviceCommunicationServiceIntent = new Intent(context, DeviceCommunicationService.class);
if (connected) {
deviceCommunicationServiceIntent.setAction(DeviceService.ACTION_DISCONNECT);
PendingIntent disconnectPendingIntent = PendingIntent.getService(context, 0, deviceCommunicationServiceIntent, PendingIntent.FLAG_ONE_SHOT);
PendingIntent disconnectPendingIntent = PendingIntentUtils.getService(context, 0, deviceCommunicationServiceIntent, PendingIntent.FLAG_ONE_SHOT, false);
builder.addAction(R.drawable.ic_notification_disconnected, context.getString(R.string.controlcenter_disconnect), disconnectPendingIntent);
if (DeviceHelper.getInstance().getCoordinator(device).supportsActivityDataFetching()) {
deviceCommunicationServiceIntent.setAction(DeviceService.ACTION_FETCH_RECORDED_DATA);
deviceCommunicationServiceIntent.putExtra(EXTRA_RECORDED_DATA_TYPES, ActivityKind.TYPE_ACTIVITY);
PendingIntent fetchPendingIntent = PendingIntent.getService(context, 1, deviceCommunicationServiceIntent, PendingIntent.FLAG_ONE_SHOT);
PendingIntent fetchPendingIntent = PendingIntentUtils.getService(context, 1, deviceCommunicationServiceIntent, PendingIntent.FLAG_ONE_SHOT, false);
builder.addAction(R.drawable.ic_refresh, context.getString(R.string.controlcenter_fetch_activity_data), fetchPendingIntent);
}
} else if (device.getState().equals(GBDevice.State.WAITING_FOR_RECONNECT) || device.getState().equals(GBDevice.State.NOT_CONNECTED)) {
deviceCommunicationServiceIntent.setAction(DeviceService.ACTION_CONNECT);
deviceCommunicationServiceIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
PendingIntent reconnectPendingIntent = PendingIntent.getService(context, 2, deviceCommunicationServiceIntent, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent reconnectPendingIntent = PendingIntentUtils.getService(context, 2, deviceCommunicationServiceIntent, PendingIntent.FLAG_UPDATE_CURRENT, false);
builder.addAction(R.drawable.ic_notification, context.getString(R.string.controlcenter_connect), reconnectPendingIntent);
}
}else{
@ -243,7 +244,7 @@ public class GB {
Intent deviceCommunicationServiceIntent = new Intent(context, DeviceCommunicationService.class);
deviceCommunicationServiceIntent.setAction(DeviceService.ACTION_FETCH_RECORDED_DATA);
deviceCommunicationServiceIntent.putExtra(EXTRA_RECORDED_DATA_TYPES, ActivityKind.TYPE_ACTIVITY);
PendingIntent fetchPendingIntent = PendingIntent.getService(context, 1, deviceCommunicationServiceIntent, PendingIntent.FLAG_ONE_SHOT);
PendingIntent fetchPendingIntent = PendingIntentUtils.getService(context, 1, deviceCommunicationServiceIntent, PendingIntent.FLAG_ONE_SHOT, false);
builder.addAction(R.drawable.ic_refresh, context.getString(R.string.controlcenter_fetch_activity_data), fetchPendingIntent);
}
}
@ -272,7 +273,7 @@ public class GB {
if (GBApplication.getPrefs().getString("last_device_address", null) != null) {
Intent deviceCommunicationServiceIntent = new Intent(context, DeviceCommunicationService.class);
deviceCommunicationServiceIntent.setAction(DeviceService.ACTION_CONNECT);
PendingIntent reconnectPendingIntent = PendingIntent.getService(context, 2, deviceCommunicationServiceIntent, PendingIntent.FLAG_ONE_SHOT);
PendingIntent reconnectPendingIntent = PendingIntentUtils.getService(context, 2, deviceCommunicationServiceIntent, PendingIntent.FLAG_ONE_SHOT, false);
builder.addAction(R.drawable.ic_notification, context.getString(R.string.controlcenter_connect), reconnectPendingIntent);
}
@ -480,8 +481,8 @@ public class GB {
Intent notificationIntent = new Intent(context, ControlCenterv2.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
notificationIntent, 0);
PendingIntent pendingIntent = PendingIntentUtils.getActivity(context, 0,
notificationIntent, 0, false);
NotificationCompat.Builder nb = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID_TRANSFER)
.setTicker((title == null) ? context.getString(R.string.app_name) : title)
@ -515,7 +516,7 @@ public class GB {
public static void createGpsNotification(Context context, int numDevices) {
Intent notificationIntent = new Intent(context, ControlCenterv2.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
PendingIntent pendingIntent = PendingIntentUtils.getActivity(context, 0, notificationIntent, 0, false);
NotificationCompat.Builder nb = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID_GPS)
.setTicker(context.getString(R.string.notification_gps_title))
@ -538,8 +539,8 @@ public class GB {
Intent notificationIntent = new Intent(context, ControlCenterv2.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
notificationIntent, 0);
PendingIntent pendingIntent = PendingIntentUtils.getActivity(context, 0,
notificationIntent, 0, false);
NotificationCompat.Builder nb = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
.setContentTitle(context.getString(R.string.app_name))
@ -568,8 +569,8 @@ public class GB {
Intent notificationIntent = new Intent(context, ControlCenterv2.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
notificationIntent, 0);
PendingIntent pendingIntent = PendingIntentUtils.getActivity(context, 0,
notificationIntent, 0, false);
NotificationCompat.Builder nb = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID_LOW_BATTERY)
.setContentTitle(context.getString(R.string.notif_battery_low_title))
@ -602,8 +603,8 @@ public class GB {
Intent notificationIntent = new Intent(context, SettingsActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
notificationIntent, 0);
PendingIntent pendingIntent = PendingIntentUtils.getActivity(context, 0,
notificationIntent, 0, false);
NotificationCompat.Builder nb = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
.setContentTitle(context.getString(R.string.notif_export_failed_title))

View File

@ -0,0 +1,61 @@
package nodomain.freeyourgadget.gadgetbridge.util;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
public class PendingIntentUtils {
public static PendingIntent getBroadcast(Context context,
int requestCode,
Intent intent,
int flags,
boolean makeMutable) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (makeMutable) {
flags |= PendingIntent.FLAG_MUTABLE;
} else {
flags |= PendingIntent.FLAG_IMMUTABLE;
}
return PendingIntent.getBroadcast(context, requestCode, intent, flags);
}
return PendingIntent.getBroadcast(context, requestCode, intent, flags);
}
public static PendingIntent getActivity(Context context,
int requestCode,
Intent intent,
int flags,
boolean makeMutable) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (makeMutable) {
flags |= PendingIntent.FLAG_MUTABLE;
} else {
flags |= PendingIntent.FLAG_IMMUTABLE;
}
return PendingIntent.getActivity(context, requestCode, intent, flags);
}
return PendingIntent.getActivity(context, requestCode, intent, flags);
}
public static PendingIntent getService(Context context,
int requestCode,
Intent intent,
int flags,
boolean makeMutable) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (makeMutable) {
flags |= PendingIntent.FLAG_MUTABLE;
} else {
flags |= PendingIntent.FLAG_IMMUTABLE;
}
return PendingIntent.getService(context, requestCode, intent, flags);
}
return PendingIntent.getService(context, requestCode, intent, flags);
}
}