mirror of
https://github.com/zoriya/ags.git
synced 2026-06-03 19:01:12 +00:00
monitor and cache number of app launches
This commit is contained in:
+2
-2
@@ -180,8 +180,8 @@ export default class App extends Gtk.Application {
|
||||
});
|
||||
|
||||
this.emit('config-parsed');
|
||||
} catch (error) {
|
||||
logError(error as Error);
|
||||
} catch (_) {
|
||||
print(`No config found at ${App.configPath}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+101
-55
@@ -1,79 +1,121 @@
|
||||
import Gio from 'gi://Gio';
|
||||
import Service from './service.js';
|
||||
import GObject from 'gi://GObject';
|
||||
import { CACHE_DIR, ensureDirectory, readFile, writeFile } from '../utils.js';
|
||||
|
||||
interface App {
|
||||
app: Gio.DesktopAppInfo
|
||||
name: string
|
||||
desktop: string | null
|
||||
description: string | null
|
||||
wmClass: string | null
|
||||
executable: string
|
||||
iconName: string
|
||||
launch: () => void
|
||||
match: (term: string) => boolean
|
||||
const APPS_CACHE_DIR = `${CACHE_DIR}/apps`;
|
||||
const CACHE_FILE = APPS_CACHE_DIR + '/apps_frequency.json';
|
||||
|
||||
class Application extends GObject.Object {
|
||||
static { GObject.registerClass(this); }
|
||||
|
||||
app: Gio.DesktopAppInfo;
|
||||
frequency: number;
|
||||
name: string;
|
||||
desktop: string | null;
|
||||
description: string | null;
|
||||
wmClass: string | null;
|
||||
executable: string;
|
||||
iconName: string;
|
||||
service: ApplicationsService;
|
||||
|
||||
constructor(app: Gio.DesktopAppInfo, service: ApplicationsService) {
|
||||
super();
|
||||
this.service = service;
|
||||
this.app = app;
|
||||
this.name = app.get_name();
|
||||
this.desktop = app.get_id();
|
||||
this.executable = app.get_executable();
|
||||
this.description = app.get_description();
|
||||
this.iconName = this._iconName(app);
|
||||
this.wmClass = app.get_startup_wm_class();
|
||||
this.frequency = this.desktop && service.frequents[this.desktop] || 0;
|
||||
}
|
||||
|
||||
_iconName(app: any): string {
|
||||
if (!app.get_icon())
|
||||
return '';
|
||||
|
||||
if (typeof app.get_icon()?.get_names !== 'function')
|
||||
return '';
|
||||
|
||||
const name = app.get_icon()?.get_names()[0];
|
||||
return name || '';
|
||||
}
|
||||
|
||||
_match(prop: string | null, search: string) {
|
||||
if (!prop)
|
||||
return false;
|
||||
|
||||
if (!search)
|
||||
return true;
|
||||
|
||||
return prop?.toLowerCase().includes(search.toLowerCase());
|
||||
}
|
||||
|
||||
match(term: string) {
|
||||
const { name, desktop, description, executable } = this;
|
||||
return this._match(name, term) ||
|
||||
this._match(desktop, term) ||
|
||||
this._match(executable, term) ||
|
||||
this._match(description, term);
|
||||
}
|
||||
|
||||
launch() {
|
||||
this.frequency++;
|
||||
this.service._launched(this.desktop);
|
||||
this.app.launch([], null);
|
||||
}
|
||||
}
|
||||
|
||||
function _appIconName(app: any): string {
|
||||
if (!app.get_icon())
|
||||
return '';
|
||||
|
||||
if (typeof app.get_icon()?.get_names !== 'function')
|
||||
return '';
|
||||
|
||||
const name = app.get_icon()?.get_names()[0];
|
||||
return name || '';
|
||||
}
|
||||
|
||||
function _match(prop: string | null, search: string): boolean {
|
||||
if (!prop)
|
||||
return false;
|
||||
|
||||
if (!search)
|
||||
return true;
|
||||
|
||||
return prop?.toLowerCase().includes(search.toLowerCase());
|
||||
}
|
||||
|
||||
function _search(app: App, search: string): boolean {
|
||||
const { name, desktop, description, executable } = app;
|
||||
return _match(name, search) ||
|
||||
_match(desktop, search) ||
|
||||
_match(executable, search) ||
|
||||
_match(description, search);
|
||||
}
|
||||
|
||||
const _wrapper = (app: Gio.DesktopAppInfo): App => ({
|
||||
app,
|
||||
name: app.get_name(),
|
||||
desktop: app.get_id(),
|
||||
executable: app.get_executable(),
|
||||
description: app.get_description(),
|
||||
iconName: _appIconName(app),
|
||||
wmClass: app.get_startup_wm_class(),
|
||||
launch: () => app.launch([], null),
|
||||
match: (term: string) => _search(_wrapper(app), term),
|
||||
});
|
||||
|
||||
class ApplicationsService extends Service {
|
||||
static { Service.register(this); }
|
||||
private _list!: App[];
|
||||
static {
|
||||
Service.register(this, {
|
||||
'launched': ['string'],
|
||||
});
|
||||
}
|
||||
|
||||
private _list!: Application[];
|
||||
frequents: { [app: string]: number };
|
||||
|
||||
query(term: string) {
|
||||
return this._list.filter(app => _search(app, term));
|
||||
return this._list.filter(app => app.match(term)).sort((a, b) => {
|
||||
return a.frequency < b.frequency ? 1 : 0;
|
||||
});
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
Gio.AppInfoMonitor.get().connect('changed', this._sync.bind(this));
|
||||
|
||||
try {
|
||||
this.frequents = JSON.parse(readFile(CACHE_FILE)) as { [app: string]: number };
|
||||
} catch (_) {
|
||||
this.frequents = {};
|
||||
}
|
||||
|
||||
this._sync();
|
||||
}
|
||||
|
||||
_launched(id: string | null) {
|
||||
if (!id)
|
||||
return;
|
||||
|
||||
typeof this.frequents[id] === 'number'
|
||||
? this.frequents[id] += 1
|
||||
: this.frequents[id] = 1;
|
||||
|
||||
ensureDirectory(APPS_CACHE_DIR);
|
||||
const json = JSON.stringify(this.frequents, null, 2);
|
||||
writeFile(json, CACHE_FILE).catch(logError);
|
||||
}
|
||||
|
||||
_sync() {
|
||||
this._list = Gio.AppInfo.get_all()
|
||||
.filter(app => app.should_show())
|
||||
.map(app => Gio.DesktopAppInfo.new(app.get_id() || ''))
|
||||
.filter(app => app)
|
||||
.map(app => _wrapper(app));
|
||||
.map(app => new Application(app, this));
|
||||
|
||||
this.emit('changed');
|
||||
}
|
||||
@@ -88,6 +130,10 @@ export default class Applications {
|
||||
return Applications._instance;
|
||||
}
|
||||
|
||||
static frequents() {
|
||||
return Applications.instance.frequents;
|
||||
}
|
||||
|
||||
static query(term: string) {
|
||||
return Applications.instance.query(term);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ import Service from './service.js';
|
||||
import { ensureDirectory, timeout } from '../utils.js';
|
||||
import { MprisPlayerProxy, MprisProxy, TMprisProxy, TPlayerProxy, MprisMetadata } from '../dbus/mpris.js';
|
||||
import { DBusProxy, TDBusProxy } from '../dbus/dbus.js';
|
||||
import { MEDIA_CACHE_PATH } from '../utils.js';
|
||||
import { CACHE_DIR } from '../utils.js';
|
||||
|
||||
const MEDIA_CACHE_PATH = `${CACHE_DIR}/media`;
|
||||
|
||||
type PlaybackStatus = 'Playing' | 'Paused' | 'Stopped';
|
||||
type LoopStatus = 'None' | 'Track' | 'Playlist';
|
||||
@@ -144,8 +146,7 @@ class MprisPlayer extends GObject.Object {
|
||||
if (GLib.file_test(coverPath, GLib.FileTest.EXISTS))
|
||||
return;
|
||||
|
||||
ensureDirectory();
|
||||
|
||||
ensureDirectory(MEDIA_CACHE_PATH);
|
||||
Gio.File.new_for_uri(trackCoverUrl).copy_async(
|
||||
Gio.File.new_for_path(coverPath),
|
||||
Gio.FileCopyFlags.OVERWRITE,
|
||||
|
||||
@@ -4,7 +4,10 @@ import GLib from 'gi://GLib';
|
||||
import Service from './service.js';
|
||||
import App from '../app.js';
|
||||
import { NotificationIFace } from '../dbus/notifications.js';
|
||||
import { NOTIFICATIONS_CACHE_PATH, ensureDirectory, readFileAsync, timeout, writeFile } from '../utils.js';
|
||||
import { CACHE_DIR, ensureDirectory, readFileAsync, timeout, writeFile } from '../utils.js';
|
||||
|
||||
const NOTIFICATIONS_CACHE_PATH = `${CACHE_DIR}/notifications`;
|
||||
const CACHE_FILE = NOTIFICATIONS_CACHE_PATH + '/notifications.json';
|
||||
|
||||
interface action {
|
||||
id: string
|
||||
@@ -183,8 +186,7 @@ class NotificationsService extends Service {
|
||||
|
||||
async _readFromFile() {
|
||||
try {
|
||||
const path = NOTIFICATIONS_CACHE_PATH + '/notifications.json';
|
||||
const file = await readFileAsync(path);
|
||||
const file = await readFileAsync(CACHE_FILE);
|
||||
const notifications = JSON.parse(file as string) as Notification[];
|
||||
notifications.forEach(n => {
|
||||
if (n.id > this._idCount)
|
||||
@@ -207,7 +209,7 @@ class NotificationsService extends Service {
|
||||
if (!image_data)
|
||||
return null;
|
||||
|
||||
ensureDirectory();
|
||||
ensureDirectory(NOTIFICATIONS_CACHE_PATH);
|
||||
const fileName = NOTIFICATIONS_CACHE_PATH + '/' + name.replace(/[^a-zA-Z0-9]/g, '');
|
||||
const image = image_data.recursiveUnpack();
|
||||
const pixbuf = GdkPixbuf.Pixbuf.new_from_bytes(
|
||||
@@ -237,8 +239,8 @@ class NotificationsService extends Service {
|
||||
notifications.push(n);
|
||||
}
|
||||
|
||||
ensureDirectory();
|
||||
writeFile(JSON.stringify(notifications, null, 2), NOTIFICATIONS_CACHE_PATH + '/notifications.json').catch();
|
||||
ensureDirectory(NOTIFICATIONS_CACHE_PATH);
|
||||
writeFile(JSON.stringify(notifications, null, 2), CACHE_FILE).catch(logError);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-14
@@ -5,8 +5,6 @@ import GObject from 'gi://GObject';
|
||||
|
||||
export const USER = GLib.get_user_name();
|
||||
export const CACHE_DIR = `${GLib.get_user_cache_dir()}/${pkg.name}`;
|
||||
export const MEDIA_CACHE_PATH = `${CACHE_DIR}/media`;
|
||||
export const NOTIFICATIONS_CACHE_PATH = `${CACHE_DIR}/notifications`;
|
||||
|
||||
export function readFile(path: string) {
|
||||
const f = Gio.File.new_for_path(path);
|
||||
@@ -129,19 +127,8 @@ export function lookUpIcon(name?: string, size = 16) {
|
||||
}
|
||||
|
||||
export function ensureDirectory(path?: string) {
|
||||
if (path && !GLib.file_test(path, GLib.FileTest.EXISTS)) {
|
||||
if (path && !GLib.file_test(path, GLib.FileTest.EXISTS))
|
||||
Gio.File.new_for_path(path).make_directory_with_parents(null);
|
||||
}
|
||||
else {
|
||||
[
|
||||
MEDIA_CACHE_PATH,
|
||||
NOTIFICATIONS_CACHE_PATH,
|
||||
]
|
||||
.forEach(path => {
|
||||
if (!GLib.file_test(path, GLib.FileTest.EXISTS))
|
||||
Gio.File.new_for_path(path).make_directory_with_parents(null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function execAsync(cmd: string | string[]): Promise<string> {
|
||||
|
||||
Reference in New Issue
Block a user