feat!: installing wivrn apk will use a version matching the server
Some checks are pending
/ cargo-fmtcheck (push) Waiting to run
/ cargo-clippy (push) Waiting to run
/ cargo-test (push) Waiting to run
/ appimage (push) Waiting to run

This commit is contained in:
Gabriele Musco 2024-08-24 11:36:17 +02:00
commit 7daa5987b5

View file

@ -1,22 +1,61 @@
use super::{
alert::alert,
job_worker::{
internal_worker::JobWorkerOut,
job::{FuncWorkerOut, WorkerJob},
JobWorker,
},
};
use super::alert::alert;
use crate::{
async_process::async_process,
dependencies::adb_dep::adb_dep,
downloader::download_file_sync,
paths::wivrn_apk_download_path,
downloader::{cache_file, cache_file_path},
profile::{Profile, XRServiceType},
};
use gtk::{glib::clone, prelude::*};
use gtk::prelude::*;
use relm4::{new_action_group, new_stateless_action, prelude::*};
use std::collections::VecDeque;
use std::fs::remove_file;
const DOWNLOAD_ERROR: &str = "download error";
const ADB_ERR_NO_DEV: &str = "no devices/emulators found";
const WIVRN_LATEST_RELEASE_APK_URL: &str =
"https://github.com/WiVRn/WiVRn/releases/latest/download/WiVRn-standard-release.apk";
#[derive(Debug)]
enum GetWivrnApkRefErr {
NotWivrn,
RepoDirNotFound,
RepoManipulationFailed(git2::Error),
}
#[derive(Debug, Clone)]
enum WivrnApkRef {
Tag(String),
Commit(String),
}
fn get_wivrn_apk_ref(p: &Profile) -> Result<WivrnApkRef, GetWivrnApkRefErr> {
if p.xrservice_type != XRServiceType::Wivrn {
Err(GetWivrnApkRefErr::NotWivrn)
} else if !p.xrservice_path.is_dir() {
Err(GetWivrnApkRefErr::RepoDirNotFound)
} else {
let repo = git2::Repository::open(&p.xrservice_path)
.map_err(GetWivrnApkRefErr::RepoManipulationFailed)?;
if let Ok(tag) = repo
.describe(
git2::DescribeOptions::new()
.describe_tags()
.max_candidates_tags(0),
)
.and_then(|d| d.format(None))
{
Ok(WivrnApkRef::Tag(tag))
} else {
Ok(WivrnApkRef::Commit(
repo.head()
.map_err(GetWivrnApkRefErr::RepoManipulationFailed)?
.peel_to_commit()
.map_err(GetWivrnApkRefErr::RepoManipulationFailed)?
.id()
.to_string(),
))
}
}
}
#[derive(PartialEq, Eq, Debug, Clone)]
pub enum InstallWivrnStatus {
@ -30,10 +69,6 @@ pub struct InstallWivrnBox {
selected_profile: Profile,
install_wivrn_status: InstallWivrnStatus,
#[tracker::do_not_track]
install_runner: Option<JobWorker>,
#[tracker::do_not_track]
install_runner_log: String,
#[tracker::do_not_track]
root_win: gtk::Window,
}
@ -41,9 +76,10 @@ pub struct InstallWivrnBox {
#[derive(Debug)]
pub enum InstallWivrnBoxMsg {
UpdateSelectedProfile(Profile),
InstallWivrnApk(String),
OnInstallRunnerLog(Vec<String>),
OnInstallRunnerExit(i32),
/// prepares state for async action, calls DoInstall
InstallWivrnApk,
/// dowloads, installs, sets state back to what it needs to be
DoInstall(String),
}
#[derive(Debug)]
@ -94,9 +130,7 @@ impl AsyncComponent for InstallWivrnBox {
#[track = "model.changed(Self::install_wivrn_status())"]
set_sensitive: model.install_wivrn_status != InstallWivrnStatus::InProgress,
connect_clicked[sender] => move |_| {
sender.input(Self::Input::InstallWivrnApk(
"https://github.com/WiVRn/WiVRn/releases/latest/download/WiVRn-standard-release.apk".into()
))
sender.input(Self::Input::InstallWivrnApk)
},
},
gtk::Label {
@ -131,82 +165,70 @@ impl AsyncComponent for InstallWivrnBox {
self.reset();
match message {
Self::Input::InstallWivrnApk(link) => {
Self::Input::InstallWivrnApk => {
if !adb_dep().check() {
alert("ADB is not installed", Some("Please install ADB on your computer to install WiVRn on your Android headset"), Some(&self.root_win));
return;
}
self.set_install_wivrn_status(InstallWivrnStatus::InProgress);
self.install_runner_log = String::default();
let mut jobs = VecDeque::new();
let wivrn_apk_path = wivrn_apk_download_path();
jobs.push_back(WorkerJob::new_func(Box::new(clone!(
#[strong]
wivrn_apk_path,
move || {
if download_file_sync(&link, &wivrn_apk_path).is_err() {
FuncWorkerOut {
out: vec![DOWNLOAD_ERROR.into()],
success: false,
match get_wivrn_apk_ref(&self.selected_profile) {
Err(GetWivrnApkRefErr::NotWivrn) => {
eprintln!("This is not a WiVRn profile, how did you get here?");
}
} else if wivrn_apk_path.is_file() {
FuncWorkerOut {
out: Vec::default(),
success: true,
Err(GetWivrnApkRefErr::RepoDirNotFound) => {
self.set_install_wivrn_status(InstallWivrnStatus::Done(Some(
"Could not open WiVRn repository. Please build the profile before attempting to install the client APK".into()
)));
}
} else {
FuncWorkerOut {
out: vec![format!(
"Provided apk path {} does not exist",
wivrn_apk_path.to_string_lossy()
)],
success: false,
}
}
}
))));
jobs.push_back(WorkerJob::new_cmd(
None,
"adb".into(),
Some(vec![
"install".into(),
wivrn_apk_path.to_string_lossy().to_string(),
]),
));
let runner = JobWorker::new(
jobs,
sender.input_sender(),
Box::new(move |e| match e {
JobWorkerOut::Log(log) => Self::Input::OnInstallRunnerLog(log),
JobWorkerOut::Exit(code) => Self::Input::OnInstallRunnerExit(code),
}),
Err(GetWivrnApkRefErr::RepoManipulationFailed(giterr)) => {
eprintln!("Error: failed to manipulate WiVRn repo: {giterr}, falling back to latest release APK");
let existing = cache_file_path(WIVRN_LATEST_RELEASE_APK_URL, Some("apk"));
if existing.is_file() {
if let Err(e) = remove_file(&existing) {
eprintln!(
"Failed to remove file {}: {e}",
existing.to_string_lossy()
);
runner.start();
self.install_runner = Some(runner);
}
Self::Input::UpdateSelectedProfile(p) => {
self.set_selected_profile(p);
}
Self::Input::OnInstallRunnerLog(rows) => {
self.install_runner_log.push('\n');
self.install_runner_log.push_str(&rows.join("\n"));
sender.input(Self::Input::DoInstall(WIVRN_LATEST_RELEASE_APK_URL.into()));
}
Self::Input::OnInstallRunnerExit(code) => {
self.set_install_wivrn_status(match code {
0 => InstallWivrnStatus::Success,
255 => InstallWivrnStatus::Done(Some(
concat!(
"You need to authorize this computer to run developer ",
"commands in your headset. Authorize it and try again.",
Ok(WivrnApkRef::Tag(tag)) => {
sender.input(Self::Input::DoInstall(
format!("https://github.com/WiVRn/WiVRn/releases/download/{tag}/WiVRn-standard-release.apk")
));
}
Ok(WivrnApkRef::Commit(commit)) => {
sender.input(Self::Input::DoInstall(
format!("https://github.com/WiVRn/WiVRn-APK/releases/download/apk-${commit}/org.meumeu.wivrn-standard-release.apk")
));
}
};
}
Self::Input::DoInstall(url) => {
// TODO: we gonna cache or just download async every time?
match cache_file(&url, Some("apk")).await {
Err(e) => {
eprintln!("Failed to download apk: {e}");
self.set_install_wivrn_status(InstallWivrnStatus::Done(Some(
"Error downloading WiVRn client APK".into(),
)));
}
Ok(apk_path) => {
self.set_install_wivrn_status(match async_process(
"adb",
Some(&["install", apk_path.to_string_lossy().to_string().as_str()]),
None,
)
.into(),
)),
1 if self.install_runner_log.contains(DOWNLOAD_ERROR) => {
InstallWivrnStatus::Done(Some("Error downloading WiVRn APK".into()))
.await
{
Ok(out) if out.exit_code == 0 => {
InstallWivrnStatus::Success
}
1 if self
.install_runner_log
.contains("no devices/emulators found") =>
Ok(out) => {
if out.stdout.contains(ADB_ERR_NO_DEV)
|| out.stderr.contains(ADB_ERR_NO_DEV)
{
InstallWivrnStatus::Done(Some(
concat!(
@ -215,12 +237,26 @@ impl AsyncComponent for InstallWivrnBox {
)
.into(),
))
} else {
eprintln!("Error: ADB failed with code {}.\nstdout:\n{}\n======\nstderr:\n{}", out.exit_code, out.stdout, out.stderr);
InstallWivrnStatus::Done(Some(
format!("ADB exited with code \"{}\"", out.exit_code)
))
}
_ => {
InstallWivrnStatus::Done(Some(format!("ADB exited with code \"{}\"", code)))
}
Err(e) => {
eprintln!("Error: failed to run ADB: {e}");
InstallWivrnStatus::Done(Some(
"Failed to run ADB".into()
))
}
});
}
};
}
Self::Input::UpdateSelectedProfile(p) => {
self.set_selected_profile(p);
}
}
}
@ -232,8 +268,6 @@ impl AsyncComponent for InstallWivrnBox {
let model = Self {
selected_profile: init.selected_profile,
install_wivrn_status: InstallWivrnStatus::Done(None),
install_runner: None,
install_runner_log: String::default(),
root_win: init.root_win,
tracker: 0,
};
@ -246,4 +280,3 @@ impl AsyncComponent for InstallWivrnBox {
new_action_group!(pub InstallWivrnActionGroup, "installwivrn");
new_stateless_action!(pub WivrnApkStandardAction, InstallWivrnActionGroup, "apkoculus");
new_stateless_action!(pub WivrnApkPicoAction, InstallWivrnActionGroup, "apkpico");