Skip to content

Commit

Permalink
android widget: add log.txt and error_log.txt + add notification perm…
Browse files Browse the repository at this point in the history
…ission for showing toasts

This is (probably) the last commit for this year, since i'll be away until January 2nd.

Thank you so much for all the support for customfetch. I wish you an Happy New Year and hope everything goes well.
I hope that the next year, when i'll release 0.11.0 with the android app and support, customfetch will be even more popular than now.
Surely android ricers will love this feature.

Thank you again and see you next year. Bye Bye :)
  • Loading branch information
Toni500github committed Dec 27, 2024
1 parent 95d25ba commit ca7cc0c
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 40 deletions.
1 change: 1 addition & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<application
android:allowBackup="true"
Expand Down
15 changes: 3 additions & 12 deletions android/app/src/main/cpp/customfetch_android.cpp
Original file line number Diff line number Diff line change
@@ -1,28 +1,19 @@
#include <jni.h>

#include <string>
#include <sstream>

#include "query.hpp"
#include "util.hpp"

#define ARRAY_SIZE(x) sizeof(x) / sizeof(x[0])

void stringToCharArray(const std::vector<std::string>& tokens, char *argv[]) {
// Allocate memory for each word
for (size_t i = 0; i < tokens.size(); ++i) {
argv[i] = strdup(tokens[i].c_str());
}
//argv[size] = nullptr; // Null-terminate
}

extern std::string mainAndroid_and_render(int argc, char *argv[], JNIEnv *env, jobject obj);

