diff --git a/android/appmgr/src/org/anbox/appmgr/PlatformService.java b/android/appmgr/src/org/anbox/appmgr/PlatformService.java index 5ede4081bd6a7f64d095714023e921504cd80b4d..6e2cf39c1f3e2f0fe4489a5a0610edd0b75b08e1 100644 --- a/android/appmgr/src/org/anbox/appmgr/PlatformService.java +++ b/android/appmgr/src/org/anbox/appmgr/PlatformService.java @@ -27,8 +27,14 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.ApplicationInfo; import android.net.Uri; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Canvas; import java.util.List; +import java.io.ByteArrayOutputStream; public final class PlatformService { private static final String TAG = "AnboxAppMgr"; @@ -69,28 +75,41 @@ public final class PlatformService { data.writeInt(apps.size()); for (int n = 0; n < apps.size(); n++) { ApplicationInfo appInfo = apps.get(n); - data.writeString(appInfo.name); - data.writeString(appInfo.packageName); Intent launchIntent = mPm.getLaunchIntentForPackage(appInfo.packageName); - if (launchIntent != null) { - data.writeInt(1); - data.writeString(launchIntent.getAction()); - if (launchIntent.getData() != null) - data.writeString(launchIntent.getData().toString()); - else - data.writeString(""); - data.writeString(launchIntent.getType()); - data.writeString(launchIntent.getComponent().getPackageName()); - data.writeString(launchIntent.getComponent().getClassName()); - data.writeInt(launchIntent.getCategories().size()); - for (String category : launchIntent.getCategories()) - data.writeString(category); - } else { - data.writeInt(0); + if (launchIntent == null) + continue; + + Drawable icon = null; + try { + icon = mPm.getApplicationIcon(appInfo.packageName); } + catch (PackageManager.NameNotFoundException ex) { + continue; + } + + if (icon == null) + continue; + + data.writeString(appInfo.name); + data.writeString(appInfo.packageName); - // FIXME add icon, flags, ... + data.writeString(launchIntent.getAction()); + if (launchIntent.getData() != null) + data.writeString(launchIntent.getData().toString()); + else + data.writeString(""); + data.writeString(launchIntent.getType()); + data.writeString(launchIntent.getComponent().getPackageName()); + data.writeString(launchIntent.getComponent().getClassName()); + data.writeInt(launchIntent.getCategories().size()); + for (String category : launchIntent.getCategories()) + data.writeString(category); + + Bitmap iconBitmap = drawableToBitmap(icon); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + iconBitmap.compress(Bitmap.CompressFormat.PNG, 90, outStream); + data.writeByteArray(outStream.toByteArray()); } Parcel reply = Parcel.obtain(); @@ -107,4 +126,16 @@ public final class PlatformService { public void notifyPackageRemoved(Intent intent) { } + + private Bitmap drawableToBitmap(Drawable drawable) { + if (drawable instanceof BitmapDrawable) + return ((BitmapDrawable)drawable).getBitmap(); + + Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + + return bitmap; + } } diff --git a/android/service/platform_api_stub.cpp b/android/service/platform_api_stub.cpp index 85deddfa420e79ce52d462142096b6edef9215dd..abda887aead03ff40e05a8c74ba1bf43dc076107 100644 --- a/android/service/platform_api_stub.cpp +++ b/android/service/platform_api_stub.cpp @@ -81,6 +81,8 @@ void PlatformApiStub::update_application_list(const ApplicationListUpdate &updat auto c = launch_intent->add_categories(); *c = category; } + + app->set_icon(a.icon.data(), a.icon.size()); } rpc_channel_->send_event(seq); diff --git a/android/service/platform_api_stub.h b/android/service/platform_api_stub.h index 4f14b3a17cd48b3a0cd9ba8a47722880ca219f6e..af5c14d3e9dd20c799f594601369964482ca0895 100644 --- a/android/service/platform_api_stub.h +++ b/android/service/platform_api_stub.h @@ -73,6 +73,7 @@ public: std::vector categories; }; Intent launch_intent; + std::vector icon; }; std::vector applications; }; diff --git a/android/service/platform_service.cpp b/android/service/platform_service.cpp index 6d2a879d432f8deaecc704e7f921c4e2b4cba871..8015122b1278107abc138753eb230b1c962d8cfa 100644 --- a/android/service/platform_service.cpp +++ b/android/service/platform_service.cpp @@ -97,27 +97,27 @@ status_t PlatformService::update_application_list(const Parcel &data) { package_name.string(), }; - if (data.readInt32() == 1) { - String8 action(data.readString16()); - String8 uri(data.readString16()); - String8 type(data.readString16()); - String8 component_package(data.readString16()); - String8 component_class(data.readString16()); - - std::vector categories; - unsigned int num_categories = data.readInt32(); - for (int m = 0; m < num_categories; m++) - categories.push_back(String8(data.readString16()).string()); - - p.launch_intent.action = action; - p.launch_intent.uri = uri; - p.launch_intent.type = type; - p.launch_intent.package = component_package; - p.launch_intent.component = component_class; - p.launch_intent.categories = categories; - - update.applications.push_back(p); - }; + String8 action(data.readString16()); + String8 uri(data.readString16()); + String8 type(data.readString16()); + String8 component_package(data.readString16()); + String8 component_class(data.readString16()); + + std::vector categories; + unsigned int num_categories = data.readInt32(); + for (int m = 0; m < num_categories; m++) + categories.push_back(String8(data.readString16()).string()); + + p.launch_intent.action = action; + p.launch_intent.uri = uri; + p.launch_intent.type = type; + p.launch_intent.package = component_package; + p.launch_intent.component = component_class; + p.launch_intent.categories = categories; + + data.readByteVector(&p.icon); + + update.applications.push_back(p); } platform_api_stub_->update_application_list(update); diff --git a/src/anbox/application/launcher_storage.cpp b/src/anbox/application/launcher_storage.cpp index 40032890833fcec80f3029b77170bc038ec9d1de..3173ea3de05b7f40861d1b8e5804e8291a8e0655 100644 --- a/src/anbox/application/launcher_storage.cpp +++ b/src/anbox/application/launcher_storage.cpp @@ -17,6 +17,7 @@ #include "anbox/application/launcher_storage.h" #include "anbox/utils.h" +#include "anbox/logger.h" #include #include @@ -26,19 +27,25 @@ namespace fs = boost::filesystem; namespace anbox { namespace application { -LauncherStorage::LauncherStorage(const fs::path &path) : path_(path) {} +LauncherStorage::LauncherStorage(const fs::path &path, + const boost::filesystem::path &icon_path) : path_(path), icon_path_(icon_path) { +} LauncherStorage::~LauncherStorage() {} void LauncherStorage::add(const Item &item) { if (!fs::exists(path_)) fs::create_directories(path_); + if (!fs::exists(icon_path_)) fs::create_directories(icon_path_); auto package_name = item.package; std::replace(package_name.begin(), package_name.end(), '.', '-'); const auto item_path = path_ / utils::string_format("anbox-%s.desktop", package_name); - std::string exec = "anbox launch "; + const auto item_icon_path = + icon_path_ / utils::string_format("anbox-%s.png", package_name); + + std::string exec = utils::string_format("%s launch ", utils::process_get_exe_path(getpid())); if (!item.launch_intent.action.empty()) exec += utils::string_format("--action=%s ", item.launch_intent.action); @@ -62,7 +69,11 @@ void LauncherStorage::add(const Item &item) { << "Exec=" << exec << std::endl << "Terminal=false" << std::endl << "Type=Application" << std::endl - << "Encoding=UTF-8" << std::endl; + << "Icon=" << item_icon_path.string() << std::endl; + f.close(); + + f = std::ofstream(item_icon_path.string()); + f.write(item.icon.data(), item.icon.size()); f.close(); } } // namespace application diff --git a/src/anbox/application/launcher_storage.h b/src/anbox/application/launcher_storage.h index db2c882db11a86c39aefc3f2e727a7def0a7d432..255943dcf63dc88d5ffeaf384d39b55444d61ada 100644 --- a/src/anbox/application/launcher_storage.h +++ b/src/anbox/application/launcher_storage.h @@ -29,19 +29,22 @@ namespace anbox { namespace application { class LauncherStorage { public: - LauncherStorage(const boost::filesystem::path &path); + LauncherStorage(const boost::filesystem::path &path, + const boost::filesystem::path &icon_path); ~LauncherStorage(); struct Item { std::string name; std::string package; android::Intent launch_intent; + std::vector icon; }; void add(const Item &item); private: boost::filesystem::path path_; + boost::filesystem::path icon_path_; }; } // namespace application } // namespace anbox diff --git a/src/anbox/bridge/platform_api_skeleton.cpp b/src/anbox/bridge/platform_api_skeleton.cpp index 7861d3143c4b5190c5c4f1d55ba17689129d9168..e63f3d6fe886b1214a9170785fb87e46268bc9f6 100644 --- a/src/anbox/bridge/platform_api_skeleton.cpp +++ b/src/anbox/bridge/platform_api_skeleton.cpp @@ -89,6 +89,11 @@ void PlatformApiSkeleton::handle_application_list_update_event( for (int m = 0; m < li.categories_size(); m++) item.launch_intent.categories.push_back(li.categories(m)); + item.icon = std::vector(app.icon().begin(), app.icon().end()); + + if (item.package.empty()) + continue; + // If the item is already stored it will be updated launcher_storage_->add(item); } diff --git a/src/anbox/cmds/run.cpp b/src/anbox/cmds/run.cpp index e335dfd17a911b9399dca5671b6a0673b007f720..8802459936671f9d726142e369cf4cb4d75ea8b6 100644 --- a/src/anbox/cmds/run.cpp +++ b/src/anbox/cmds/run.cpp @@ -114,7 +114,8 @@ anbox::cmds::Run::Run(const BusFactory &bus_factory) auto window_manager = std::make_shared(policy); auto launcher_storage = std::make_shared( - xdg::data().home() / "applications"); + xdg::data().home() / "applications" / "anbox", + xdg::data().home() / "anbox" / "icons"); auto gl_server = std::make_shared(window_manager); diff --git a/src/anbox/protobuf/anbox_bridge.proto b/src/anbox/protobuf/anbox_bridge.proto index 2bbb6008eec0ff9635ba2549d8b27a824f9bacce..8a919dc6152105d40f2b714f9ea37b3ab5c17576 100644 --- a/src/anbox/protobuf/anbox_bridge.proto +++ b/src/anbox/protobuf/anbox_bridge.proto @@ -74,6 +74,7 @@ message ApplicationListUpdateEvent { required string name = 1; required string package = 2; optional Intent launch_intent = 3; + optional bytes icon = 4; } repeated Application applications = 1; } diff --git a/src/anbox/utils.cpp b/src/anbox/utils.cpp index 3b4af39cf61f3f7013b5aee1654ea90f7e8f19dc..ae31a718b1bb958b6eb3d2fc1783ca0cb4a54503 100644 --- a/src/anbox/utils.cpp +++ b/src/anbox/utils.cpp @@ -158,5 +158,10 @@ std::string prefix_dir_from_env(const std::string &path, return result; } +std::string process_get_exe_path(const pid_t &pid) { + auto exe_path = string_format("/proc/%d/exe", pid); + return boost::filesystem::read_symlink(exe_path).string(); +} + } // namespace utils } // namespace anbox diff --git a/src/anbox/utils.h b/src/anbox/utils.h index 93c3946c2aac07b4e538b62147a0346362574fc2..ab2f2a51fba03b4ca8a7017bcdd3430356bb3816 100644 --- a/src/anbox/utils.h +++ b/src/anbox/utils.h @@ -48,6 +48,8 @@ void ensure_paths(const std::vector &paths); std::string prefix_dir_from_env(const std::string &path, const std::string &env_var); +std::string process_get_exe_path(const pid_t &pid); + template static std::string string_format(const std::string &fmt_str, Types &&... args); } // namespace utils