Главная > Apple, Coding, Творчество > HexLauncher: быстрый запуск приложений в Mac OS X

HexLauncher: быстрый запуск приложений в Mac OS X

История создания данного продукта возвращает меня на 5 лет назад во времена, когда я только начал входить в мир Apple технологий, а именно — мигрировать на операционную систему Mac OS X. Как и для многих пенреход был связан с некоторыми неудобставми из-за отсутствия привычных программ и элементов управления системой. Одним из недоразуменей стало отсутствие аналога кнопки Пуск для запуска программ путем выбора из списка. Честно говоря, до сих пор не уверен, что я правильно запускаю программы в OS X (для этого я использую Spotlight). И вот, спустя несколько лет я решил устранить этот недостаток.

Надо сказать, я неоднократно пытался найти готовое решение данной проблемы. Но все, что я находил по той или иной причине не устраивало меня. И это в очередной раз привело меня к сакральной истине: если хочешь что-то сделать, сделай это сам. Да будет так!
Вооружившись Xcode’ом и поисковыми средствами интернета я приступил к разработке. Кстати, забегая немного вперед, скачать HexLauncher можно здесь.
Тех задание было следующим. Требуется разработать приложение, которое при запуске помещало бы в системное меню иконку, при нажатии на которую появлялось бы меню со списком избранных приложений, выбрав одно из которых можно было осуществить быстрый запуск. Данный способ гораздо более удобен с точки зрения юзабилити, нежели Spotlight, хотя бы из тех соображений, что при использовании Spotlight нужно использовать и мышь и клавиатуру. HexLauncher же позволяет запускать приложение в два клика. Список приложений в меню формируется с помощью окна настроек, вызов которого так же доступен из меню программы. При открытии окна настроек в него загружаются иконки и названия всех приложений, находящихся в папке /Applications — это главное хранилище приложений в платформе Mac OS X. Пользователь может отметить галками желаемые приложения, и они будут отображаться в выпадающем меню. Так же необходимо предоставить пользователю возможность запуска программы при старте системы. HexLauncher screenshot Вот такое незамысловатое ТЗ. Что ж, приступим.
Мало-мальски сложным в данном проекте для меня стали несколько вещей.
1. Собственно, поместить иконку в системное меню Mac OS X и ассоциировать с ним меню. Не буду долго теоретизировать, просто приведу кусок кода, который родился после средней продолжительности гугления.

- (void)setupMenu {
    NSMenu *menu = [[NSMenu alloc] init];
    //    for (NSString * app in self.apps) {
    NSMenuItem * item = [menu addItemWithTitle:@"HexLauncher Preferences" action:@selector(setup:) keyEquivalent:@""];
    NSImage *iconImage = [[NSWorkspace sharedWorkspace] iconForFile:[[NSBundle mainBundle] bundlePath]];
 
    item.image = iconImage;
    [menu addItem:[NSMenuItem separatorItem]];
    int count = 0;
    for (int i = 0; i < self.apps.count; i++) {
        AppItem * app = self.apps[i];
        if (!app.checked)
            continue;
        count++;
        NSMenuItem * item = [menu addItemWithTitle:[app.appTitle substringWithRange:NSMakeRange(0, app.appTitle.length - 4)] action:@selector(launchApp:) keyEquivalent:@""];
        item.tag = i;
        item.image = app.icon;
    }
    if (count)
        [menu addItem:[NSMenuItem separatorItem]];
    [menu addItemWithTitle:@"Quit HexLauncher" action:@selector(terminate:) keyEquivalent:@""];
 
    self.statusItem.menu = menu;
}
 
 
- (void)setupStatusItem {
    _statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
    _statusItem.title = @"";
    _statusItem.image = [NSImage imageNamed:@"StatusItem-Image-rocket"];
    _statusItem.alternateImage = [NSImage imageNamed:@"StatusItem-Image-rocket-alt"];
    _statusItem.highlightMode = YES;
 
    [self gatherAppsList];
    [self setupMenu];
}

Думаю, тут все понятно. systemMenu — объект класса NSStatusItem, он собственно и делает всю магию с иконкой в системном меню. Метод gatherAppsList сканирует рекурсивно директорию /Applications и выдает массив, из которого и формируется меню.
2. Следующим сложным моментом стало отображение в списке приложений иконок. Еще когда я создавал свое первое приложение для OS X (MacMines) я столкнулся с этим хитрым форматом icns (IconSet), который содержит сет из иконок разного разрешения. Очень долго искал, как можно с ним работать, и как, грубо говоря, конвертировать ICNS в NSImage. Из чего-то более-менее вразумительного попался какой-то opensource-проект написанный на c++. К счастью, удачный поисковый запрос выдал мне гениально-простое решение и избавил тем самым от возни с сорцами на плюсах. Оказывается, в Cocoa-фреймворке существуют стредства для получения готового изображения для любого приложения. Вот как это выглядит.

NSImage *iconImage = [[NSWorkspace sharedWorkspace] iconForFile:appPath];

где appPath, как нетрудно догадаться, путь к приложению (например, /Applications/Skype.app)
3. Запуск приложения Mac OS X из другого приложения так же не оказался чем-то криминальным (у меня были опасения, что возможно будут какие-то трудности связанные с безопасностью)

NSWorkspace *sharedWorkspace = [NSWorkspace sharedWorkspace];
BOOL didLaunch = [sharedWorkspace launchApplication:app.appPath];

