#!/usr/bin/python import os import errno from shutil import rmtree import json import urllib.request from urllib.request import urlopen import urllib.parse import requests import subprocess import signal import threading import keyring from time import sleep import platform from packaging import version # PyGObject import gi gi.require_version("Gtk", "3.0") gi.require_version("Handy", "1") gi.require_version("Notify", "0.7") try: gi.require_version("AppIndicator3", "0.1") from gi.repository import Gtk, AppIndicator3 as appindicator except ValueError: # Fix for Solus and other Ayatana users gi.require_version('AyatanaAppIndicator3', '0.1') from gi.repository import Gtk, AyatanaAppIndicator3 as appindicator from gi.repository import GLib from gi.repository import GObject, Handy from gi.repository import GdkPixbuf from gi.repository import Notify from gi.repository import Gdk GObject.type_ensure(Handy.ActionRow) installedcheck = False computer_cpu_platform = platform.machine() def resource_path(relative_path): global installedcheck CheckRun10 = subprocess.run( f"find /usr/lib/althea/althea > /dev/null 2>&1", shell=True ) if CheckRun10.returncode == 0: installedcheck = True base_path = "/usr/lib/althea" else: base_path = os.path.abspath(".") return os.path.join(base_path, relative_path) installedcheck = subprocess.run("test -e /usr/lib/althea/althea", shell=True).returncode == 0 base_path = "/usr/lib/althea" if installedcheck else os.path.abspath(".") # Global variables ipa_path_exists = False savedcheck = False InsAltStore = subprocess.Popen( "test", stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True ) login_or_file_chooser = "login" apple_id = "lol" password = "lol" Warnmsg = "warn" Failmsg = "fail" icon_name = "changes-prevent-symbolic" command_six = Gtk.CheckMenuItem(label="Launch at Login") AltServer = "$HOME/.local/share/althea/AltServer" AnisetteServer = "$HOME/.local/share/althea/anisette-server" AltStore = "$HOME/.local/share/althea/AltStore.ipa" PATH = AltStore AutoStart = resource_path("resources/AutoStart.sh") altheapath = os.path.join( os.environ.get("XDG_DATA_HOME") or f'{ os.environ["HOME"] }/.local/share', "althea", ) export_anisette = "export ALTSERVER_ANISETTE_SERVER='http://127.0.0.1:6969'" # Check version with open(resource_path("resources/version"), "r", encoding="utf-8") as f: LocalVersion = f.readline().strip() # Functions def connectioncheck(): try: urlopen("http://www.example.com", timeout=5) return True except: return False def menu(): menu = Gtk.Menu() if notify(): command_upd = Gtk.MenuItem(label="Download Update") command_upd.connect("activate", showurl) menu.append(command_upd) menu.append(Gtk.SeparatorMenuItem()) commands = [ ("About althea", on_abtdlg), #("Settings", lambda x: openwindow(SettingsWindow)), ("Install AltStore", altstoreinstall), ("Install an IPA file", altserverfile), ("Pair", lambda x: openwindow(PairWindow)), ("Restart AltServer", restart_altserver), ("Quit althea", lambda x: quitit()) ] for label, callback in commands: command = Gtk.MenuItem(label=label) command.connect("activate", callback) menu.append(command) if label == "Settings": menu.append(Gtk.SeparatorMenuItem()) CheckRun11 = subprocess.run(f"test -e /usr/lib/althea/althea", shell=True) if installedcheck: global command_six CheckRun12 = subprocess.run( f"test -e $HOME/.config/autostart/althea.desktop", shell=True ) if CheckRun12.returncode == 0: command_six.set_active(command_six) command_six.connect("activate", launchatlogin1) menu.append(Gtk.SeparatorMenuItem()) menu.append(command_six) menu.show_all() return menu def on_abtdlg(self): about = Gtk.AboutDialog() width = 100 height = 100 pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( resource_path("resources/3.png"), width, height ) about.set_logo(pixbuf) about.set_program_name("althea") about.set_version("0.5.0") about.set_authors( [ "vyvir", "AltServer-Linux", "made by NyaMisty", "Provision", "made by Dadoum", ] ) # , 'Provision made by', 'Dadoum']) about.set_artists(["nebula"]) about.set_comments("A GUI for AltServer-Linux written in Python.") about.set_website("https://github.com/vyvir/althea") about.set_website_label("Github") about.set_copyright("GUI by vyvir") about.set_position(Gtk.WindowPosition.CENTER_ALWAYS) about.run() about.destroy() def paircheck(): # Check if the device is paired already pairchecking = subprocess.run('idevicepair pair | grep -q "SUCCESS"', shell=True) if pairchecking.returncode == 0: return False else: return True def altstoreinstall(_): if version.parse(ios_version()) < version.parse("15.0"): global Warnmsg Warnmsg = f"""\niOS {ios_version()} is not supported by AltStore.\nThe lowest supported version is iOS 15.0.\nYou can still continue, but errors may occur.\n""" ios_dialog = WarningDialog(parent=None) ios_dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) ios_response = ios_dialog.run() if ios_response == Gtk.ResponseType.OK: ios_dialog.destroy() if paircheck(): openwindow(PairWindow) else: win1() elif ios_response == Gtk.ResponseType.CANCEL: ios_dialog.destroy() else: if paircheck(): openwindow(PairWindow) else: win1() def altserverfile(_): if paircheck(): global login_or_file_chooser login_or_file_chooser = "file_chooser" openwindow(PairWindow) else: win2 = FileChooserWindow() global ipa_path_exists if ipa_path_exists == True: global PATH PATH = win2.PATHFILE win1() ipa_path_exists = False def notify(): if (connectioncheck()) == True: LatestVersion = ( urllib.request.urlopen( "https://raw.githubusercontent.com/vyvir/althea/main/resources/version" ) .readline() .rstrip() .decode() ) if LatestVersion > LocalVersion: Notify.init("MyProgram") n = Notify.Notification.new( "An update is available!", "Click 'Download Update' in the tray menu.", resource_path("resources/3.png"), ) n.set_timeout(Notify.EXPIRES_DEFAULT) # n.add_action("newupd", "Download", actionCallback) n.show() return True else: return False else: return False def showurl(_): Gtk.show_uri_on_window( None, "https://github.com/vyvir/althea/releases", Gdk.CURRENT_TIME ) quitit() def openwindow(window): w = window() w.show_all() def quitit(): subprocess.run(f"killall {AltServer}", shell=True) subprocess.run(f"killall {AnisetteServer}", shell=True) Gtk.main_quit() os.kill(os.getpid(), signal.SIGKILL) def restart_altserver(_): subprocess.run(f"killall {AltServer}", shell=True) subprocess.run(f"killall {AnisetteServer}", shell=True) subprocess.run("idevicepair pair", shell=True) subprocess.run( f"""{export_anisette} ; {(altheapath)}/AltServer &""", shell=True, ) def use_saved_credentials(): silent_remove(f"{(altheapath)}/log.txt") dialog = Gtk.MessageDialog( # transient_for=self, flags=0, message_type=Gtk.MessageType.QUESTION, buttons=Gtk.ButtonsType.YES_NO, text="Do you want to login automatically?", ) dialog.format_secondary_text("Your login and password have been saved earlier.") response = dialog.run() if response == Gtk.ResponseType.YES: global apple_id global password apple_id = keyring.get_password("althea", "apple_id") password = keyring.get_password("althea", "password") #print(apple_id, password) global savedcheck savedcheck = True Login().on_click_me_clicked1() else: apple_id = keyring.delete_password("althea", "apple_id") password = keyring.delete_password("althea", "password") win3 = Login() win3.show_all() dialog.destroy() def win1(): try: if keyring.get_password("althea", "apple_id"): use_saved_credentials() else: openwindow(Login) except keyring.errors.KeyringError: openwindow(Login) def win2(_): try: if keyring.get_password("althea", "apple_id"): use_saved_credentials() else: openwindow(Login) except keyring.errors.KeyringError: openwindow(Login) def actionCallback(notification, action, user_data=None): Gtk.show_uri_on_window( None, "https://github.com/vyvir/althea/releases", Gdk.CURRENT_TIME ) quitit() def launchatlogin1(_): global command_six if command_six.get_active(): global AutoStart os.popen(AutoStart).read() return True else: silent_remove("$HOME/.config/autostart/althea.desktop") return False def silent_remove(filename): try: os.remove(filename) except OSError as e: if e.errno != errno.ENOENT: # errno.ENOENT = no such file or directory raise # re-raise exception if a different error occurred def altstore_download(value): # setting the base URL value baseUrl = "https://cdn.altstore.io/file/altstore/apps.json" # retrieving data from JSON Data json_data = requests.get(baseUrl) if json_data.status_code == 200: data = json_data.json() for app in data['apps']: if app['name'] == "AltStore": if value == "Check": size = app['versions'][0]['size'] return size == os.path.getsize(f'{(altheapath)}/AltStore.ipa') break if value == "Download": latest = app['versions'][0]['downloadURL'] r = requests.get( latest, allow_redirects=True, ) latest_filename = latest.split('/')[-1] open(f"{(altheapath)}/{(latest_filename)}", "wb").write(r.content) os.rename(f"{(altheapath)}/{(latest_filename)}", f"{(altheapath)}/AltStore.ipa") subprocess.run(f"chmod 755 {(altheapath)}/AltStore.ipa", shell=True) break return True else: return False def ios_version(): silent_remove(f"{(altheapath)}/ideviceinfo.txt") subprocess.run(f"ideviceinfo > {(altheapath)}/ideviceinfo.txt", shell=True) result = "result" pathsy = f"{(altheapath)}/ideviceinfo.txt" with open(pathsy) as file: # Iterate through lines for line in file.readlines(): # Find the start of the word index = line.find("ProductVersion: ") # If the word is inside the line if index != -1: result = line[:-1][16:] silent_remove(f"{(altheapath)}/ideviceinfo.txt") print(result) return(result) # Classes class SplashScreen(Handy.Window): def __init__(self): super().__init__(title="Loading") self.set_resizable(False) self.set_default_size(512, 288) self.present() self.set_position(Gtk.WindowPosition.CENTER_ALWAYS) self.set_keep_above(True) self.mainBox = Gtk.Box( spacing=6, orientation=Gtk.Orientation.VERTICAL, halign=Gtk.Align.START, valign=Gtk.Align.START, ) self.add(self.mainBox) pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( filename=os.path.join("resources/4.png"), width=512, height=288, preserve_aspect_ratio=False, ) image = Gtk.Image.new_from_pixbuf(pixbuf) image.show() self.mainBox.pack_start(image, False, True, 0) self.lbl1 = Gtk.Label(label="Starting althea...") self.mainBox.pack_start(self.lbl1, False, False, 6) self.loadalthea = Gtk.ProgressBar() self.mainBox.pack_start(self.loadalthea, True, True, 0) self.t = threading.Thread(target=self.startup_process) self.t.start() self.wait_for_t(self.t) def wait_for_t(self, t): if not self.t.is_alive(): global indicator indicator.set_status(appindicator.IndicatorStatus.ACTIVE) self.t.join() self.destroy() else: GLib.timeout_add(200, self.wait_for_t, self.t) def download_bin(self, name, link): match computer_cpu_platform: case 'x86_64': r = requests.get( f"{link}-x86_64", allow_redirects=True, ) case 'aarch64': r = requests.get( f"{link}-aarch64", allow_redirects=True ) case _: if computer_cpu_platform.find('v7') != -1 \ or computer_cpu_platform.find('ARM') != -1 \ or computer_cpu_platform.find('hf') != -1: r = requests.get( f"{link}-armv7", allow_redirects=True ) else: self.lbl1.set_text('Could not identify the CPU architecture, downloading the x86_64 version...') r = requests.get( f"{link}-x86_64", allow_redirects=True, ) open(f"{(altheapath)}/{name}", "wb").write(r.content) subprocess.run(f"chmod +x {(altheapath)}/{name}", shell=True) subprocess.run(f"chmod 755 {(altheapath)}/{name}", shell=True) def startup_process(self): self.lbl1.set_text("Checking if anisette-server is already running...") self.loadalthea.set_fraction(0.1) command = 'curl 127.0.0.1:6969 | grep -q "{"' CheckRun = subprocess.run(command, shell=True) if not os.path.isfile(f"{(altheapath)}/anisette-server"): self.lbl1.set_text("Downloading anisette-server...") self.download_bin("anisette-server", "https://github.com/vyvir/althea/releases/download/v0.5.0/anisette-server") self.loadalthea.set_fraction(0.2) self.lbl1.set_text("Downloading Apple Music APK...") r = requests.get( "https://apps.mzstatic.com/content/android-apple-music-apk/applemusic.apk", allow_redirects=True, ) open(f"{(altheapath)}/am.apk", "wb").write(r.content) os.makedirs(f"{(altheapath)}/lib/x86_64") self.loadalthea.set_fraction(0.3) self.lbl1.set_text("Extracting necessary libraries...") CheckRunB = subprocess.run( f'unzip -j "{(altheapath)}/am.apk" "lib/x86_64/libstoreservicescore.so" -d "{(altheapath)}/lib/x86_64"', shell=True, ) CheckRunC = subprocess.run( f'unzip -j "{(altheapath)}/am.apk" "lib/x86_64/libCoreADI.so" -d "{(altheapath)}/lib/x86_64"', shell=True, ) silent_remove(f"{(altheapath)}/am.apk") self.loadalthea.set_fraction(0.4) self.lbl1.set_text("Starting anisette-server...") subprocess.run(f"{(altheapath)}/anisette-server -n 127.0.0.1 -p 6969 &", shell=True) #subprocess.run(f"cd {(altheapath)} && ./anisette-server &", shell=True)#-n 127.0.0.1 -p 6969 &", shell=True self.loadalthea.set_fraction(0.5) finished = False while not finished: CheckRun5 = subprocess.run(command, shell=True) if CheckRun5.returncode == 0: finished = True else: sleep(1) if not os.path.isfile(f"{(altheapath)}/AltServer"): self.download_bin("AltServer", "https://github.com/NyaMisty/AltServer-Linux/releases/download/v0.0.5/AltServer") self.lbl1.set_text("Downloading AltServer...") self.loadalthea.set_fraction(0.6) self.loadalthea.set_fraction(0.8) if not os.path.isfile(f"{(altheapath)}/AltStore.ipa"): self.lbl1.set_text("Downloading AltStore...") altstore_download("Download") else: self.lbl1.set_text("Checking latest AltStore version...") if not altstore_download("Check"): self.lbl1.set_text("Downloading new version of AltStore...") altstore_download("Download") self.lbl1.set_text("Starting AltServer...") self.loadalthea.set_fraction(1.0) subprocess.run(f"{export_anisette} ; {(altheapath)}/AltServer &", shell=True) return 0 class Login(Gtk.Window): def __init__(self): super().__init__(title="Login") self.present() self.set_position(Gtk.WindowPosition.CENTER_ALWAYS) self.set_resizable(False) self.set_border_width(10) grid = Gtk.Grid() self.add(grid) label = Gtk.Label(label="Apple ID: ") label.set_justify(Gtk.Justification.LEFT) self.entry1 = Gtk.Entry() label1 = Gtk.Label(label="Password: ") label1.set_justify(Gtk.Justification.LEFT) self.entry = Gtk.Entry() self.entry.set_visibility(False) global icon_name self.entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon_name) self.entry.connect("icon-press", self.on_icon_toggled) self.button = Gtk.Button.new_with_label("Login") self.button.connect("clicked", self.on_click_me_clicked) grid.add(label) grid.attach(self.entry1, 1, 0, 2, 1) grid.attach_next_to(label1, label, Gtk.PositionType.BOTTOM, 1, 2) grid.attach(self.entry, 1, 2, 1, 1) grid.attach_next_to(self.button, self.entry, Gtk.PositionType.RIGHT, 1, 1) silent_remove(f"{(altheapath)}/log.txt") def on_click_me_clicked1(self): self.realthread1 = threading.Thread(target=self.onclickmethread) self.realthread1.start() GLib.idle_add(self.install_process) def on_click_me_clicked(self, button): silent_remove(f"{(altheapath)}/log.txt") try: if not keyring.get_password("althea", "apple_id"): self.set_position(Gtk.WindowPosition.CENTER_ALWAYS) dialog = Gtk.MessageDialog( transient_for=self, flags=0, message_type=Gtk.MessageType.QUESTION, buttons=Gtk.ButtonsType.YES_NO, text="Do you want to save your login and password?", ) dialog.format_secondary_text("This will allow you to login automatically.") response = dialog.run() if response == Gtk.ResponseType.YES: apple_id = self.entry1.get_text().lower() password = self.entry.get_text() keyring.set_password("althea", "apple_id", apple_id) keyring.set_password("althea", "password", password) dialog.destroy() except keyring.errors.KeyringError: pass self.entry.set_progress_pulse_step(0.2) # Call self.do_pulse every 100 ms self.timeout_id = GLib.timeout_add(100, self.do_pulse, None) self.entry.set_editable(False) self.entry1.set_editable(False) self.button.set_sensitive(False) self.realthread1 = threading.Thread(target=self.onclickmethread) self.realthread1.start() GLib.idle_add(self.install_process) def onclickmethread(self): if ios_version() >= "15.0": global savedcheck global apple_id global password if not savedcheck: apple_id = self.entry1.get_text().lower() password = self.entry.get_text() UDID = subprocess.check_output("idevice_id -l", shell=True).decode().strip() global InsAltStore print(PATH) silent_remove(f"{(altheapath)}/log.txt") #f = open(f"{(altheapath)}/log.txt", "w") #f.close() if os.path.isdir(f'{ os.environ["HOME"] }/.adi'): rmtree(f'{ os.environ["HOME"] }/.adi') InsAltStoreCMD = f"""{export_anisette} ; {(AltServer)} -u {UDID} -a {apple_id} -p \"{password}\" {PATH} > {("$HOME/.local/share/althea/log.txt")}""" InsAltStore = subprocess.Popen( InsAltStoreCMD, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True, ) else: global Failmsg Failmsg = "iOS 15.0 or later is required." dialog2 = FailDialog(self) dialog2.run() dialog2.destroy() self.destroy() def install_process(self): Installing = True WarnTime = 0 TwoFactorTime = 0 global InsAltStore while Installing: CheckIns = subprocess.run( f'grep -F "Could not" {(altheapath)}/log.txt', shell=True ) CheckWarn = subprocess.run( f'grep -F "Are you sure you want to continue?" {(altheapath)}/log.txt', shell=True, ) CheckSuccess = subprocess.run( f'grep -F "Notify: Installation Succeeded" {(altheapath)}/log.txt', shell=True, ) Check2fa = subprocess.run( f'grep -F "Enter two factor code" {(altheapath)}/log.txt', shell=True ) if CheckIns.returncode == 0: InsAltStore.terminate() Installing = False global Failmsg Failmsg = subprocess.check_output( f"tail -6 {(altheapath)}/log.txt", shell=True ).decode() dialog2 = FailDialog(self) dialog2.run() dialog2.destroy() self.destroy() elif CheckWarn.returncode == 0 and WarnTime == 0: Installing = False word = "Are you sure you want to continue?" # This fixes an issue where the warn window appears when it shouldn't with open(f"{(altheapath)}/log.txt", "r") as file: # Read all content of the file content = file.read() # Check if a string present in the file if word in content: global Warnmsg Warnmsg = subprocess.check_output( f"tail -8 {('$HOME/.local/share/althea/log.txt')}", shell=True, ).decode() dialog1 = WarningDialog(self) response1 = dialog1.run() if response1 == Gtk.ResponseType.OK: dialog1.destroy() InsAltStore.communicate(input=b"\n") WarnTime = 1 Installing = True elif response1 == Gtk.ResponseType.CANCEL: dialog1.destroy() os.system(f"pkill -TERM -P {InsAltStore.pid}") self.cancel() else: WarnTime = 1 Installing = True elif Check2fa.returncode == 0 and TwoFactorTime == 0: Installing = False dialog = VerificationDialog(self) response = dialog.run() if response == Gtk.ResponseType.OK: vercode = dialog.entry2.get_text() vercode = vercode + "\n" vercodebytes = bytes(vercode.encode()) InsAltStore.communicate(input=vercodebytes) TwoFactorTime = 1 dialog.destroy() Installing = True elif response == Gtk.ResponseType.CANCEL: TwoFactorTime = 1 os.system(f"pkill -TERM -P {InsAltStore.pid}") self.cancel() dialog.destroy() self.destroy() elif CheckSuccess.returncode == 0: Installing = False self.success() self.destroy() def success(self): dialog = Gtk.MessageDialog( transient_for=self, flags=0, message_type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.OK, text="Success!", ) dialog.format_secondary_text("Operation completed") dialog.run() dialog.destroy() def cancel(self): self.set_position(Gtk.WindowPosition.CENTER_ALWAYS) dialog = Gtk.MessageDialog( transient_for=self, flags=0, message_type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.OK, text="Cancelled", ) dialog.format_secondary_text("Operation cancelled by user") dialog.run() dialog.destroy() def do_pulse(self, user_data): self.entry.progress_pulse() return True def on_icon_toggled(self, widget, icon, event): global icon_name if icon_name == "changes-prevent-symbolic": icon_name = "changes-allow-symbolic" self.entry.set_visibility(True) elif icon_name == "changes-allow-symbolic": icon_name = "changes-prevent-symbolic" self.entry.set_visibility(False) self.entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon_name) # #def on_editable_toggled(self, widget): # print("lol") class PairWindow(Handy.Window): def __init__(self): super().__init__(title="Pair your device") self.present() self.set_position(Gtk.WindowPosition.CENTER_ALWAYS) self.set_resizable(False) self.set_border_width(20) self.handle = Handy.WindowHandle() self.add(self.handle) self.hbox = Gtk.Box(spacing=5, orientation=Gtk.Orientation.VERTICAL) self.handle.add(self.hbox) self.hb = Handy.HeaderBar() self.hb.set_show_close_button(True) self.hb.props.title = "Pair your device" self.hbox.pack_start(self.hb, False, True, 0) pixbuf = Gtk.IconTheme.get_default().load_icon( "phone-apple-iphone-symbolic", 48, 0 ) image = Gtk.Image.new_from_pixbuf(pixbuf) image.show() image.set_margin_top(5) self.hbox.pack_start(image, True, True, 0) lbl1 = Gtk.Label( label="Please make sure your device is connected to the computer.\nPress 'Pair' to pair your device." ) lbl1.set_property("margin_left", 15) lbl1.set_property("margin_right", 15) lbl1.set_margin_top(5) lbl1.set_justify(Gtk.Justification.CENTER) self.hbox.pack_start(lbl1, False, False, 0) button = Gtk.Button(label="Pair") button.connect("clicked", self.on_info_clicked) button.set_property("margin_left", 150) button.set_property("margin_right", 150) self.hbox.pack_start(button, False, False, 10) self.add(button) self.add(self.hbox) def on_info_clicked(self, widget): try: subprocess.run(["idevicepair pair"], shell=True, check=True) except subprocess.CalledProcessError as e: print(e.output) dialog = Gtk.MessageDialog( transient_for=self, flags=0, message_type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.OK, text="Accept the trust dialog on the screen of your device,\nthen press 'OK'.", ) dialog.run() try: subprocess.run( ["idevicepair pair"], shell=True, check=True, capture_output=True ) self.destroy() global login_or_file_chooser global PATH if login_or_file_chooser == "file_chooser": win2 = FileChooserWindow() else: PATH = f"{(altheapath)}/AltStore.ipa" win1() global ipa_path_exists if ipa_path_exists == True: PATH = win2.PATHFILE win1() ipa_path_exists = False login_or_file_chooser = "login" except subprocess.CalledProcessError as e: errormsg = e.output.decode("utf-8") dialog1 = Gtk.MessageDialog( transient_for=self, flags=0, message_type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK, text=(errormsg), ) dialog1.run() dialog1.destroy() try: dialog.destroy() except: pass class FileChooserWindow(Gtk.Window): def __init__(self): super().__init__(title="File chooser") box = Gtk.Box() self.add(box) dialog = Gtk.FileChooserDialog( title="Please choose a file", parent=self, action=Gtk.FileChooserAction.OPEN ) dialog.add_buttons( Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK, ) self.add_filters(dialog) response = dialog.run() if response == Gtk.ResponseType.OK: self.PATHFILE = dialog.get_filename() global ipa_path_exists ipa_path_exists = True elif response == Gtk.ResponseType.CANCEL: self.destroy() dialog.destroy() def add_filters(self, dialog): filter_ipa = Gtk.FileFilter() filter_ipa.set_name("IPA files") filter_ipa.add_pattern("*.ipa") dialog.add_filter(filter_ipa) filter_any = Gtk.FileFilter() filter_any.set_name("Any files") filter_any.add_pattern("*") dialog.add_filter(filter_any) class VerificationDialog(Gtk.Dialog): def __init__(self, parent): if not savedcheck: super().__init__(title="Verification code", transient_for=parent, flags=0) else: super().__init__(title="Verification code", flags=0) self.present() self.add_buttons( Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK, ) self.set_resizable(True) self.set_border_width(10) labelhelp = Gtk.Label( label="Enter the verification \ncode on your device: " ) labelhelp.set_justify(Gtk.Justification.CENTER) self.entry2 = Gtk.Entry() box = self.get_content_area() box.add(labelhelp) box.add(self.entry2) self.show_all() class WarningDialog(Gtk.Dialog): def __init__(self, parent): global Warnmsg super().__init__(title="Warning", transient_for=parent, flags=0) self.present() self.add_buttons( Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK ) self.set_resizable(False) self.set_border_width(10) labelhelp = Gtk.Label(label="Are you sure you want to continue?") labelhelp.set_justify(Gtk.Justification.CENTER) labelhelp1 = Gtk.Label(label=Warnmsg) labelhelp1.set_justify(Gtk.Justification.CENTER) labelhelp1.set_line_wrap(True) labelhelp1.set_max_width_chars(48) labelhelp1.set_selectable(True) box = self.get_content_area() box.add(labelhelp) box.add(labelhelp1) self.show_all() class FailDialog(Gtk.Dialog): def __init__(self, parent): global Failmsg super().__init__(title="Fail", transient_for=parent, flags=0) self.present() self.add_buttons(Gtk.STOCK_OK, Gtk.ResponseType.OK) self.set_resizable(False) self.set_border_width(10) labelhelp = Gtk.Label(label="AltServer has failed.") labelhelp.set_justify(Gtk.Justification.CENTER) labelhelp1 = Gtk.Label(label=Failmsg) labelhelp1.set_justify(Gtk.Justification.CENTER) labelhelp1.set_line_wrap(True) labelhelp1.set_max_width_chars(48) labelhelp1.set_selectable(True) box = self.get_content_area() box.add(labelhelp) box.add(labelhelp1) self.show_all() class Oops(Handy.Window): def __init__(self, markup_text, pixbuf_icon): super().__init__(title="Error") self.present() self.set_position(Gtk.WindowPosition.CENTER_ALWAYS) self.set_resizable(False) self.set_size_request(450, 100) self.set_border_width(10) # WindowHandle handle = Handy.WindowHandle() self.add(handle) box = Gtk.VBox() vb = Gtk.VBox(spacing=0, orientation=Gtk.Orientation.VERTICAL) # Headerbar self.hb = Handy.HeaderBar() self.hb.set_show_close_button(True) self.hb.props.title = "Error" vb.pack_start(self.hb, False, True, 0) pixbuf = Gtk.IconTheme.get_default().load_icon( pixbuf_icon, 48, 0 ) image = Gtk.Image.new_from_pixbuf(pixbuf) image.show() image.set_margin_top(10) vb.pack_start(image, True, True, 0) lbl1 = Gtk.Label() lbl1.set_justify(Gtk.Justification.CENTER) lbl1.set_markup(markup_text) lbl1.set_property("margin_left", 15) lbl1.set_property("margin_right", 15) lbl1.set_margin_top(10) button = Gtk.Button(label="OK") button.set_property("margin_left", 125) button.set_property("margin_right", 125) button.connect("clicked", self.on_info_clicked2) handle.add(vb) vb.pack_start(lbl1, expand=False, fill=True, padding=0) vb.pack_start(button, False, False, 10) box.add(vb) self.add(box) self.show_all() def on_info_clicked2(self, widget): quitit() class SettingsWindow(Handy.Window): def __init__(self): super().__init__(title="Settings") self.present() self.set_position(Gtk.WindowPosition.CENTER_ALWAYS) self.set_resizable(False) #self.set_size_request(450, 100) self.set_border_width(10) # WindowHandle handle = Handy.WindowHandle() self.add(handle) box = Gtk.VBox() vb = Gtk.VBox(spacing=0, orientation=Gtk.Orientation.VERTICAL) # Headerbar self.hb = Handy.HeaderBar() self.hb.set_show_close_button(True) self.hb.props.title = "Settings" vb.pack_start(self.hb, False, True, 0) pixbuf = Gtk.IconTheme.get_default().load_icon( "emblem-system-symbolic", 48, 0 ) image = Gtk.Image.new_from_pixbuf(pixbuf) image.show() image.set_margin_top(10) vb.pack_start(image, True, True, 0) lbl1 = Gtk.Label() lbl1.set_justify(Gtk.Justification.CENTER) lbl1.set_markup( "These settings are experimental. Use at own risk." ) lbl1.set_property("margin_left", 15) lbl1.set_property("margin_right", 15) lbl1.set_margin_top(10) button = Gtk.Button(label="OK") button.set_property("margin_left", 125) button.set_property("margin_right", 125) button.connect("clicked", self.on_info_clicked2) button1 = Gtk.Button(label="Open PairWindow") button1.set_property("margin_left", 125) button1.set_property("margin_right", 125) button1.connect("clicked", self.on_info_clicked3) handle.add(vb) vb.pack_start(lbl1, expand=False, fill=True, padding=0) vb.pack_start(button, False, False, 10) vb.pack_start(button1, False, False, 10) box.add(vb) self.add(box) self.show_all() def on_info_clicked2(self, widget): self.destroy() def on_info_clicked3(self, widget): openwindow(PairWindow) # ----------------------------------------------------------------------------- # Main function def main(): GLib.set_prgname("althea") # Sets the global program name global altheapath #global file_name if not os.path.exists(altheapath): # Creates $HOME/.local/share/althea os.mkdir(altheapath) if Gtk.StatusIcon.is_embedded: if connectioncheck(): global indicator indicator = appindicator.Indicator.new( "althea-tray-icon", resource_path("resources/1.png"), appindicator.IndicatorCategory.APPLICATION_STATUS, ) indicator.set_status(appindicator.IndicatorStatus.ACTIVE) indicator.set_menu(menu()) indicator.set_status(appindicator.IndicatorStatus.PASSIVE) openwindow(SplashScreen) else: markup_text = "althea is unable to connect to the Internet.\nPlease connect to the Internet and restart althea." pixbuf_icon = "network-wireless-no-route-symbolic" Oops(markup_text, pixbuf_icon) # Notify the user there is no Internet connection else: markup_text = ( "You don't have the AppIndicator extension installed.\n" 'You can download it on GNOME Extensions.' ) pixbuf_icon = "application-x-addon-symbolic" Oops(markup_text, pixbuf_icon) # Notify the user the tray icons aren't installed Handy.init() Gtk.main() # Call main if __name__ == "__main__": main()