Merge develop into main branch (#137)

* SdCard HAL refactored (#135)

- Refactor SdCard HAL
- introduce Lockable

* Screenshot and FatFS improvements (#136)

- Fix screenshots on ESP32
- Improve Screenshot service
- Convert Screenshot app to class-based instead of structs
- Screenshot app now automatically updates when task is finished
- Enable FatFS long filename support

* Re-use common log messages (#138)

For consistency and binary size reduction

* Toolbar spinner should get margin to the right

* More TactilityC features (#139)

* Rewrote Loader

- Simplified Loader by removing custom threa
- Created DispatcherThread
- Move auto-starting apps to Boot app
- Fixed Dispatcher bug where it could get stuck not processing new
messages

* Hide AP settings if the AP is not saved

* Missing from previous commit

* Replace LV_EVENT_CLICKED with LV_EVENT_SHORT_CLICKED

* Refactored files app and created InputDialog (#140)

- Changed Files app so that it has a View and State
- Files app now allows for long-pressing on files to perform actions
- Files app now has rename and delete actions
- Created InputDialog app
- Improved AlertDialog app layout
This commit is contained in:
Ken Van Hoeylandt
2024-12-27 22:12:39 +00:00
committed by GitHub
parent 9033daa6dd
commit 50bd6e8bf6
144 changed files with 3244 additions and 2038 deletions
+66 -64
View File
@@ -1,97 +1,99 @@
#include "FileUtils.h"
#include "TactilityCore.h"
#include <cstdlib>
#include <cstring>
#include <bits/stdc++.h>
namespace tt::app::files {
#define TAG "file_utils"
#define SCANDIR_LIMIT 128
std::string getChildPath(const std::string& basePath, const std::string& childPath) {
// Postfix with "/" when the current path isn't "/"
if (basePath.length() != 1) {
return basePath + "/" + childPath;
} else {
return "/" + childPath;
}
}
int dirent_filter_dot_entries(const struct dirent* entry) {
return (strcmp(entry->d_name, "..") == 0 || strcmp(entry->d_name, ".") == 0) ? -1 : 0;
}
int dirent_sort_alpha_and_type(const struct dirent** left, const struct dirent** right) {
bool left_is_dir = (*left)->d_type == TT_DT_DIR || (*left)->d_type == TT_DT_CHR;
bool right_is_dir = (*right)->d_type == TT_DT_DIR || (*right)->d_type == TT_DT_CHR;
bool dirent_sort_alpha_and_type(const struct dirent& left, const struct dirent& right) {
bool left_is_dir = left.d_type == TT_DT_DIR || left.d_type == TT_DT_CHR;
bool right_is_dir = right.d_type == TT_DT_DIR || right.d_type == TT_DT_CHR;
if (left_is_dir == right_is_dir) {
return strcmp((*left)->d_name, (*right)->d_name);
return strcmp(left.d_name, right.d_name) < 0;
} else {
return (left_is_dir < right_is_dir) ? 1 : -1;
return left_is_dir > right_is_dir;
}
}
int dirent_sort_alpha(const struct dirent** left, const struct dirent** right) {
return strcmp((*left)->d_name, (*right)->d_name);
}
int scandir(
const char* path,
struct dirent*** output,
ScandirFilter _Nullable filter,
ScandirSort _Nullable sort
const std::string& path,
std::vector<dirent>& outList,
ScandirFilter _Nullable filterMethod,
ScandirSort _Nullable sortMethod
) {
DIR* dir = opendir(path);
DIR* dir = opendir(path.c_str());
if (dir == nullptr) {
TT_LOG_E(TAG, "Failed to open dir %s", path);
TT_LOG_E(TAG, "Failed to open dir %s", path.c_str());
return -1;
}
*output = static_cast<dirent**>(malloc(sizeof(void*) * SCANDIR_LIMIT));
if (*output == nullptr) {
TT_LOG_E(TAG, "Out of memory");
closedir(dir);
return -1;
}
struct dirent** dirent_array = *output;
int next_dirent_index = 0;
struct dirent* current_entry;
bool out_of_memory = false;
while ((current_entry = readdir(dir)) != nullptr) {
if (filter(current_entry) == 0) {
dirent_array[next_dirent_index] = static_cast<dirent*>(malloc(sizeof(struct dirent)));
if (dirent_array[next_dirent_index] != nullptr) {
memcpy(dirent_array[next_dirent_index], current_entry, sizeof(struct dirent));
next_dirent_index++;
if (next_dirent_index >= SCANDIR_LIMIT) {
TT_LOG_E(TAG, "Directory has more than %d files", SCANDIR_LIMIT);
break;
}
} else {
TT_LOG_E(TAG, "Alloc failed. Aborting and cleaning up.");
out_of_memory = true;
break;
}
}
}
// Out-of-memory clean-up
if (out_of_memory && next_dirent_index > 0) {
for (int i = 0; i < next_dirent_index; ++i) {
TT_LOG_I(TAG, "Cleanup item %d", i);
free(dirent_array[i]);
}
TT_LOG_I(TAG, "Free");
free(*output);
closedir(dir);
return -1;
// Empty directory
} else if (next_dirent_index == 0) {
free(*output);
*output = nullptr;
} else {
if (sort) {
qsort(dirent_array, next_dirent_index, sizeof(struct dirent*), (__compar_fn_t)sort);
if (filterMethod(current_entry) == 0) {
outList.push_back(*current_entry);
}
}
closedir(dir);
return next_dirent_index;
if (sortMethod != nullptr) {
sort(outList.begin(), outList.end(), sortMethod);
}
return (int)outList.size();
};
bool isSupportedExecutableFile(const std::string& filename) {
#ifdef ESP_PLATFORM
// Currently only the PNG library is built into Tactility
return filename.ends_with(".elf");
#else
return false;
#endif
}
template <typename T>
std::basic_string<T> lowercase(const std::basic_string<T>& input) {
std::basic_string<T> output = input;
std::transform(
output.begin(),
output.end(),
output.begin(),
[](const T character) { return static_cast<T>(std::tolower(character)); }
);
return std::move(output);
}
bool isSupportedImageFile(const std::string& filename) {
// Currently only the PNG library is built into Tactility
return lowercase(filename).ends_with(".png");
}
bool isSupportedTextFile(const std::string& filename) {
std::string filename_lower = lowercase(filename);
return filename_lower.ends_with(".txt") ||
filename_lower.ends_with(".ini") ||
filename_lower.ends_with(".json") ||
filename_lower.ends_with(".yaml") ||
filename_lower.ends_with(".yml") ||
filename_lower.ends_with(".lua") ||
filename_lower.ends_with(".js") ||
filename_lower.ends_with(".properties");
}
}