4. Интересной окалась задача показать окно настроек поверх других — то есть, вынести на самый верх окно программы.

- (void)setup:(id)sender {
    [self.mainWindowController.window makeKeyAndOrderFront:self];
    [NSApp activateIgnoringOtherApps:YES];
}

5. Самой, пожалуй, сложной для реализации стала функциональность запуска приложения при старте (Launch app on start up). Самый распространенный способ, используемый большинством прикладных приложений, является так называемые Launch agents. Подробно об агентах запуска в Mac OS X можно ознакомиться здесь. Я лишь расскажу в кратце, как это действует. Агент запуска создается с помощью обычного Plist, содержащего информацию о запускаемом приложении, а так же о правилах запуска (это такой продвинутый cron-планировщик по сути). Вот, как выглядит агент запуска для моего приложения

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>ru.heximal.HexLauncher</string>
    <key>ProgramArguments</key>
    <array>
        <string>%APP_DIR%/Contents/MacOS/HexLauncher</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>

Думаю, комментарии тут излишни. Скажу лишь, что %APP_DIR% — это placeholder. Этот файл лежит в бандле проекта, и плэйсхолдер %APP_DIR% подменяется строкой, которая содержит путь к папке, где лежит само приложение HexLauncher. Вот, кстати, как получить путь в папке Mac OS X приложения:

NSBundle *myBundle = [NSBundle mainBundle];
NSString * bundlePath = [myBundle bundlePath];

Также про агентов запуска можно добавить следующее. Они могут находиться либо в домашней директории пользоватля

~/Library/LaunchAgents/

либо в глобальной библиотеке

/Library/LaunchAgents/

Как не трудно догадаться, в первом случае агент будет работать только для того самого пользователя, а во втором — для любого пользователя. Вот, пожалуй, и все на сегодня.

  1. wb77
    7 апреля 2015 в 01:09 | #1

    То есть это, по сути, список любимых и самых важных программ?
    А чем плох док?

  2. wb77
    7 апреля 2015 в 01:15 | #2

    И еще такая мысль пришла: запустить все свои приложения, которыми пользуешься в рабочей среде, нужно только раз — затем мак просто засыпает, и просыпается на той же ноте..)
    Да, утечки существуют, но они накапливаются месяцами…

  3. 15 апреля 2015 в 13:13 | #3

    Привет. Да, почти док) но док тоже немного мозг выносит, не информативен за счет того, что непонятно, запущено в данный момент приложение или нет. ну то есть, конечно понятно, там ведь рядом точечка есть, если приложение активно. но бесит, когда случайно тыкаешь иконку в доке, которую не хотел, и при этом запускается какой-то нежелательный апп. особенно, если этот апп тяжелый. поэтому я предпочитаю по минимуму в доке закрепленных аппов держать, в большинстве только те, которые почти всегда запущены) вот для всего остального этот чудесный лончер)
    Есть еще launchpad — но тоже как-то я не проникся им.

    сори за долгий тормоз, после переезда на новый серв как-то почта нестабильно ходит)

  4. 27 августа 2015 в 17:58 | #4

    Здравствуйте!

    Скачал HexLauncher и у меня возникла проблема.
    На странице скачивания написано Requires Mac OS X 10.8 or later
    Во время установки на 10.9.5 (хак) вышло сообщение, что требуется OS X 10.10
    и никак иначе…

    P.S.
    Спасибо за скринсэйвер «матрица» — обожаю его! Вот бы такой под макось (тамошний верик не такой красивший, как Ваш))

  5. 28 августа 2015 в 18:25 | #5

    Здравствуйте.
    упс, действительно, косяк, забыл выставить минимальный таргет в настройках проекта, в ближайшее время пересоберу и отпишусь сюда)
    Спасибо за отзыв, очень приятно.
    Матрица у меня была портирована под мак, и даже работала на 10.5 (Leopard).
    А в следующей версии 10.6 (Lion) Apple ужесточила политику безопасности относительно экранных заставок сторонних производителей, и у меня не хватило терпения разобраться, как это победить)
    Может когда-нить дойдут и до этого руки)

  6. 22 сентября 2015 в 23:04 | #6

    Ещё раз здравствуйте! ))
    Я прошу прощения за свою назойливость — просто хочу узнать, Вы ещё не пересобрали HexLauncher? Или вдруг по прошествии (почти) месяца запамятовали…
    Мы как бы ждём-с :-)

  7. 23 сентября 2015 в 01:30 | #7

    Мильон извинений, дела-дела, суматошное девелоперское бытиё…
    Исправляюсь.
    Выложил билд 1.1
    http://heximal.ru/download/hexlauncher/HexLauncher_1.1.dmg

    Буду премного признателен за тестирование на OS X 10.8
    Собираюсь опубликовать в качестве первого mac-приложение в Mac AppStore

  8. 26 сентября 2015 в 12:15 | #8

    Отлично, спасибо!
    У меня самого в распоряжении есть только 10.6.8 и 10.9.6. — однако, мой знакомый с 10.8 уже при деле ))
    Будем тестить.
    Если что найдём, я Вам непременно напишу.

  9. 26 сентября 2015 в 13:24 | #9

    Итак, сейчас ситуация такая.

    После запуска на осях 10.8.5 и 10.9.5 лаунчер появляется в процессах, но иконки его сверху нет (не подаёт признаков жизни).
    В 10.11.1 GM запустился успешно, заработал.
    На других версиях OS X мы пока не можем проверить приложение.

Подписаться на комментарии по RSS