diff --git a/app/build.gradle b/app/build.gradle index c86b26426..4fd3a5630 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' diff --git a/app/src/banglejs/AndroidManifest.xml b/app/src/banglejs/AndroidManifest.xml index f285bad1f..525452a9e 100644 --- a/app/src/banglejs/AndroidManifest.xml +++ b/app/src/banglejs/AndroidManifest.xml @@ -12,4 +12,34 @@ --> + + + + + + + + + + + + + + + + + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d7f79c804..795931d76 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -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"> @@ -134,7 +135,8 @@ + android:parentActivityName=".activities.ControlCenterv2" + android:exported="true"> @@ -355,7 +357,8 @@ + android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" + android:exported="false"> @@ -368,7 +371,8 @@ + android:enabled="true" + android:exported="false"> @@ -376,13 +380,15 @@ + android:enabled="true" + android:exported="false"> - + @@ -390,7 +396,8 @@ + android:permission="android.permission.RECEIVE_BOOT_COMPLETED" + android:exported="false"> @@ -583,7 +590,8 @@ + android:label="@string/appwidget_sleep_alarm_widget_label" + android:exported="false"> @@ -595,7 +603,8 @@ + android:label="@string/widget_listing_label" + android:exported="false"> @@ -614,13 +623,15 @@ android:launchMode="singleInstance" android:theme="@style/Theme.AppCompat.Light.Dialog" /> - + - + @@ -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"> @@ -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"> diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/SleepAlarmWidget.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/SleepAlarmWidget.java index 9ce53d3e5..33c817012 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/SleepAlarmWidget.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/SleepAlarmWidget.java @@ -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); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/Widget.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/Widget.java index b820a146c..4ea91e834 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/Widget.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/Widget.java @@ -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); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java index 26cdeb3ad..be89f5b18 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java @@ -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 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); + } + }); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java index 7f39d39d8..d113f641b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java @@ -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) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/PeriodicExporter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/PeriodicExporter.java index fb3ac9610..36861f5a2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/PeriodicExporter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/PeriodicExporter.java @@ -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) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmReceiver.java index 1b408cf5b..f1a93f9c2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmReceiver.java @@ -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) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java index 52f15f1cd..a970ab721 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java @@ -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(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/AutoConnectIntervalReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/AutoConnectIntervalReceiver.java index a425fb0d9..dbfc5b860 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/AutoConnectIntervalReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/AutoConnectIntervalReceiver.java @@ -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); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java index 4b850a78e..edcf372ae 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java @@ -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)) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PendingIntentUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PendingIntentUtils.java new file mode 100644 index 000000000..eaedfd0d9 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PendingIntentUtils.java @@ -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); + } + +}