Skip to content

Commit

Permalink
[+] 1) support saving config in the android filesystem. 2) support bu…
Browse files Browse the repository at this point in the history
…ilding desktop. 3) cache news for restart displaying
  • Loading branch information
Heng30 committed Jan 15, 2024
1 parent 47d68f4 commit 383bd9b
Show file tree
Hide file tree
Showing 12 changed files with 560 additions and 29 deletions.
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ build-android-lib:
build-android-app: build-android-lib
cd ./cpnews && ./gradlew build

install: build-android-app
build-desktop-app:
$(build-evn) $(run-evn) cargo build --bin cpnews --features=desktop --release

install-debug: build-android-app
cd ./cpnews && ./gradlew installDebug

install-release: build-android-app
Expand Down
1 change: 0 additions & 1 deletion build.help
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,4 @@ cargo install cargo-ndk
cargo ndk -t arm64-v8a -o app/src/main/jniLibs/ build
./gradlew build
./gradlew installDebug
adb shell am start -n co.realfit.naegui/.MainActivity
```
1 change: 1 addition & 0 deletions cpnews/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ egui-winit = { version = "0.22", default-features = false, features = [ "android
[target.'cfg(not(target_os = "android"))'.dependencies]
reqwest = { version = "0.11", features = ["json", "blocking"]}
env_logger = "0.10"
platform-dirs = "0.3"

[target.'cfg(target_os = "android")'.dependencies]
reqwest = { version = "0.11", features = ["rustls-tls", "native-tls-vendored", "json", "blocking"]}
Expand Down
4 changes: 2 additions & 2 deletions cpnews/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ android {
compileSdk 31

defaultConfig {
applicationId "co.realfit.naegui"
applicationId "xyz.heng30.cpnews"
minSdk 28
targetSdk 31
versionCode 1
Expand Down Expand Up @@ -50,7 +50,7 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
namespace 'co.realfit.naegui'
namespace 'xyz.heng30.cpnews'
}

dependencies {
Expand Down
2 changes: 2 additions & 0 deletions cpnews/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
</activity>
</application>

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />

</manifest>
Expand Down
10 changes: 5 additions & 5 deletions cpnews/src/about.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,33 @@ pub fn ui(app: &mut App, ui: &mut Ui) {
Button::image_and_text(
app.back_icon.clone().unwrap().id(),
theme::BACK_ICON_SIZE,
RichText::new(tr(app.is_cn, "关于"))
RichText::new(tr(app.conf.ui.is_cn, "关于"))
.font(FontId::proportional(theme::NEWS_TITLE_FONT_SIZE)),
)
.frame(false),
)
.clicked()
{
app.currency_panel = CurrentPanel::News;
app.current_panel = CurrentPanel::News;
}

ui.vertical_centered(|ui| {
let title = format!("{} {}", tr(app.is_cn, "加密新闻"), version::VERSION);
let title = format!("{} {}", tr(app.conf.ui.is_cn, "加密新闻"), version::VERSION);
let address = "0xf1199999751b1a3A74590adBf95401D19AB30014";
let etherscan = "https://etherscan.io/address/";

ui.add_space(theme::SPACING * 4.);
ui.heading(title);
ui.add_space(theme::SPACING);

if app.is_cn {
if app.conf.ui.is_cn {
ui.label("基于egui。版权2022-2030 Heng30公司有限公司,保留所有权利。该程序按原样提供,不提供任何形式的保证,包括设计,适销性和特定用途的保证。");
} else {
ui.label("Based on egui. Copyright 2022-2030 The Heng30 Company Ltd. All rights reserved. The program is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.");
}

ui.add_space(theme::SPACING * 2.);
if app.is_cn {
if app.conf.ui.is_cn {
ui.label("🎉❤给我买一杯咖啡(MetaMask)❤🎉");
} else {
ui.label("🎉❤Buy Me a Coffee(MetaMask)❤🎉");
Expand Down
52 changes: 35 additions & 17 deletions cpnews/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::{
about::{self, About},
config::Config,
news,
news::NewsItem,
theme,
Expand Down Expand Up @@ -58,13 +59,14 @@ enum ChannelItem {

#[derive(Clone)]
pub struct App {
pub is_cn: bool,
pub is_fetching: bool,
pub is_scroll_to_top: bool,
pub news_items_cn: Vec<NewsItem>,
pub news_items_en: Vec<NewsItem>,

pub currency_panel: CurrentPanel,
pub current_panel: CurrentPanel,
pub conf: Config,

pub about_panel: About,
msg_spec: MsgSpec,

Expand All @@ -83,14 +85,14 @@ impl Default for App {
let (tx, rx) = mpsc::sync_channel(10);

Self {
is_cn: true,
is_fetching: false,
is_scroll_to_top: false,
news_items_cn: vec![],
news_items_en: vec![],

currency_panel: Default::default(),
current_panel: Default::default(),
msg_spec: Default::default(),
conf: Default::default(),

about_panel: Default::default(),

Expand All @@ -108,6 +110,12 @@ impl Default for App {

impl App {
pub fn init(&mut self, ctx: &Context) {
if let Err(e) = self.conf.init() {
log::warn!("{e:?}");
}

(self.news_items_cn, self.news_items_en) = news::load(self.conf.cache_dir.as_path());

self.fetch_data();

self.brand_icon = Some(ctx.load_texture(
Expand Down Expand Up @@ -143,7 +151,7 @@ impl App {

pub fn ui(&mut self, ctx: &Context) {
egui::CentralPanel::default().show(ctx, |ui| {
match self.currency_panel {
match self.current_panel {
CurrentPanel::News => {
self.header(ui);
self.news_list(ui);
Expand All @@ -158,10 +166,15 @@ impl App {
}

fn header(&mut self, ui: &mut Ui) {
// let res = std::fs::metadata("/data/data/xyz.heng30.cpnews/data/cache/news-en.json");
// ui.label(format!("{:?}", res));

ui.horizontal(|ui| {
ui.with_layout(Layout::left_to_right(Align::Center), |ui| {
ui.image(&self.brand_icon.clone().unwrap(), theme::ICON_SIZE);
ui.heading(RichText::new(tr(self.is_cn, "加密新闻")).color(theme::BRAND_COLOR));
ui.heading(
RichText::new(tr(self.conf.ui.is_cn, "加密新闻")).color(theme::BRAND_COLOR),
);
});

// double-clicked-area to scroll to top
Expand Down Expand Up @@ -190,7 +203,7 @@ impl App {
)
.clicked()
{
self.currency_panel = CurrentPanel::About;
self.current_panel = CurrentPanel::About;
}

if ui
Expand All @@ -203,11 +216,14 @@ impl App {
)
.clicked()
{
self.is_cn = !self.is_cn;
self.conf.ui.is_cn = !self.conf.ui.is_cn;
if let Err(e) = self.conf.save() {
log::warn!("{e:?}");
}

// fetch data only without news cache
if (self.is_cn && self.news_items_cn.is_empty())
|| (!self.is_cn && self.news_items_en.is_empty())
if (self.conf.ui.is_cn && self.news_items_cn.is_empty())
|| (!self.conf.ui.is_cn && self.news_items_en.is_empty())
{
self.fetch_data();
}
Expand All @@ -225,7 +241,8 @@ impl App {

if self.is_fetching {
ui.label(
RichText::new(tr(self.is_cn, "正在刷新")).color(theme::NEWS_TITLE_COLOR),
RichText::new(tr(self.conf.ui.is_cn, "正在刷新"))
.color(theme::NEWS_TITLE_COLOR),
);
}
});
Expand All @@ -237,13 +254,13 @@ impl App {
fn news_list(&mut self, ui: &mut Ui) {
let row_height = ui.spacing().interact_size.y;

let num_rows = if self.is_cn {
let num_rows = if self.conf.ui.is_cn {
self.news_items_cn.len()
} else {
self.news_items_en.len()
};

let news_items = if self.is_cn {
let news_items = if self.conf.ui.is_cn {
&self.news_items_cn
} else {
&self.news_items_en
Expand Down Expand Up @@ -288,7 +305,7 @@ impl App {
if !item.link.is_empty() {
ui.add_space(theme::SPACING);

ui.hyperlink_to(tr(self.is_cn, "原文链接"), &item.link);
ui.hyperlink_to(tr(self.conf.ui.is_cn, "原文链接"), &item.link);
}
});

Expand Down Expand Up @@ -324,13 +341,14 @@ impl App {

self.is_fetching = true;
let tx = self.tx.clone();
let is_cn = self.is_cn;
let is_cn = self.conf.ui.is_cn;
let cache_dir = self.conf.cache_dir.clone();

std::thread::spawn(move || {
match if is_cn {
news::fetch_odaily()
news::fetch_odaily(cache_dir.join("news-cn.json").as_path())
} else {
news::fetch_cryptocompare()
news::fetch_cryptocompare(cache_dir.join("news-en.json").as_path())
} {
Err(e) => {
let _ = tx.try_send(ChannelItem::ErrMsg(e.to_string()));
Expand Down
113 changes: 113 additions & 0 deletions cpnews/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use anyhow::{anyhow, Result};
use std::{env, fs};

#[allow(unused_imports)]
use std::path::PathBuf;

#[cfg(not(target_os = "android"))]
use platform_dirs::AppDirs;

#[cfg(target_os = "android")]
pub struct AppDirs {
pub config_dir: PathBuf,
pub data_dir: PathBuf,
}

#[cfg(target_os = "android")]
impl AppDirs {
pub fn new(name: Option<&str>, _: bool) -> Option<Self> {
let root_dir = "/data/data";
let name = name.unwrap();

Some(Self {
config_dir: PathBuf::from(&format!("{root_dir}/{name}/config")),
data_dir: PathBuf::from(&format!("{root_dir}/{name}/data")),
})
}
}

#[derive(Serialize, Deserialize, Default, Debug, Clone)]
pub struct Config {
#[serde(skip)]
pub working_dir: PathBuf,

#[serde(skip)]
pub config_path: PathBuf,

#[serde(skip)]
pub db_path: PathBuf,

#[serde(skip)]
pub cache_dir: PathBuf,

pub ui: UI,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct UI {
pub is_cn: bool,
}

impl Default for UI {
fn default() -> Self {
Self { is_cn: true }
}
}

impl Config {
pub fn init(&mut self) -> Result<()> {
let app_name = if cfg!(not(target_os = "android")) {
"cpnews"
} else {
"xyz.heng30.cpnews"
};

let app_dirs = AppDirs::new(Some(app_name), true).unwrap();

self.init_app_dir(&app_dirs)?;
self.load()?;
log::debug!("{:?}", self);
Ok(())
}

fn init_app_dir(&mut self, app_dirs: &AppDirs) -> Result<()> {
self.config_path = app_dirs.config_dir.join("cpnews.conf");
self.db_path = app_dirs.data_dir.join("cpnews.db");

self.cache_dir = app_dirs.data_dir.join("cache");
self.working_dir = {
let mut dir = env::current_exe()?;
dir.pop();
dir
};

fs::create_dir_all(&app_dirs.config_dir)?;
fs::create_dir_all(&app_dirs.data_dir)?;
fs::create_dir_all(&self.cache_dir)?;

Ok(())
}

fn load(&mut self) -> Result<()> {
match fs::read_to_string(&self.config_path) {
Ok(text) => match serde_json::from_str::<Config>(&text) {
Ok(c) => {
self.ui = c.ui;
Ok(())
}
Err(e) => Err(anyhow!("{e:?}")),
},
Err(_) => match serde_json::to_string_pretty(self) {
Ok(text) => Ok(fs::write(&self.config_path, text)?),
Err(e) => Err(anyhow!("{e:?}")),
},
}
}

pub fn save(&self) -> Result<()> {
match serde_json::to_string_pretty(self) {
Ok(text) => Ok(fs::write(&self.config_path, text)?),
Err(e) => Err(anyhow!("{e:?}")),
}
}
}
1 change: 1 addition & 0 deletions cpnews/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ mod theme;
mod tr;
mod util;
mod version;
mod config;

use app::App;

Expand Down
Loading

0 comments on commit 383bd9b

Please sign in to comment.