extern "C" JNIEXPORT jstring JNICALL
Java_org_toni_customfetch_1android_widget_CustomfetchMainRender_mainAndroid(JNIEnv *env, jobject obj, jstring args) {
const std::string& str_args = env->GetStringUTFChars(args, nullptr);
const std::vector<std::string>& tokens = split(str_args, ' ');
char *argv[tokens.size()];
stringToCharArray(tokens, argv);
for (size_t i = 0; i < tokens.size(); ++i)
argv[i] = strdup(tokens[i].c_str());

return env->NewStringUTF(mainAndroid_and_render(ARRAY_SIZE(argv), argv, env, obj).c_str());
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.text.Editable
import android.text.SpannableStringBuilder
import android.text.Spanned
Expand All @@ -21,6 +23,7 @@ import android.widget.EditText
import android.widget.LinearLayout
import android.widget.RadioGroup
import android.widget.TextView
import android.widget.Toast
import androidx.core.graphics.toColorInt
import androidx.core.text.HtmlCompat
import com.skydoves.colorpickerview.ColorPickerView
Expand All @@ -29,6 +32,9 @@ import com.skydoves.colorpickerview.sliders.AlphaSlideBar
import com.skydoves.colorpickerview.sliders.BrightnessSlideBar
import org.toni.customfetch_android.R
import org.toni.customfetch_android.databinding.CustomfetchConfigureBinding
import java.io.File
import java.nio.file.Files
import kotlin.io.path.Path


/**
Expand Down Expand Up @@ -203,7 +209,23 @@ class CustomfetchMainRender {
getArgsPref(context, appWidgetId)
}

val errorFile = "/storage/emulated/0/.config/customfetch/error_log.txt"
val errorLock = "/storage/emulated/0/.config/customfetch/error.lock"
val htmlContent = mainAndroid("customfetch $arguments")
if (Files.exists(Path(errorLock))) {
val file = File(errorLock)
val error = file.bufferedReader().use { it.readText() }
val handler = Handler(Looper.getMainLooper())
handler.post {
Toast.makeText(context, error, Toast.LENGTH_LONG).show()
}
handler.post {
Toast.makeText(context, "read error logs at $errorFile", Toast.LENGTH_LONG).show()
}
file.delete()
parsedContent.append("read error logs at $errorFile\n\n$error")
return parsedContent
}

if (disableLineWrap) {
val eachLine = htmlContent!!.split("<br>").map { it.trim() }
Expand Down
59 changes: 41 additions & 18 deletions include/util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,16 @@
#include <dlfcn.h>
#include <sys/types.h>

#include <cstdlib>
#include <iostream>
#include <chrono>
#include <string>
#include <vector>

#include "fmt/chrono.h"
#include "fmt/color.h"
#include "fmt/base.h"
#include "fmt/os.h"
#include "platform.hpp"

// clang-format off
Expand Down Expand Up @@ -291,7 +295,6 @@ std::string get_android_property(const std::string_view name);
#endif

#if !ANDROID_APP

template <typename... Args>
void error(const std::string_view fmt, Args&&... args) noexcept
{
Expand Down Expand Up @@ -339,56 +342,76 @@ inline struct jni_objects
jobject obj;
} jni_objs;

static constexpr const char *APPNAME = "customfetch_android";

template <typename... Args>
static void nativeLog(JNIEnv *env, jobject obj, int log_level, const std::string_view fmt, Args&&... args)
static void nativeAndFileLog(JNIEnv *env, int log_level, const std::string_view fmt, Args&&... args)
{
jstring jMessage = env->NewStringUTF(fmt::format(fmt::runtime(fmt), args...).c_str());
const std::string& fmt_str = fmt::format(fmt::runtime(fmt), args...);
jstring jMessage = env->NewStringUTF(fmt_str.c_str());
const char *cMessage = env->GetStringUTFChars(jMessage, nullptr);

// Use Android's native logging
__android_log_print(log_level, APPNAME, "%s", cMessage);
__android_log_print(log_level, "customfetch_android", "%s", cMessage);

env->ReleaseStringUTFChars(jMessage, cMessage);

auto f = fmt::output_file(getConfigDir() + "/log.txt", fmt::file::CREATE | fmt::file::APPEND | fmt::file::WRONLY);
auto now = std::chrono::system_clock::now();
f.print("[{:%H:%M:%S}] ", now);
switch(log_level)
{
case ANDROID_LOG_FATAL: f.print("FATAL: {}\n", fmt_str); break;
case ANDROID_LOG_ERROR: f.print("ERROR: {}\n", fmt_str); break;
case ANDROID_LOG_WARN: f.print("WARNING: {}\n", fmt_str); break;
case ANDROID_LOG_INFO: f.print("INFO: {}\n", fmt_str); break;
//case ANDROID_LOG_DEBUG: f.print("[DEBUG]: {}\n", fmt_str); break;
}
}

env->ReleaseStringUTFChars(jMessage, cMessage); // Clean up
template <typename... Args>
static void writeToErrorLog(const bool fatal, const std::string_view fmt, Args&&... args)
{
const std::string& fmt_str = fmt::format(fmt::runtime(fmt), args...);
const std::string_view title = fatal ? "FATAL" : "ERROR";
auto f = fmt::output_file(getConfigDir() + "/error_log.txt", fmt::file::CREATE | fmt::file::APPEND | fmt::file::RDWR);
auto lock = fmt::output_file(getConfigDir() + "/error.lock");
auto now = std::chrono::system_clock::now();
f.print("[{:%H:%M:%S}] {}: {}\n", now, title, fmt_str);
lock.print("{}: {}\n", title, fmt_str);
}

template <typename... Args>
void error(const std::string_view fmt, Args&&... args) noexcept
{
const std::string& fmt_str = fmt::format(fmt::runtime(fmt), std::forward<Args>(args)...);
nativeLog(jni_objs.env, jni_objs.obj, ANDROID_LOG_ERROR, "{}", fmt_str);
nativeAndFileLog(jni_objs.env, ANDROID_LOG_ERROR, fmt, std::forward<Args>(args)...);
writeToErrorLog(false, fmt, std::forward<Args>(args)...);
}

template <typename... Args>
void die(const std::string_view fmt, Args&&... args) noexcept
{
const std::string& fmt_str = fmt::format(fmt::runtime(fmt), std::forward<Args>(args)...);
nativeLog(jni_objs.env, jni_objs.obj, ANDROID_LOG_FATAL, "{}", fmt_str);
abort();
nativeAndFileLog(jni_objs.env, ANDROID_LOG_FATAL, fmt, std::forward<Args>(args)...);
writeToErrorLog(true, fmt, std::forward<Args>(args)...);
//exit(1);
}

template <typename... Args>
void debug(const std::string_view fmt, Args&&... args) noexcept
{
#if DEBUG
const std::string& fmt_str = fmt::format(fmt::runtime(fmt), std::forward<Args>(args)...);
nativeLog(jni_objs.env, jni_objs.obj, ANDROID_LOG_DEBUG, "{}", fmt_str);
nativeAndFileLog(jni_objs.env, ANDROID_LOG_DEBUG, fmt, std::forward<Args>(args)...);
#endif
}

template <typename... Args>
void warn(const std::string_view fmt, Args&&... args) noexcept
{
const std::string& fmt_str = fmt::format(fmt::runtime(fmt), std::forward<Args>(args)...);
nativeLog(jni_objs.env, jni_objs.obj, ANDROID_LOG_WARN, "{}", fmt_str);
nativeAndFileLog(jni_objs.env, ANDROID_LOG_WARN, fmt, std::forward<Args>(args)...);
}

template <typename... Args>
void info(const std::string_view fmt, Args&&... args) noexcept
{
const std::string& fmt_str = fmt::format(fmt::runtime(fmt), std::forward<Args>(args)...);
nativeLog(jni_objs.env, jni_objs.obj, ANDROID_LOG_INFO, "{}", fmt_str);
nativeAndFileLog(jni_objs.env, ANDROID_LOG_INFO, fmt, std::forward<Args>(args)...);
}

#endif // !ANDROID_APP
Expand Down
15 changes: 7 additions & 8 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,11 @@ void Config::loadConfigFile(const std::string_view filename, colors_t& colors)
}
catch (const toml::parse_error& err)
{
error("Parsing config file {} failed:", filename);
std::cerr << err << std::endl;
exit(-1);
die("Parsing config file '{}' failed:\n"
"{}\n"
"\t(error occurred at line {} column {})",
filename, err.description(),
err.source().begin.line, err.source().begin.column);
}

// clang-format off
Expand Down Expand Up @@ -146,12 +148,9 @@ std::vector<std::string> Config::getValueArrayStr(const std::string_view
[&ret, value](auto&& el)
{
if (const toml::value<std::string>* str_elem = el.as_string())
{
const toml::value<std::string>& v = *str_elem;
ret.push_back(v->data());
}
ret.push_back((*str_elem)->data());
else
warn("An element of the {} array variable is not a string", value);
warn("An element of the '{}' array variable is not a string", value);
}
);
}
Expand Down
2 changes: 2 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,8 @@ int main(int argc, char *argv[])
// then let's make it always true
config.gui = true;
config.wrap_lines = true;

// reset option index
optind = 0;
#endif

Expand Down
4 changes: 2 additions & 2 deletions src/parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ static std::array<std::string, 3> get_ansi_color(const std::string_view str, con
{
const size_t first_m = str.rfind('m');
if (first_m == std::string::npos)
die("Parser: failed to parse layout/ascii art: missing m while using ANSI color escape code");
die("Parser: failed to parse layout/ascii art: missing 'm' while using ANSI color escape code in '{}'", str);

std::string col = str.data();
col.erase(first_m); // 1;42
Expand Down Expand Up @@ -147,7 +147,7 @@ static std::array<std::string, 3> get_ansi_color(const std::string_view str, con
const int n = std::stoi(col);
if ((n >= 100 && n <= 107) || (n >= 40 && n <= 47))
type = "background-color";
#endif
#endif // !ANDROID_APP

// last number
// clang-format off
Expand Down

0 comments on commit ca7cc0c

Please sign in to comment.