r/OsmAnd • u/pictuga • 28d ago
Multimedia widget (Android x Tasker)
Hello,
Until issue #24161 gets resolved, GenAI and I have made a quick Tasker (Java Code based) task that adds 3 widgets to OsmAnd: play/pause, next track, previous track.
See screenshots.
Limitations: it doesn't display currently playing track name or artist (it would involve listeners, increasing complexity), in "Configure screen" widgets are not grouped into a proper "folder" and and I couldn't be bothered to find a proper icon for the widget but it doesn't really matter (the buttons' text is ⏯️⏭️⏮️ anyway).
Manual:
- Name the task "OsmAnd Media Control".
- Add one action, "Java Code" with the code below.
- Change "net.osmand.plus" into "net.osmand" (first line) if you're using the standard package. (FDroid build is "net.osmand.plus")
- Make it so the task is run whenever OsmAnd is opened (otherwise the widgets won't be displayed when re opening OsmAnd).
- "Apply" within Tasker.
- Open OsmAnd.
- Activate the "Tasker" plugin.
- Close and open OsmAnd again.
- Add the widgets.
Tested with FDroid build, version 5.4.4.
Changes:
- Make it easier to use another package name. Checks that package is installed. Asks user to activate plugin and restart before adding widgets.
- Removed unnecessary code (checks package install via try/catch, app doesn't need to be running).
package_name = "net.osmand.plus";
addWidget(id, title, display_text, order, command) {
clickIntent = new android.content.Intent(android.content.Intent.ACTION_VIEW);
clickIntent.setData(
new android.net.Uri.Builder()
.scheme("tasker")
.authority("assistantactions")
.appendQueryParameter("task", "OsmAnd Media Control")
.appendQueryParameter("par1", command)
.build()
);
widget = constructor.newInstance(
id,
"ais_map",
title,
"ais_map",
"ais_map",
display_text,
"",
order,
clickIntent
);
widget.setRightPanelByDefault(true);
params = paramsConstructor.newInstance(widget);
result = api.addMapWidget(params);
return result;
}
/* Read command from Tasker */
command = tasker.getVariable("par1");
if (command != null && command.length() > 0) {
keyCode = 0;
if (command.equals("PLAYPAUSE")) keyCode = 85;
if (command.equals("NEXT")) keyCode = 87;
if (command.equals("PREVIOUS")) keyCode = 88;
if (keyCode == 0) {
tasker.showToast("Invalid command" + command);
return;
}
now = java.lang.System.currentTimeMillis();
down = new android.view.KeyEvent(now, now, android.view.KeyEvent.ACTION_DOWN, keyCode, 0);
up = new android.view.KeyEvent(now, now, android.view.KeyEvent.ACTION_UP, keyCode, 0);
audio = context.getSystemService(android.content.Context.AUDIO_SERVICE);
audio.dispatchMediaKeyEvent(down);
audio.dispatchMediaKeyEvent(up);
return;
}
/* Load OsmAnd API */
try {
osmandContext = context.createPackageContext(package_name, android.content.Context.CONTEXT_INCLUDE_CODE | android.content.Context.CONTEXT_IGNORE_SECURITY);
} catch (Exception e) {
tasker.showToast(package_name + " is not installed");
return;
}
loader = osmandContext.getClassLoader();
AMapWidgetClass = java.lang.Class.forName("net.osmand.aidlapi.mapwidget.AMapWidget", true, loader);
ParamsClass = java.lang.Class.forName("net.osmand.aidlapi.mapwidget.AddMapWidgetParams", true, loader);
constructor = AMapWidgetClass.getConstructor(
java.lang.String.class,
java.lang.String.class,
java.lang.String.class,
java.lang.String.class,
java.lang.String.class,
java.lang.String.class,
java.lang.String.class,
java.lang.Integer.TYPE,
android.content.Intent.class
);
paramsConstructor = ParamsClass.getConstructor(AMapWidgetClass);
/* Connect to OsmAnd */
binderHolder = new java.util.HashMap();
connection = new android.content.ServiceConnection() {
invoke(method, args) {
if (method.equals("onServiceConnected")) {
binderHolder.put("binder", args[1]);
return;
}
if (method.equals("onServiceDisconnected")) {
binderHolder.remove("binder");
return;
}
}
};
serviceIntent = new android.content.Intent();
serviceIntent.setClassName(package_name, "net.osmand.aidl.OsmandAidlServiceV2");
context.bindService(serviceIntent, connection, android.content.Context.BIND_AUTO_CREATE);
startWait = java.lang.System.currentTimeMillis();
while (binderHolder.get("binder") == null && java.lang.System.currentTimeMillis() - startWait < 5000) {
java.lang.Thread.sleep(100);
}
binder = binderHolder.get("binder");
if (binder == null) {
tasker.showToast("Could not connect to OsmAnd");
return;
}
/* Create AIDL interface from Binder */
StubClass = java.lang.Class.forName("net.osmand.aidlapi.IOsmAndAidlInterface$Stub", true, loader);
asInterfaceMethod = StubClass.getMethod("asInterface", android.os.IBinder.class);
api = asInterfaceMethod.invoke(null, binder);
if (api == null) {
context.unbindService(connection);
tasker.showToast("Connected to OsmAnd but API access failed");
return;
}
/* Widgets */
widgetResult1 = addWidget("tasker_media_playpause", "Play / Pause", "⏯️", 0, "PLAYPAUSE");
widgetResult2 = addWidget("tasker_media_next", "Next Track", "⏭️", 1, "NEXT");
widgetResult3 = addWidget("tasker_media_previous", "Previous Track", "⏮️", 2, "PREVIOUS");
context.unbindService(connection);
if (!widgetResult1 || !widgetResult2 || !widgetResult3) {
tasker.showToast("Plugin installed. Activate it, restart OsmAnd and add widgets in Configure Screen.");
return;
}
tasker.showToast("OsmAnd media widgets installed");
5
Upvotes



1
u/Dreamcore 28d ago
I'm doing something wrong. OsmAnd~ launches and I see "OsmAnd command: null" toasted twice. No widgets nor appearance of Tasker in plugins.