Is there a way to tell how long it has been since a running app has been used or perhaps frontmost?
I assume you’re looking for something less clunky that an always-open script that periodically polls running apps and updates a timestamp for which was frontmost each time it polls.
- Tom.
Not with vanilla AppleScript.
It is possible with a (faceless) Cocoa / AppleScriptObjC app which is able to receive workspace notifications like NSWorkspaceDidActivateApplication
and NSWorkspaceDidDeactivateApplication
and collect the data in a database.
This is a very lightweight solution. Any polling to get the changes is too expensive.
It may be too resource intensive, but I wrote it the Applescript way anyway, because that’s what I know how to do.
I had the “on idle” handler repeat every 5 seconds, so if someone flips to an app for less than that, it could be missed. You can adjust that based on how important it is to not miss brief uses, weighed against resource usage.
This logs the data:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
property appList : {}
property timeList : {}
idle
on idle
set appPath to the path to the frontmost application as text
set {delimitHolder, AppleScript's text item delimiters} to {AppleScript's text item delimiters, ":"}
set frontApp to text item -2 of appPath
set AppleScript's text item delimiters to delimitHolder
set frontApp to text 1 thru ((offset of "." in frontApp) - 1) of frontApp
set theTime to the current date
if frontApp is in appList then
set appListPosition to get_index_in_list(appList, frontApp)
set item appListPosition of timeList to theTime
else
set appList to appList & frontApp
set timeList to timeList & theTime
end if
set {delimitHolder, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "|"}
write_data_file((appList as text) & return & timeList as text)
set AppleScript's text item delimiters to delimitHolder
return 5
end idle
on get_index_in_list(fullList, indexItem)
set theIndex to 0
repeat with itemNumber from 1 to count of fullList
if (item itemNumber of fullList) is indexItem then return itemNumber
end repeat
return false
end get_index_in_list
on write_data_file(dataToWrite)
-- Set folder path to save file, in a temp folder in the user folder.
set userPath to ((path to home folder) as text)
set textFileDirectory to userPath & "tmp"
set textFilePath to (textFileDirectory & ":last_used_app_data.txt")
tell application "Finder"
if not (exists folder textFileDirectory) then
make new folder at userPath with properties {name:"tmp"}
end if
end tell
-- Just in case this script previosuly errored before close, so the OS thinks the file is still opened by Applescript
try
close access file textFilePath
end try
try
set fileReference to open for access file (textFilePath) with write permission
set eof fileReference to 0 --Clears the previous text in the file
write dataToWrite to fileReference
close access fileReference
on error
display dialog "Unable to write App time data to temp file."
end try
end write_data_file
And this retrieves it:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
set fileData to read ((((path to home folder) as text) & "tmp:last_used_app_data.txt") as alias)
set {delimitHolder, AppleScript's text item delimiters} to {AppleScript's text item delimiters, return}
set appList to the first text item of fileData
set timeList to the last text item of fileData
set AppleScript's text item delimiters to "|"
set appList to the text items of appList
set timeList to the text items of timeList
set AppleScript's text item delimiters to delimitHolder
if appList is {} then
display dialog "This application hasn't gathered enough data to use yet."
else
choose from list appList with title "Last Used Reporter" with prompt "Choose an application to see when it was last frontmost."
set appChoice to item 1 of the result
set usedTime to item (get_index_in_list(appList, appChoice)) of timeList
display dialog "The application " & appChoice & " was most recently the front application " & return & usedTime as text
end if
on get_index_in_list(fullList, indexItem)
set theIndex to 0
repeat with itemNumber from 1 to count of fullList
if (item itemNumber of fullList) is indexItem then return itemNumber
end repeat
return false
end get_index_in_list
Depending on your use case, maybe that’s not a good UI for retrieving it. But I didn’t know what your intentions were.
I watched Activity Monitor for a while with it running, and while I’m sure it’s not as efficient as ASObjC would be, it didn’t exactly bring my system to a crawl either.
- Tom.
Thank you all. As I suspected, polling intensively in vanilla AppleScript is not the way to go. Watching Activity Monitor for a while also shows that unused open apps don’t consume resources except the memory they occupy which means that the system knows what’s in use and not.