Hello again,
This week, I was able to upload the first pEFL app to the OpenStore. You can find everything at https://github.com/MaxPerl/emedia.maxperl/ and in the OpenStore at https://open-store.io/app/emedia.maxperl
In the end, it was a bit of a challenge dealing with hard-coded paths and full ContentHub integration for importing files (Open and Open With actions). Fortunately, the ContentHub also works with plain DBus. But since that isn’t documented anywhere, I want to write a few lines about it (maybe it’ll be helpful to others, too):
The necessary changes to the $appid.apparmor file (+policygroup -> content_exchange), the manifest.json.in file (+hook for content_hub), and the creation of a $appid_content_hub.json file (+destination for the respective formats) are well documented, so I’ll skip those details here.
My approach is to use a FileMonitor for the "$appcache/HubIncomings" directory (actually, I use two FileMonitors because, I first have to check whether the HubIncomings directory already exists. AppArmor prevents me from creating it and from making any changes to it). If a change occurs, I copy ( (!) really copy (!) because moving files is also prohibited by AppArmor) the import to a directory that belongs to my app (e.g., $appcache/imported_files)
At first, I ran into a problem where, when I launched the app via FileManager, I could only open one file. The app crashed when I tried to open a second file.
This was because the Content_Hub interface strictly requires that transfers be properly terminated (in many apps—and, to be honest, even in the instructions I found—this important intermediate step is missing). The procedure appears to be as follows:
-
The source sends "Charged" once the file has been copied to the HubIncoming directory (irrelevant for me because I use a FileMonitor).
-
Our app must send “Collected” and then copy the files to the import path.
-
After copying, we must send “Finalize.” This causes Content Hub to clean up the Hubincoming directory, which is of course helpful because it ensures that there is only one import in the Hubincoming directory after each finalize (so you don't have to search for the latest import).
I’ll quickly show you how my Perl function does this, and then you might be able to figure out the relevant DBus settings from there. It’s important to send the full App_ID in the path (i.e., including the version, etc.) and to escape it in a DBus-compliant manner. This means that special characters such as periods, hyphens, or spaces must be escaped. A proven standard is underscore hex escaping: Each invalid character is replaced by an underscore (_) followed by its two-digit hexadecimal value (in lowercase).
sub import_path {
my ($self, $path) = @_;
my ($transfer_id) = $path =~ /(\d+)$/;
my $app_id = $self->app_id();
# Mask special characters
my $encoded_app_id = $app_id;
$encoded_app_id =~ s/([^a-zA-Z0-9])/sprintf("_%02x", ord($1))/eg;
my $service = "com.lomiri.content.dbus.Service";
my $dbus_path = "/transfers/$encoded_app_id/import";
my $interface = "com.lomiri.content.dbus.Transfer";
my $dbus = Protocol::DBus::Client::login_session();
$dbus->initialize();
# Send collect an das DBus Interface
$dbus->send_call(
path => "$dbus_path/$transfer_id",
interface => $interface,
member => 'Collect',
destination => $service,
);
# Obviously there is no answer to collect send!
#my $msg = $dbus->get_message();
[... Copying files ...]
my $got_response;
$dbus->send_call(
path => "$dbus_path/$transfer_id",
interface => $interface,
member => 'Finalize',
destination => $service
)->then( sub {
$got_response = 1;
});
$dbus->get_message() while !$got_response;
return "$new_path/$transfer_id/$file";
}
Sorry for the long post. It took me quite a while to implement this. And as I said, maybe this explanation of DBus will be helpful to others as well. The next step, at some point, will be figuring out how to set up an export using DBus alone. If anyone here already knows the steps, please feel free to post some tips here. But for now, I’m going to take a little break 
Warm regards,
Max
). Here’s the code:
How can I start the manual review process?
