diff --git a/libflatpak_query.py b/libflatpak_query.py
index 9602424..3f4471a 100755
--- a/libflatpak_query.py
+++ b/libflatpak_query.py
@@ -1197,7 +1197,7 @@ def get_perm_key_file(app_id: str | None, override=False, system=False) -> GLib
return key_file
-def add_file_permissions(app_id: str, path: str, system=False) -> tuple[bool, str]:
+def add_file_permissions(app_id: str, path: str, perm_type=None, system=False) -> tuple[bool, str]:
"""
Add filesystem permissions to a Flatpak application.
@@ -1214,7 +1214,7 @@ def add_file_permissions(app_id: str, path: str, system=False) -> tuple[bool, st
try:
key_file = get_perm_key_file(app_id, system)
-
+ perm_type = perm_type or "filesystem"
# Handle special case for home directory
if path.lower() == "host":
filesystem_path = "host"
@@ -1229,32 +1229,35 @@ def add_file_permissions(app_id: str, path: str, system=False) -> tuple[bool, st
filesystem_path = path.rstrip('/')
if not key_file.has_group("Context"):
- key_file.set_string("Context", "filesystems", "")
+ key_file.set_string("Context", perm_type, "")
# Now get the keys
context_keys = key_file.get_keys("Context")
# Check if perm_type exists in the section
- if "filesystems" not in str(context_keys):
+ if perm_type not in str(context_keys):
# Create the key with an empty string
- key_file.set_string("Context", "filesystems", "")
+ key_file.set_string("Context", perm_type, "")
# Get existing filesystem paths
- existing_paths = key_file.get_string("Context", "filesystems")
- if existing_paths is None:
+ existing_paths = key_file.get_string("Context", perm_type)
+ if existing_paths is None or existing_paths == "":
# If no filesystems entry exists, create it
- key_file.set_string("Context", "filesystems", filesystem_path)
+ key_file.set_string("Context", perm_type, filesystem_path)
else:
# Split existing paths and check if our path already exists
+ print(existing_paths)
existing_paths_list = existing_paths.split(';')
-
+ print(existing_paths_list)
# Normalize paths for comparison (remove trailing slashes, convert to absolute paths)
normalized_new_path = os.path.abspath(filesystem_path.rstrip('/'))
normalized_existing_paths = [os.path.abspath(p.rstrip('/')) for p in existing_paths_list]
+ print(normalized_new_path)
+ print(normalized_existing_paths)
# Only add if the path doesn't already exist
if normalized_new_path not in normalized_existing_paths:
- key_file.set_string("Context", "filesystems",
+ key_file.set_string("Context", perm_type,
existing_paths + filesystem_path + ";")
# Write the modified metadata back
@@ -1269,7 +1272,7 @@ def add_file_permissions(app_id: str, path: str, system=False) -> tuple[bool, st
return False, f"Failed to modify permissions: {str(e)}"
-def remove_file_permissions(app_id: str, path: str, system=False) -> tuple[bool, str]:
+def remove_file_permissions(app_id: str, path: str, perm_type=None, system=False) -> tuple[bool, str]:
"""
Remove filesystem permissions from a Flatpak application.
@@ -1285,6 +1288,7 @@ def remove_file_permissions(app_id: str, path: str, system=False) -> tuple[bool,
"""
try:
key_file = get_perm_key_file(app_id, system)
+ perm_type = perm_type or "filesystem"
# Handle special case for home directory
if path.lower() == "host":
@@ -1300,7 +1304,7 @@ def remove_file_permissions(app_id: str, path: str, system=False) -> tuple[bool,
filesystem_path = path.rstrip('/')
# Get existing filesystem paths
- existing_paths = key_file.get_string("Context", "filesystems")
+ existing_paths = key_file.get_string("Context", perm_type)
if existing_paths is None:
return True, f"No filesystem permissions to remove for {app_id}"
@@ -1322,9 +1326,9 @@ def remove_file_permissions(app_id: str, path: str, system=False) -> tuple[bool,
new_permissions = ";".join(filtered_paths_list)
if new_permissions:
# Save changes
- key_file.set_string("Context", "filesystems", new_permissions)
+ key_file.set_string("Context", perm_type, new_permissions)
else:
- key_file.remove_key("Context", "filesystems")
+ key_file.remove_key("Context", perm_type)
# Write the modified metadata back
try:
@@ -1577,6 +1581,9 @@ def add_permission_value(app_id: str, perm_type: str, value: str, system=False)
key, val = parts
+ if val not in ['talk', 'own']:
+ return False, "Value must be in format 'key=value' with value as 'talk' or 'own'"
+
# Set the value
key_file.set_string(perm_type, key, val)
@@ -2141,10 +2148,7 @@ def portal_get_app_permissions(app_id: str):
# Format and return the results
if app_permissions:
- success_message = f"\nFound permissions for {app_id}:\n"
- for portal, permission in app_permissions.items():
- success_message += f"- {portal}: {permission}\n"
- return True, success_message.strip()
+ return True, app_permissions
return False, f"No permissions found for {app_id} in any portal"
@@ -2239,11 +2243,11 @@ def main():
parser.add_argument('--list-file-perms', action='store_true',
help='List configured file permissions for an app')
parser.add_argument('--list-other-perm-toggles', type=str, metavar='PERM_NAME',
- help='List configured other permission toggles for an app (e.g. "shared", "sockets", "devices", "features", "persistent")')
+ help='List configured other permission toggles for an app (e.g. "shared", "sockets", "devices", "features")')
parser.add_argument('--toggle-other-perms', type=str, metavar=('ENABLE/DISABLE'),
help='Toggle other permissions on/off (True/False)')
parser.add_argument('--perm-type', type=str,
- help='Type of permission to toggle (shared, sockets, devices, features)')
+ help='Type of permission to toggle (shared, sockets, devices, features, persistent)')
parser.add_argument('--perm-option', type=str,
help='Specific permission option to toggle (e.g. network, ipc)')
parser.add_argument('--list-other-perm-values', type=str, metavar='PERM_NAME',
@@ -2262,7 +2266,7 @@ def main():
parser.add_argument('--global-list-file-perms', action='store_true',
help='List configured file permissions for an app')
parser.add_argument('--global-list-other-perm-toggles', type=str, metavar='PERM_NAME',
- help='List configured other permission toggles for an app (e.g. "shared", "sockets", "devices", "features", "persistent")')
+ help='List configured other permission toggles for an app (e.g. "shared", "sockets", "devices", "features")')
parser.add_argument('--global-toggle-other-perms', type=str, metavar=('ENABLE/DISABLE'),
help='Toggle other permissions on/off (True/False)')
parser.add_argument('--global-list-other-perm-values', type=str, metavar='PERM_NAME',
@@ -2538,14 +2542,14 @@ def handle_subcategories(args, searcher):
def handle_add_file_perms(args, searcher):
try:
- success, message = add_file_permissions(args.id, args.add_file_perms, args.system)
+ success, message = add_file_permissions(args.id, args.add_file_perms, args.perm_type, args.system)
print(f"{message}")
except GLib.Error as e:
print(f"{str(e)}")
def handle_remove_file_perms(args, searcher):
try:
- success, message = remove_file_permissions(args.id, args.remove_file_perms, args.system)
+ success, message = remove_file_permissions(args.id, args.remove_file_perms, args.perm_type, args.system)
print(f"{message}")
except GLib.Error as e:
print(f"{str(e)}")
diff --git a/main.py b/main.py
index 6a8c433..54f5272 100755
--- a/main.py
+++ b/main.py
@@ -313,6 +313,7 @@ class MainWindow(Gtk.Window):
self.create_panels()
self.refresh_data()
+ #self.refresh_local()
# Select Trending by default
self.select_default_category()
@@ -1372,7 +1373,7 @@ class MainWindow(Gtk.Window):
condition=lambda x: True
)
add_rm_icon = "list-remove"
- add_rm_style = "dark-remove-buton"
+ add_rm_style = "dark-remove-button"
else:
button = self.create_button(
self.on_install_clicked,
@@ -1389,6 +1390,23 @@ class MainWindow(Gtk.Window):
button.get_style_context().add_class(add_rm_style)
buttons_box.pack_end(button, False, False, 0)
+ # Install/Remove button
+ if is_installed:
+ button = self.create_button(
+ self.on_app_options_clicked,
+ app,
+ None,
+ condition=lambda x: True
+ )
+ add_options_icon = "system-run"
+ add_options_style = "dark-remove-button"
+
+ if button:
+ use_icon = Gio.Icon.new_for_string(add_options_icon)
+ button.set_image(Gtk.Image.new_from_gicon(use_icon, Gtk.IconSize.BUTTON))
+ button.get_style_context().add_class(add_options_style)
+ buttons_box.pack_end(button, False, False, 0)
+
# Add Update button if available
if is_updatable:
update_button = self.create_button(
@@ -1649,6 +1667,525 @@ class MainWindow(Gtk.Window):
dialog.destroy()
+ def _add_bus_section(self, app_id, app, listbox, section_title, perm_type):
+ """Helper method to add System Bus or Session Bus section"""
+ # Add separator
+ sep = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
+ listbox.add(sep)
+
+ # Add section header
+ row_header = Gtk.ListBoxRow(selectable=False)
+ box_header = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
+ label_header = Gtk.Label(label=f"{section_title}",
+ use_markup=True, xalign=0)
+ box_header.pack_start(label_header, True, True, 0)
+ row_header.add(box_header)
+ listbox.add(row_header)
+
+ # Get permissions
+ success, perms = libflatpak_query.list_other_perm_values(app_id, perm_type, self.system_mode)
+ if not success:
+ perms = {"paths": []}
+
+ # Add Talks section
+ talks_row = Gtk.ListBoxRow(selectable=False)
+ talks_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
+ talks_row.add(talks_box)
+
+ talks_header = Gtk.Label(label="Talks", xalign=0)
+ talks_box.pack_start(talks_header, False, False, 0)
+
+ # Add separator between header and paths
+ talks_box.pack_start(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL),
+ False, False, 0)
+
+ # Add talk paths
+ for path in perms["paths"]:
+ if "talk" in path:
+ row = Gtk.ListBoxRow(selectable=False)
+ hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
+ row.add(hbox)
+
+ vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ hbox.pack_start(vbox, True, True, 0)
+
+ label = Gtk.Label(label=path.split("=")[0], xalign=0)
+ vbox.pack_start(label, True, True, 0)
+
+ btn = Gtk.Button(label="Remove")
+ btn.connect("clicked", self._on_remove_path, app_id, app, path, perm_type)
+ hbox.pack_end(btn, False, True, 0)
+
+ talks_box.add(row)
+
+ listbox.add(talks_row)
+
+ # Add Owns section
+ owns_row = Gtk.ListBoxRow(selectable=False)
+ owns_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
+ owns_row.add(owns_box)
+
+ owns_header = Gtk.Label(label="Owns", xalign=0)
+ owns_box.pack_start(owns_header, False, False, 0)
+
+ # Add separator between header and paths
+ owns_box.pack_start(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL),
+ False, False, 0)
+
+ # Add own paths
+ for path in perms["paths"]:
+ if "own" in path:
+ row = Gtk.ListBoxRow(selectable=False)
+ hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
+ row.add(hbox)
+
+ vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ hbox.pack_start(vbox, True, True, 0)
+
+ label = Gtk.Label(label=path.split("=")[0], xalign=0)
+ vbox.pack_start(label, True, True, 0)
+
+ btn = Gtk.Button(label="Remove")
+ btn.connect("clicked", self._on_remove_path, app_id, app, path, perm_type)
+ hbox.pack_end(btn, False, True, 0)
+
+ owns_box.add(row)
+
+ owns_row.show_all()
+ listbox.add(owns_row)
+
+ # Add add button
+ add_path_row = Gtk.ListBoxRow(selectable=False)
+ hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
+ add_path_row.add(hbox)
+
+ btn = Gtk.Button(label="Add Path")
+ btn.connect("clicked", self._on_add_path, app_id, app, perm_type)
+ hbox.pack_end(btn, False, True, 0)
+
+ listbox.add(add_path_row)
+
+ def _add_path_section(self, app_id, app, listbox, section_title, perm_type):
+ """Helper method to add sections with paths (Persistent, Environment)"""
+ # Add separator
+ sep = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
+ listbox.add(sep)
+
+ # Add section header
+ row_header = Gtk.ListBoxRow(selectable=False)
+ box_header = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
+ label_header = Gtk.Label(label=f"{section_title}",
+ use_markup=True, xalign=0)
+ box_header.pack_start(label_header, True, True, 0)
+ row_header.add(box_header)
+ listbox.add(row_header)
+
+ # Get permissions
+ if perm_type == "persistent":
+ success, perms = libflatpak_query.list_other_perm_toggles(app_id, perm_type, self.system_mode)
+ else:
+ success, perms = libflatpak_query.list_other_perm_values(app_id, perm_type, self.system_mode)
+ if not success:
+ perms = {"paths": []}
+
+ # Add paths
+ for path in perms["paths"]:
+ row = Gtk.ListBoxRow(selectable=False)
+ hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
+ row.add(hbox)
+
+ vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ hbox.pack_start(vbox, True, True, 0)
+
+ label = Gtk.Label(label=path, xalign=0)
+ vbox.pack_start(label, True, True, 0)
+
+ btn = Gtk.Button(label="Remove")
+ btn.connect("clicked", self._on_remove_path, app_id, app, path, perm_type)
+ hbox.pack_end(btn, False, True, 0)
+
+ listbox.add(row)
+
+ # Add add button
+ row = Gtk.ListBoxRow(selectable=False)
+ hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
+ row.add(hbox)
+
+ btn = Gtk.Button(label="Add Path")
+ btn.connect("clicked", self._on_add_path, app_id, app, perm_type)
+ hbox.pack_end(btn, False, True, 0)
+
+ listbox.add(row)
+
+ def _add_filesystem_section(self, app_id, app, listbox, section_title):
+ """Helper method to add the Filesystems section"""
+ # Add separator
+ sep = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
+ listbox.add(sep)
+
+ # Add section header
+ row_header = Gtk.ListBoxRow(selectable=False)
+ box_header = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
+ label_header = Gtk.Label(label=f"{section_title}",
+ use_markup=True, xalign=0)
+ box_header.pack_start(label_header, True, True, 0)
+ row_header.add(box_header)
+ listbox.add(row_header)
+
+ # Get filesystem permissions
+ success, perms = libflatpak_query.list_file_perms(app_id, self.system_mode)
+ if not success:
+ perms = {"paths": [], "special_paths": []}
+
+ # Add special paths as toggles
+ special_paths = [
+ ("All user files", "home", "Access to all user files"),
+ ("All system files", "host", "Access to all system files"),
+ ("All system libraries, executables and static data", "host-os", "Access to system libraries and executables"),
+ ("All system configurations", "host-etc", "Access to system configurations")
+ ]
+
+ for display_text, option, description in special_paths:
+ row = Gtk.ListBoxRow(selectable=False)
+ hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
+ row.add(hbox)
+
+ vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ hbox.pack_start(vbox, True, True, 0)
+
+ label = Gtk.Label(label=display_text, xalign=0)
+ desc = Gtk.Label(label=description, xalign=0)
+ vbox.pack_start(label, True, True, 0)
+ vbox.pack_start(desc, True, True, 0)
+
+ switch = Gtk.Switch()
+ switch.props.valign = Gtk.Align.CENTER
+ switch.set_active(option in perms["special_paths"])
+ switch.set_sensitive(True)
+ switch.connect("state-set", self._on_switch_toggled, app_id, "filesystems", option)
+ hbox.pack_end(switch, False, True, 0)
+
+ listbox.add(row)
+
+ # Add normal paths with remove buttons
+ for path in perms["paths"]:
+ if path != "":
+ row = Gtk.ListBoxRow(selectable=False)
+ hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
+ row.add(hbox)
+
+ vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ hbox.pack_start(vbox, True, True, 0)
+
+ label = Gtk.Label(label=path, xalign=0)
+ vbox.pack_start(label, True, True, 0)
+
+ btn = Gtk.Button(label="Remove")
+ btn.connect("clicked", self._on_remove_path, app_id, app, path)
+ hbox.pack_end(btn, False, True, 0)
+
+ listbox.add(row)
+
+ # Add add button
+ row = Gtk.ListBoxRow(selectable=False)
+ hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
+ row.add(hbox)
+
+ btn = Gtk.Button(label="Add Path")
+ btn.connect("clicked", self._on_add_path, app_id, app)
+ hbox.pack_end(btn, False, True, 0)
+
+ listbox.add(row)
+
+
+ def on_app_options_clicked(self, button, app):
+ """Handle the app options click"""
+ details = app.get_details()
+ app_id = details['id']
+
+ # Create window (as before)
+ self.options_window = Gtk.Window(title=f"{details['name']} Settings")
+ self.options_window.set_default_size(500, 700)
+
+ # Set subtitle
+ header_bar = Gtk.HeaderBar(title=f"{details['name']} Settings",
+ subtitle="List of resources selectively granted to the application")
+ header_bar.set_show_close_button(True)
+ self.options_window.set_titlebar(header_bar)
+
+ # Create main container with padding
+ box_outer = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
+ box_outer.set_border_width(20)
+ self.options_window.add(box_outer)
+
+ # Create scrolled window for content
+ scrolled = Gtk.ScrolledWindow()
+ scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
+
+ # Create list box for options
+ listbox = Gtk.ListBox()
+ listbox.set_selection_mode(Gtk.SelectionMode.NONE)
+
+ # Add Portals section first
+ self._add_section(app_id, listbox, "Portals", section_options=[
+ ("Background", "background", "Can run in the background"),
+ ("Notifications", "notifications", "Can send notifications"),
+ ("Microphone", "microphone", "Can listen to your microphone"),
+ ("Speakers", "speakers", "Can play sounds to your speakers"),
+ ("Camera", "camera", "Can record videos with your camera"),
+ ("Location", "location", "Can access your location")
+ ])
+
+ # Add other sections with correct permission types
+ self._add_section(app_id, listbox, "Shared", "shared", [
+ ("Network", "network", "Can communicate over network"),
+ ("Inter-process communications", "ipc", "Can communicate with other applications")
+ ])
+
+ self._add_section(app_id, listbox, "Sockets", "sockets", [
+ ("X11 windowing system", "x11", "Can access X11 display server"),
+ ("Wayland windowing system", "wayland", "Can access Wayland display server"),
+ ("Fallback to X11 windowing system", "fallback-x11", "Can fallback to X11 if Wayland unavailable"),
+ ("PulseAudio sound server", "pulseaudio", "Can access PulseAudio sound system"),
+ ("D-Bus session bus", "session-bus", "Can communicate with session D-Bus"),
+ ("D-Bus system bus", "system-bus", "Can communicate with system D-Bus"),
+ ("Secure Shell agent", "ssh-auth", "Can access SSH authentication agent"),
+ ("Smart cards", "pcsc", "Can access smart card readers"),
+ ("Printing system", "cups", "Can access printing subsystem"),
+ ("GPG-Agent directories", "gpg-agent", "Can access GPG keyring"),
+ ("Inherit Wayland socket", "inherit-wayland-socket", "Can inherit existing Wayland socket")
+ ])
+
+ self._add_section(app_id, listbox, "Devices", "devices", [
+ ("GPU Acceleration", "dri", "Can use hardware graphics acceleration"),
+ ("Input devices", "input", "Can access input devices"),
+ ("Virtualization", "kvm", "Can access virtualization services"),
+ ("Shared memory", "shm", "Can use shared memory"),
+ ("All devices (e.g. webcam)", "all", "Can access all device files")
+ ])
+
+ self._add_section(app_id, listbox, "Features", "features", [
+ ("Development syscalls", "devel", "Can perform development operations"),
+ ("Programs from other architectures", "multiarch", "Can execute programs from other architectures"),
+ ("Bluetooth", "bluetooth", "Can access Bluetooth hardware"),
+ ("Controller Area Network bus", "canbus", "Can access CAN bus"),
+ ("Application Shared Memory", "per-app-dev-shm", "Can use shared memory for IPC")
+ ])
+
+ # Add Filesystems section
+ self._add_filesystem_section(app_id, app, listbox, "Filesystems")
+ self._add_path_section(app_id, app, listbox, "Persistent", "persistent")
+ self._add_path_section(app_id, app, listbox, "Environment", "environment")
+ self._add_bus_section(app_id, app, listbox, "System Bus", "system_bus")
+ self._add_bus_section(app_id, app, listbox, "Session Bus", "session_bus")
+
+ # Add widgets to container
+ box_outer.pack_start(scrolled, True, True, 0)
+ scrolled.add(listbox)
+
+ # Connect destroy signal
+ self.options_window.connect("destroy", lambda w: w.destroy())
+
+ # Show window
+ self.options_window.show_all()
+
+ def _add_section(self, app_id, listbox, section_title, perm_type=None, section_options=None):
+ """Helper method to add a section with multiple options"""
+ # Add separator
+ sep = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
+ listbox.add(sep)
+
+ # Add section header
+ row_header = Gtk.ListBoxRow(selectable=False)
+ box_header = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
+ label_header = Gtk.Label(label=f"{section_title}",
+ use_markup=True, xalign=0)
+ box_header.pack_start(label_header, True, True, 0)
+ row_header.add(box_header)
+ listbox.add(row_header)
+
+ # Handle portal permissions specially
+ if section_title == "Portals":
+ success, perms = libflatpak_query.portal_get_app_permissions(app_id)
+ if not success:
+ perms = {}
+ elif section_title in ["Persistent", "Environment", "System Bus", "Session Bus"]:
+ success, perms = libflatpak_query.list_other_perm_toggles(app_id, perm_type, self.system_mode)
+ if not success:
+ perms = {"paths": []}
+ else:
+ success, perms = libflatpak_query.list_other_perm_toggles(app_id, perm_type, self.system_mode)
+ if not success:
+ perms = {"paths": []}
+
+ if section_options:
+ # Add options
+ for display_text, option, description in section_options:
+ row = Gtk.ListBoxRow(selectable=False)
+ hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
+ row.add(hbox)
+
+ vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ hbox.pack_start(vbox, True, True, 0)
+
+ label = Gtk.Label(label=display_text, xalign=0)
+ desc = Gtk.Label(label=description, xalign=0)
+ vbox.pack_start(label, True, True, 0)
+ vbox.pack_start(desc, True, True, 0)
+
+ switch = Gtk.Switch()
+ switch.props.valign = Gtk.Align.CENTER
+
+ # Handle portal permissions differently
+ if section_title == "Portals":
+ if option in perms:
+ switch.set_active(perms[option] == 'yes')
+ switch.set_sensitive(True)
+ else:
+ switch.set_sensitive(False)
+ else:
+ switch.set_active(option in [p.lower() for p in perms["paths"]])
+ switch.set_sensitive(True)
+
+ switch.connect("state-set", self._on_switch_toggled, app_id, perm_type, option)
+ hbox.pack_end(switch, False, True, 0)
+
+ listbox.add(row)
+
+ def _on_switch_toggled(self, switch, state, app_id, perm_type, option):
+ """Handle switch toggle events"""
+ if perm_type is None: # Portal section
+ success, message = libflatpak_query.portal_set_app_permissions(
+ option.lower(),
+ app_id,
+ "yes" if state else "no"
+ )
+ else:
+ success, message = libflatpak_query.toggle_other_perms(
+ app_id,
+ perm_type,
+ option.lower(),
+ state,
+ self.system_mode
+ )
+
+ if not success:
+ switch.set_active(not state)
+ print(f"Error: {message}")
+
+ def _on_remove_path(self, button, app_id, app, path, perm_type=None):
+ """Handle remove path button click"""
+ if perm_type:
+ if perm_type == "persistent":
+ success, message = libflatpak_query.remove_file_permissions(
+ app_id,
+ path,
+ "persistent",
+ self.system_mode
+ )
+ else:
+ success, message = libflatpak_query.remove_permission_value(
+ app_id,
+ perm_type,
+ path,
+ self.system_mode
+ )
+ else:
+ success, message = libflatpak_query.remove_file_permissions(
+ app_id,
+ path,
+ None,
+ self.system_mode
+ )
+ if success:
+ # Refresh the current window
+ self.options_window.destroy()
+ self.on_app_options_clicked(None, app)
+
+ def _on_add_path(self, button, app_id, app, perm_type=None):
+ """Handle add path button click"""
+ dialog = Gtk.Dialog(
+ title="Add Filesystem Path",
+ parent=self.options_window,
+ modal=True,
+ destroy_with_parent=True,
+ )
+
+ # Add buttons separately
+ dialog.add_button("Cancel", Gtk.ResponseType.CANCEL)
+ dialog.add_button("Add", Gtk.ResponseType.OK)
+
+ entry = Gtk.Entry()
+ entry.set_placeholder_text("Enter filesystem path")
+ dialog.vbox.pack_start(entry, True, True, 0)
+ dialog.show_all()
+
+ response = dialog.run()
+ if response == Gtk.ResponseType.OK:
+ path = entry.get_text()
+ if perm_type:
+ if perm_type == "persistent":
+ success, message = libflatpak_query.add_file_permissions(
+ app_id,
+ path,
+ "persistent",
+ self.system_mode
+ )
+ else:
+ success, message = libflatpak_query.add_permission_value(
+ app_id,
+ perm_type,
+ path,
+ self.system_mode
+ )
+ else:
+ success, message = libflatpak_query.add_file_permissions(
+ app_id,
+ path,
+ None,
+ self.system_mode
+ )
+ if success:
+ # Refresh the current window
+ self.options_window.destroy()
+ self.on_app_options_clicked(None, app)
+ message_type = Gtk.MessageType.INFO
+ else:
+ message_type = Gtk.MessageType.ERROR
+ if message:
+ error_dialog = Gtk.MessageDialog(
+ transient_for=None, # Changed from self
+ modal=True,
+ destroy_with_parent=True,
+ message_type=message_type,
+ buttons=Gtk.ButtonsType.OK,
+ text=message
+ )
+ error_dialog.run()
+ error_dialog.destroy()
+ dialog.destroy()
+
+ def _add_option(self, parent_box, label_text, description):
+ """Helper method to add an individual option"""
+ row = Gtk.ListBoxRow(selectable=False)
+ hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
+ row.add(hbox)
+
+ vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ hbox.pack_start(vbox, True, True, 0)
+
+ label = Gtk.Label(label=label_text, xalign=0)
+ desc = Gtk.Label(label=description, xalign=0)
+ vbox.pack_start(label, True, True, 0)
+ vbox.pack_start(desc, True, True, 0)
+
+ switch = Gtk.Switch()
+ switch.props.valign = Gtk.Align.CENTER
+ hbox.pack_end(switch, False, True, 0)
+
+ parent_box.add(row)
+ return row, switch
+
def on_update_clicked(self, button, app):
"""Handle the Remove button click with removal options"""
details = app.get_details()
diff --git a/subcategories_data.json b/subcategories_data.json
index 9b4db0c..8e09ba9 100644
--- a/subcategories_data.json
+++ b/subcategories_data.json
@@ -2227,7 +2227,7 @@
}
],
"query": "",
- "processingTimeMs": 7,
+ "processingTimeMs": 8,
"hitsPerPage": 250,
"page": 1,
"totalPages": 1,
@@ -3812,7 +3812,7 @@
}
],
"query": "",
- "processingTimeMs": 6,
+ "processingTimeMs": 5,
"hitsPerPage": 250,
"page": 1,
"totalPages": 1,
@@ -10295,7 +10295,7 @@
}
],
"query": "",
- "processingTimeMs": 16,
+ "processingTimeMs": 19,
"hitsPerPage": 250,
"page": 1,
"totalPages": 1,
@@ -12125,7 +12125,7 @@
}
],
"query": "",
- "processingTimeMs": 4,
+ "processingTimeMs": 3,
"hitsPerPage": 250,
"page": 1,
"totalPages": 1,
@@ -13672,7 +13672,7 @@
}
],
"query": "",
- "processingTimeMs": 2,
+ "processingTimeMs": 3,
"hitsPerPage": 250,
"page": 1,
"totalPages": 1,
@@ -14134,7 +14134,7 @@
}
],
"query": "",
- "processingTimeMs": 3,
+ "processingTimeMs": 2,
"hitsPerPage": 250,
"page": 1,
"totalPages": 1,
@@ -17220,7 +17220,7 @@
"verification_website": null,
"verification_timestamp": null,
"runtime": "org.freedesktop.Sdk/x86_64/24.08",
- "updated_at": 1743670880,
+ "updated_at": 1743689559,
"arches": [
"x86_64",
"aarch64"
@@ -18256,7 +18256,7 @@
}
],
"query": "",
- "processingTimeMs": 3,
+ "processingTimeMs": 2,
"hitsPerPage": 250,
"page": 1,
"totalPages": 1,
@@ -18992,7 +18992,7 @@
}
],
"query": "",
- "processingTimeMs": 4,
+ "processingTimeMs": 3,
"hitsPerPage": 250,
"page": 1,
"totalPages": 1,
@@ -34567,7 +34567,7 @@
"project_license": "MPL-2.0",
"is_free_license": true,
"app_id": "info.cemu.Cemu",
- "icon": "https://dl.flathub.org/media/info/cemu/Cemu/c2ee1c87418d1055b5efda32173e2eeb/icons/128x128/info.cemu.Cemu.png",
+ "icon": "https://dl.flathub.org/media/info/cemu/Cemu/87640c667068bea4e270477be92997a3/icons/128x128/info.cemu.Cemu.png",
"main_categories": "game",
"sub_categories": [
"Emulator"
@@ -34581,7 +34581,7 @@
"verification_website": "cemu.info",
"verification_timestamp": "1679779906",
"runtime": "org.freedesktop.Platform/x86_64/24.08",
- "updated_at": 1742475302,
+ "updated_at": 1743689882,
"arches": [
"x86_64"
],
@@ -35141,7 +35141,7 @@
"project_license": "GPL-3.0",
"is_free_license": true,
"app_id": "io.github.gopher64.gopher64",
- "icon": "https://dl.flathub.org/media/io/github/gopher64.gopher64/364d7e1b846fb507371416d5721b81a2/icons/128x128/io.github.gopher64.gopher64.png",
+ "icon": "https://dl.flathub.org/media/io/github/gopher64.gopher64/459074e53a887ab9af8f43da34f41274/icons/128x128/io.github.gopher64.gopher64.png",
"main_categories": "game",
"sub_categories": [
"Emulator"
@@ -35155,7 +35155,7 @@
"verification_website": null,
"verification_timestamp": "1699221966",
"runtime": "org.freedesktop.Platform/x86_64/24.08",
- "updated_at": 1742988449,
+ "updated_at": 1743677523,
"arches": [
"x86_64",
"aarch64"
@@ -42183,7 +42183,7 @@
}
],
"query": "",
- "processingTimeMs": 13,
+ "processingTimeMs": 12,
"hitsPerPage": 250,
"page": 1,
"totalPages": 1,
@@ -52249,7 +52249,7 @@
"project_license": "LicenseRef-proprietary",
"is_free_license": false,
"app_id": "com.wireframesketcher.WireframeSketcher",
- "icon": "https://dl.flathub.org/media/com/wireframesketcher/WireframeSketcher/63c0b229d59a15a516bb37f18c159575/icons/128x128/com.wireframesketcher.WireframeSketcher.png",
+ "icon": "https://dl.flathub.org/media/com/wireframesketcher/WireframeSketcher/08fc4f4f982674baf2112878c7fa2e3f/icons/128x128/com.wireframesketcher.WireframeSketcher.png",
"main_categories": "graphics",
"sub_categories": [
"VectorGraphics",
@@ -52263,8 +52263,8 @@
"verification_login_is_organization": "false",
"verification_website": "wireframesketcher.com",
"verification_timestamp": "1728995861",
- "runtime": "org.gnome.Platform/x86_64/47",
- "updated_at": 1738504410,
+ "runtime": "org.gnome.Platform/x86_64/48",
+ "updated_at": 1743686066,
"arches": [
"x86_64"
],
@@ -54042,7 +54042,7 @@
"project_license": "LicenseRef-proprietary=https://www.popcornfx.com/terms-and-conditions",
"is_free_license": false,
"app_id": "com.popcornfx.PopcornFXEditor",
- "icon": "https://dl.flathub.org/media/com/popcornfx/PopcornFXEditor/bbc16f228a468060e76c2bcf1f055c11/icons/128x128/com.popcornfx.PopcornFXEditor.png",
+ "icon": "https://dl.flathub.org/media/com/popcornfx/PopcornFXEditor/8950264504787fde2a41a89e6cc59a6b/icons/128x128/com.popcornfx.PopcornFXEditor.png",
"main_categories": "graphics",
"sub_categories": [
"3DGraphics"
@@ -54056,7 +54056,7 @@
"verification_website": "popcornfx.com",
"verification_timestamp": "1740390969",
"runtime": "org.freedesktop.Platform/x86_64/24.08",
- "updated_at": 1740561877,
+ "updated_at": 1743670231,
"arches": [
"x86_64"
],
@@ -60274,7 +60274,7 @@
"project_license": "LicenseRef-proprietary",
"is_free_license": false,
"app_id": "com.wireframesketcher.WireframeSketcher",
- "icon": "https://dl.flathub.org/media/com/wireframesketcher/WireframeSketcher/63c0b229d59a15a516bb37f18c159575/icons/128x128/com.wireframesketcher.WireframeSketcher.png",
+ "icon": "https://dl.flathub.org/media/com/wireframesketcher/WireframeSketcher/08fc4f4f982674baf2112878c7fa2e3f/icons/128x128/com.wireframesketcher.WireframeSketcher.png",
"main_categories": "graphics",
"sub_categories": [
"VectorGraphics",
@@ -60288,8 +60288,8 @@
"verification_login_is_organization": "false",
"verification_website": "wireframesketcher.com",
"verification_timestamp": "1728995861",
- "runtime": "org.gnome.Platform/x86_64/47",
- "updated_at": 1738504410,
+ "runtime": "org.gnome.Platform/x86_64/48",
+ "updated_at": 1743686066,
"arches": [
"x86_64"
],
@@ -63677,7 +63677,7 @@
"project_license": "AGPL-3.0-only",
"is_free_license": true,
"app_id": "org.signal.Signal",
- "icon": "https://dl.flathub.org/media/org/signal/Signal/2566ff6b14efe7a97e76b251a00480c5/icons/128x128/org.signal.Signal.png",
+ "icon": "https://dl.flathub.org/media/org/signal/Signal/7958a28cf241175ba36c885f9577b5f3/icons/128x128/org.signal.Signal.png",
"main_categories": "network",
"sub_categories": [
"InstantMessaging",
@@ -63692,7 +63692,7 @@
"verification_website": null,
"verification_timestamp": null,
"runtime": "org.freedesktop.Platform/x86_64/24.08",
- "updated_at": 1743062228,
+ "updated_at": 1743667211,
"arches": [
"x86_64"
],
@@ -69733,7 +69733,7 @@
}
],
"query": "",
- "processingTimeMs": 3,
+ "processingTimeMs": 2,
"hitsPerPage": 250,
"page": 1,
"totalPages": 1,
@@ -69918,7 +69918,7 @@
"project_license": "AGPL-3.0-only",
"is_free_license": true,
"app_id": "org.signal.Signal",
- "icon": "https://dl.flathub.org/media/org/signal/Signal/2566ff6b14efe7a97e76b251a00480c5/icons/128x128/org.signal.Signal.png",
+ "icon": "https://dl.flathub.org/media/org/signal/Signal/7958a28cf241175ba36c885f9577b5f3/icons/128x128/org.signal.Signal.png",
"main_categories": "network",
"sub_categories": [
"InstantMessaging",
@@ -69933,7 +69933,7 @@
"verification_website": null,
"verification_timestamp": null,
"runtime": "org.freedesktop.Platform/x86_64/24.08",
- "updated_at": 1743062228,
+ "updated_at": 1743667211,
"arches": [
"x86_64"
],
@@ -73160,7 +73160,7 @@
}
],
"query": "",
- "processingTimeMs": 12,
+ "processingTimeMs": 11,
"hitsPerPage": 250,
"page": 1,
"totalPages": 1,
@@ -74158,7 +74158,7 @@
}
],
"query": "",
- "processingTimeMs": 2,
+ "processingTimeMs": 3,
"hitsPerPage": 250,
"page": 1,
"totalPages": 1,
@@ -76180,7 +76180,7 @@
"project_license": "LGPL-3.0-only",
"is_free_license": true,
"app_id": "org.tribler.Tribler",
- "icon": "https://dl.flathub.org/media/org/tribler/Tribler/a2f52ea30ba1a4929dbb202e03d3e570/icons/128x128/org.tribler.Tribler.png",
+ "icon": "https://dl.flathub.org/media/org/tribler/Tribler/c0501155d99aa6ac0b25612ca1cb8d81/icons/128x128/org.tribler.Tribler.png",
"main_categories": "network",
"sub_categories": [
"P2P"
@@ -76194,7 +76194,7 @@
"verification_website": "tribler.org",
"verification_timestamp": "1706271488",
"runtime": "org.freedesktop.Platform/x86_64/24.08",
- "updated_at": 1742060332,
+ "updated_at": 1743669027,
"arches": [
"x86_64",
"aarch64"
@@ -78324,7 +78324,7 @@
"project_license": "BSD-3-Clause and LGPL-2.1+ and Apache-2.0 and IJG and MIT and GPL-2.0+ and ISC and OpenSSL and (MPL-1.1 or GPL-2.0 or LGPL-2.0)",
"is_free_license": true,
"app_id": "io.github.ungoogled_software.ungoogled_chromium",
- "icon": "https://dl.flathub.org/media/io/github/ungoogled_software.ungoogled_chromium/39894c6a7477629d3ab86d3d95ef4aeb/icons/128x128/io.github.ungoogled_software.ungoogled_chromium.png",
+ "icon": "https://dl.flathub.org/media/io/github/ungoogled_software.ungoogled_chromium/7c63ca7b5da5a57b933e1111746511e1/icons/128x128/io.github.ungoogled_software.ungoogled_chromium.png",
"main_categories": "network",
"sub_categories": [
"WebBrowser",
@@ -78340,7 +78340,7 @@
"verification_website": null,
"verification_timestamp": "1713606111",
"runtime": "org.freedesktop.Platform/x86_64/24.08",
- "updated_at": 1743133926,
+ "updated_at": 1743673160,
"arches": [
"x86_64",
"aarch64"
@@ -79779,7 +79779,7 @@
"project_license": "LicenseRef-proprietary=https://wavebox.io/eula",
"is_free_license": false,
"app_id": "io.wavebox.Wavebox",
- "icon": "https://dl.flathub.org/media/io/wavebox/Wavebox/2b3f6b5b0cd051bf329fad85e03875cc/icons/128x128/io.wavebox.Wavebox.png",
+ "icon": "https://dl.flathub.org/media/io/wavebox/Wavebox/7456cfd08230164fd31c4dc164e96a2a/icons/128x128/io.wavebox.Wavebox.png",
"main_categories": "network",
"sub_categories": [
"WebBrowser"
@@ -79793,7 +79793,7 @@
"verification_website": null,
"verification_timestamp": null,
"runtime": "org.freedesktop.Platform/x86_64/21.08",
- "updated_at": 1742817444,
+ "updated_at": 1743674289,
"arches": [
"x86_64"
],
@@ -79928,7 +79928,7 @@
}
],
"query": "",
- "processingTimeMs": 6,
+ "processingTimeMs": 7,
"hitsPerPage": 250,
"page": 1,
"totalPages": 1,
@@ -79959,7 +79959,7 @@
"project_license": "BSD-3-Clause and LGPL-2.1+ and Apache-2.0 and IJG and MIT and GPL-2.0+ and ISC and OpenSSL and (MPL-1.1 or GPL-2.0 or LGPL-2.0)",
"is_free_license": true,
"app_id": "io.github.ungoogled_software.ungoogled_chromium",
- "icon": "https://dl.flathub.org/media/io/github/ungoogled_software.ungoogled_chromium/39894c6a7477629d3ab86d3d95ef4aeb/icons/128x128/io.github.ungoogled_software.ungoogled_chromium.png",
+ "icon": "https://dl.flathub.org/media/io/github/ungoogled_software.ungoogled_chromium/7c63ca7b5da5a57b933e1111746511e1/icons/128x128/io.github.ungoogled_software.ungoogled_chromium.png",
"main_categories": "network",
"sub_categories": [
"WebBrowser",
@@ -79975,7 +79975,7 @@
"verification_website": null,
"verification_timestamp": "1713606111",
"runtime": "org.freedesktop.Platform/x86_64/24.08",
- "updated_at": 1743133926,
+ "updated_at": 1743673160,
"arches": [
"x86_64",
"aarch64"
@@ -82634,7 +82634,7 @@
}
],
"query": "",
- "processingTimeMs": 2,
+ "processingTimeMs": 3,
"hitsPerPage": 250,
"page": 1,
"totalPages": 1,
@@ -84203,7 +84203,7 @@
}
],
"query": "",
- "processingTimeMs": 7,
+ "processingTimeMs": 6,
"hitsPerPage": 250,
"page": 1,
"totalPages": 1,
@@ -84436,7 +84436,7 @@
"project_license": "MIT",
"is_free_license": true,
"app_id": "com.super_productivity.SuperProductivity",
- "icon": "https://dl.flathub.org/media/com/super_productivity/SuperProductivity/53bb571f02820641926d8f042929a3c8/icons/128x128/com.super_productivity.SuperProductivity.png",
+ "icon": "https://dl.flathub.org/media/com/super_productivity/SuperProductivity/be0a31f47f3837c8b137110fea0bb11c/icons/128x128/com.super_productivity.SuperProductivity.png",
"main_categories": "office",
"sub_categories": [
"ProjectManagement"
@@ -84450,7 +84450,7 @@
"verification_website": "super-productivity.com",
"verification_timestamp": "1733677286",
"runtime": "org.freedesktop.Platform/x86_64/24.08",
- "updated_at": 1741964323,
+ "updated_at": 1743686508,
"arches": [
"x86_64"
],
@@ -85457,7 +85457,7 @@
"verification_website": null,
"verification_timestamp": "1681472057",
"runtime": "org.kde.Platform/x86_64/6.8",
- "updated_at": 1743633547,
+ "updated_at": 1743677378,
"arches": [
"x86_64",
"aarch64"
@@ -85666,7 +85666,7 @@
"project_license": "Zlib",
"is_free_license": true,
"app_id": "com.strlen.TreeSheets",
- "icon": "https://dl.flathub.org/media/com/strlen/TreeSheets/a56dee5995e3e5b4742dfc3313966697/icons/128x128/com.strlen.TreeSheets.png",
+ "icon": "https://dl.flathub.org/media/com/strlen/TreeSheets/fa00efbb2fc13f353508162cb663dba9/icons/128x128/com.strlen.TreeSheets.png",
"main_categories": "office",
"sub_categories": [
"Spreadsheet",
@@ -85682,7 +85682,7 @@
"verification_website": null,
"verification_timestamp": null,
"runtime": "org.freedesktop.Platform/x86_64/23.08",
- "updated_at": 1743492010,
+ "updated_at": 1743674544,
"arches": [
"x86_64",
"aarch64"
@@ -87983,7 +87983,7 @@
"verification_website": null,
"verification_timestamp": null,
"runtime": "org.gnome.Platform/x86_64/46",
- "updated_at": 1743647957,
+ "updated_at": 1743681287,
"arches": [
"x86_64",
"aarch64"
@@ -89321,7 +89321,7 @@
}
],
"query": "",
- "processingTimeMs": 2,
+ "processingTimeMs": 3,
"hitsPerPage": 250,
"page": 1,
"totalPages": 1,
@@ -90915,7 +90915,7 @@
}
],
"query": "",
- "processingTimeMs": 3,
+ "processingTimeMs": 2,
"hitsPerPage": 250,
"page": 1,
"totalPages": 1,
@@ -93377,7 +93377,7 @@
}
],
"query": "",
- "processingTimeMs": 4,
+ "processingTimeMs": 5,
"hitsPerPage": 250,
"page": 1,
"totalPages": 1,