Has a running app been used lately?

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.