1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-07-21 22:31:16 +02:00

Bangle.js: extending intents functionality (continuation)

Add support for (explicit) service intents.
Add support for setting flags for intents.
Add support for setting multiple categories for intents.
Add ability for Gadgetbridge to wake the Android device and leave the
 lock screen to start activities when it is sleeping. A new activity
 'WakeActivity' is used for this. (Must use 'trusted device' in Android)
Add dismiss-button to 'display over other apps' permission pop up.
Bangle.js can send "gadgetbridge" as package info to accomodate the
 different GB build variants/flavours.
Use only getContext() and not getApplicationContext() when executing
 the intents.
This commit is contained in:
Ganblejs 2022-08-21 12:26:09 +02:00 committed by Gitea
parent 0f44d8cbf2
commit 82315b3281
7 changed files with 114 additions and 31 deletions

View File

@ -2,7 +2,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- SDK 30 https://developer.android.com/training/package-visibility for getting app name from notifications -->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<!--
SDK 30 & Android 11 - Used for getting app name from notifications, and for starting
services from other packages via intents, when targeting Android API level 30 or later
(e.g. Bangle.js build variant) on devices running Android 11 or later.
https://developer.android.com/training/package-visibility
https://support.google.com/googleplay/android-developer/answer/10158779?hl=en#zippy=%2Cpermitted-uses-of-the-query-all-packages-permission%2Cexceptions
-->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
</manifest>

View File

@ -50,7 +50,6 @@
<!-- Used for starting activities from the background with intents -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-feature
android:name="android.hardware.bluetooth"
android:required="true" />
@ -707,9 +706,15 @@
</intent-filter>
</activity>
<activity
android:name=".activities.WakeActivity"
android:label="WakeActivity">
</activity>
<activity
android:name=".externalevents.opentracks.OpenTracksController"
android:label="OpenTracks controller and intent receiver"
android:exported="true"/>
</application>
</manifest>

View File

@ -633,6 +633,8 @@ public class ControlCenterv2 extends AppCompatActivity
Intent enableIntent = new Intent(android.provider.Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
startActivity(enableIntent);
}
}).setNegativeButton(R.string.dismiss, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {}
});
return builder.create();
}

View File

@ -0,0 +1,41 @@
package nodomain.freeyourgadget.gadgetbridge.activities;
import android.app.Activity;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.WindowManager;
import androidx.annotation.Nullable;
public class WakeActivity extends Activity {
/*
By starting this activity via an intent sent e.g. by a Bangle.js the keyguard can be
dismissed automatically if the android device is in a trusted state (e.g. using a GB Device (Bangle.js) as a
trusted device via smart lock settings)
First try to start the activity you want to start with an intent and then start this activity with a second intent, both initiated on the Bangle.js, or other device.
*/
private void dismissKeyguard() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setTurnScreenOn(true);
KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
keyguardManager.requestDismissKeyguard(this, null);
} else {
this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON|WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
}
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(null);
// Unlock the android device if it's in a trusted state, otherwise request user action to unlock
dismissKeyguard();
// Go back to last activity, which can have been waiting to start under
// the lock screen, e.g. it was previously initiated via intent message from Bangle.js
this.onBackPressed();
}
}

View File

@ -190,5 +190,4 @@ public class BangleJSCoordinator extends AbstractBLEDeviceCoordinator {
for (int i=0; i<settings.size(); i++) settingsInt[i] = settings.get(i);
return settingsInt;
}
}

View File

