Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -514,33 +514,46 @@ object PluginManager {
suspend fun ___DO_NOT_CALL_FROM_A_PLUGIN_loadAllLocalPlugins(context: Context, forceReload: Boolean) {
assertNonRecursiveCallstack()

// Use app-specific files directory and copy the file there.
// We have to do this because on Android 14+, it otherwise gives SecurityException
// due to dex files and setReadOnly seems to have no effect unless it is here.
val pluginDirectory = File(context.filesDir, "plugins")
if (!pluginDirectory.exists()) {
pluginDirectory.mkdirs() // Ensure the plugins directory exists
}

// Make sure all local plugins are fully refreshed.
removeKey(PLUGINS_KEY_LOCAL)

val dir = File(LOCAL_PLUGINS_PATH)

if (!dir.exists()) {
val res = dir.mkdirs()
if (!res) {
Log.w(TAG, "Failed to create local directories")
return
}
Log.d(TAG, "No local plugins folder found at '${LOCAL_PLUGINS_PATH}'")
// Clean up all existing plugins since the local plugins folder doesn't exist
// This is required since the user might stop using local plugins but still have old
// plugins lying around taking up space.
// These cannot be deleted manually by the user without root access
pluginDirectory.listFiles()?.forEach { it.delete() }

// Since there are no local plugins, we can consider them loaded
loadedLocalPlugins = true
return
}

val sortedPlugins = dir.listFiles()
// Always sort plugins alphabetically for reproducible results

val sortedPlugins = dir.listFiles()?.sortedBy { it.name }
Log.d(TAG, "Files in '${LOCAL_PLUGINS_PATH}' folder: ${sortedPlugins?.size}")

// Use app-specific external files directory and copy the file there.
// We have to do this because on Android 14+, it otherwise gives SecurityException
// due to dex files and setReadOnly seems to have no effect unless it it here.
val pluginDirectory = File(context.getExternalFilesDir(null), "plugins")
if (!pluginDirectory.exists()) {
pluginDirectory.mkdirs() // Ensure the plugins directory exists
val sortedPluginNames = sortedPlugins?.mapTo(HashSet()) { it.name }
pluginDirectory.listFiles()?.sortedBy { it.name }?.amap {
// If the plugin doesn't exist in the local plugins folder, delete it
if (!it.isDirectory && sortedPluginNames?.contains(it.name) == false) {
Log.d(TAG, "Deleting removed local plugin: ${it.name}")
it.delete()
}
}

// Make sure all local plugins are fully refreshed.
removeKey(PLUGINS_KEY_LOCAL)

sortedPlugins?.sortedBy { it.name }?.amap { file ->
sortedPlugins?.amap { file ->
try {
val destinationFile = File(pluginDirectory, file.name)

Expand Down