@ -663,41 +663,56 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
case "intent": {
Prefs devicePrefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()));
if (devicePrefs.getBoolean(PREF_DEVICE_INTENTS, false)) {
String target = json.has("target") ? json.getString("target") : "broadcastreceiver";
Intent in = new Intent();
if (json.has("action")) in.setAction((String)json.get("action"));
if (json.has("category")) in.addCategory((String)json.get("category"));
if (json.has("mimetype")) in.setType((String)json.get("mimetype"));
if (json.has("data")) in.setData(Uri.parse((String)json.get("data")));
if (json.has("package") && !json.has("class")) in.setPackage((String)json.getString("package"));
if (json.has("package") && json.has("class")) in.setClassName((String)json.getString("package"), (String)json.getString("class"));
String target = "";
if (json.has("target")) target = (String)json.get("target");
JSONObject extra = new JSONObject();
if (json.has("extra")) extra = json.getJSONObject("extra");
if (extra != null) {
if (json.has("action")) in.setAction(json.getString("action"));
if (json.has("flags")) {
JSONArray flags = json.getJSONArray("flags");
for (int i = 0; i < flags.length(); i++) {
in = addIntentFlag(in, flags.getString(i));
}
}
if (json.has("categories")) {
JSONArray categories = json.getJSONArray("categories");
for (int i = 0; i < categories.length(); i++) {
in.addCategory(categories.getString(i));
}
}
if (json.has("package") && !json.has("class")) {
in = json.getString("package").equals("gadgetbridge") ?
in.setPackage(this.getContext().getPackageName()) :
in.setPackage(json.getString("package"));
}
if (json.has("package") && json.has("class")) {
in = json.getString("package").equals("gadgetbridge") ?
in.setClassName(this.getContext().getPackageName(), json.getString("class")) :
in.setClassName(json.getString("package"), json.getString("class"));
}
if (json.has("mimetype")) in.setType(json.getString("mimetype"));
if (json.has("data")) in.setData(Uri.parse(json.getString("data")));
if (json.has("extra")) {
JSONObject extra = json.getJSONObject("extra");
Iterator<String> iter = extra.keys();
while (iter.hasNext()) {
String key = iter.next();
in.putExtra(key, extra.getString(key));
in.putExtra(key, extra.getString(key)); // Should this be implemented for other types, e.g. extra.getInt(key)? Or will this always work even if receiving ints/doubles/etc.?
}
}
LOG.info("Sending intent: " + String.valueOf(in));
LOG.info("Executing intent:\n\t" + String.valueOf(in) + "\n\tTargeting: " + target);
//GB.toast(getContext(), String.valueOf(in), Toast.LENGTH_LONG, GB.INFO);
switch (target) {
case "":
// This case should make sure intents matched to the original Bangle.js Gadgetbridge intents implementation still work.
this.getContext().getApplicationContext().sendBroadcast(in);
break;
case "broadcastreceiver":
this.getContext().getApplicationContext().sendBroadcast(in);
getContext().sendBroadcast(in);
break;
case "activity":
in.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.getContext().getApplicationContext().startActivity(in);
case "activity": // See wakeActivity.java if you want to start activities from under the keyguard/lock sceen.
getContext().startActivity(in);
break;
case "service": // Targeting 'Service' is not yet implemented.
LOG.info("Targeting 'Service' not yet implemented.");
GB.toast(getContext(), "Targeting '"+target+"' is not yet implemented.", Toast.LENGTH_LONG, GB.INFO);
// Use jobInfo() and jobScheduler to program this part? Context.startService()? Context.bindService()?
case "service": // Should this be implemented differently, e.g. workManager?
getContext().startService(in);
break;
case "foregroundservice": // Should this be implemented differently, e.g. workManager?
getContext().startForegroundService(in);
break;
default:
LOG.info("Targeting '"+target+"' isn't implemented or doesn't exist.");
@ -761,6 +776,19 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
}
}
private Intent addIntentFlag(Intent intent, String flag) {
try {
final Class<Intent> intentClass = Intent.class;
final Field flagField = intentClass.getDeclaredField(flag);
intent.addFlags(flagField.getInt(null));
} catch (final Exception e) {
// The user sent an invalid flag
LOG.info("Flag '"+flag+"' isn't implemented or doesn't exist and was therefore not set.");
GB.toast(getContext(), "Flag '"+flag+"' isn't implemented or it doesn't exist and was therefore not set.", Toast.LENGTH_LONG, GB.INFO);
}
return intent;
}
@Override
public boolean onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {

View File

@ -944,6 +944,7 @@
<string name="Cancel">Cancel</string>
<string name="Delete">Delete</string>
<string name="ok">OK</string>
<string name="dismiss">Dismiss</string>
<string name="start">Start</string>
<string name="set">Set</string>
<string name="activity_data_management_directory_content_title">Export/Import directory content</string>
@ -1301,7 +1302,7 @@
<string name="permission_notification_listener">%1$s needs access to Notifications in order to display them on your watch when your phone\'s screen is off.\n\nPlease tap \'%2$s\' then \'%1$s\' and enable \'Allow Notification Access\', then tap \'Back\' to return to %1$s.</string>
<string name="permission_notification_policy_access">%1$s needs access to Do Not Disturb settings in order to honour them on your watch when your phone\'s screen is off.\n\nPlease tap \'%2$s\' then \'%1$s\' and enable \'Allow Do Not Disturb\', then tap \'Back\' to return to %1$s.</string>
<string name="permission_location">%1$s needs access to your location in the background to allow it to stay connected to your watch even when your screen is off.\n\nPlease tap \'%2$s\' to agree.</string>
<string name="permission_display_over_other_apps">%1$s needs permission to display over other apps in order to let Bangle.js watches start activities via intents when %1$s is in the background.\n\nThis can be used to start a music app and play a song, and many other things.\n\nPlease tap \'%2$s\' then \'%1$s\' and enable \'Allow display over other apps\', then tap \'Back\' to return to %1$s.</string>
<string name="permission_display_over_other_apps">%1$s needs permission to display over other apps in order to let Bangle.js watches start activities via intents when %1$s is in the background.\n\nThis can be used to start a music app and play a song, and many other things.\n\nPlease tap \'%2$s\' then \'%1$s\' and enable \'Allow display over other apps\', then tap \'Back\' to return to %1$s.\n\nTo stop %1$s asking for permissions go to \'Settings\' and uncheck \'Check permission status\'.\n\nMake sure to grant %1$s the permissions needed to function as you expect.</string>
<string name="error_version_check_extreme_caution">CAUTION: Error when checking version information! You should not continue! Saw version name \"%s\"</string>
<string name="require_location_provider">Location must be enabled</string>
<string name="companiondevice_pairing">CompanionDevice Pairing</string>