diff --git a/src/activity_manager.rs b/src/activity_manager.rs index 64ca29a..c9c3b6a 100644 --- a/src/activity_manager.rs +++ b/src/activity_manager.rs @@ -51,7 +51,7 @@ pub enum NextActivity { /// /// The activity manager takes care of running activities and handling them until the application has ended pub struct ActivityManager { - context: Context, + context: Option, ftparams: Option, interval: Duration, } @@ -71,7 +71,7 @@ impl ActivityManager { }; let ctx: Context = Context::new(host); Ok(ActivityManager { - context: ctx, + context: Some(ctx), ftparams: None, interval: interval, }) @@ -127,11 +127,16 @@ impl ActivityManager { let mut activity: AuthActivity = AuthActivity::new(); // Prepare result let result: Option; + // Get context + let ctx: Context = match self.context.take() { + Some(ctx) => ctx, + None => return None + }; // Create activity - activity.on_create(&mut self.context); + activity.on_create(ctx); loop { // Draw activity - activity.on_draw(&mut self.context); + activity.on_draw(); // Check if has to be terminated if activity.quit { // Quit activities @@ -161,7 +166,7 @@ impl ActivityManager { sleep(self.interval); } // Destroy activity - activity.on_destroy(&mut self.context); + self.context = activity.on_destroy(); result } @@ -179,11 +184,16 @@ impl ActivityManager { FileTransferActivity::new(self.ftparams.take().unwrap()); // Prepare result let result: Option; + // Get context + let ctx: Context = match self.context.take() { + Some(ctx) => ctx, + None => return None + }; // Create activity - activity.on_create(&mut self.context); + activity.on_create(ctx); loop { // Draw activity - activity.on_draw(&mut self.context); + activity.on_draw(); // Check if has to be terminated if activity.quit { // Quit activities @@ -199,7 +209,7 @@ impl ActivityManager { sleep(self.interval); } // Destroy activity - activity.on_destroy(&mut self.context); + self.context = activity.on_destroy(); result } } diff --git a/src/ui/activities/auth_activity.rs b/src/ui/activities/auth_activity.rs index 581046a..788e192 100644 --- a/src/ui/activities/auth_activity.rs +++ b/src/ui/activities/auth_activity.rs @@ -77,6 +77,7 @@ pub struct AuthActivity { pub password: String, pub submit: bool, // becomes true after user has submitted fields pub quit: bool, // Becomes true if user has pressed esc + context: Option, selected_field: InputField, input_mode: InputMode, popup_message: Option, @@ -96,6 +97,7 @@ impl AuthActivity { password: String::new(), submit: false, quit: false, + context: None, selected_field: InputField::Address, input_mode: InputMode::Text, popup_message: None, @@ -409,20 +411,27 @@ impl Activity for AuthActivity { /// /// `on_create` is the function which must be called to initialize the activity. /// `on_create` must initialize all the data structures used by the activity - fn on_create(&mut self, context: &mut Context) { + /// Context is taken from activity manager and will be released only when activity is destroyed + fn on_create(&mut self, context: Context) { + // Set context + self.context = Some(context); + // Clear terminal + let _ = self.context.as_mut().unwrap().terminal.clear(); // Put raw mode on enabled let _ = enable_raw_mode(); - // Clear terminal - let _ = context.terminal.clear(); } /// ### on_draw /// /// `on_draw` is the function which draws the graphical interface. /// This function must be called at each tick to refresh the interface - fn on_draw(&mut self, context: &mut Context) { + fn on_draw(&mut self) { + // Context must be something + if self.context.is_none() { + return; + } // Start catching Input Events - if let Ok(input_events) = context.input_hnd.fetch_events() { + if let Ok(input_events) = self.context.as_ref().unwrap().input_hnd.fetch_events() { // Iterate over input events for event in input_events.iter() { self.handle_input_event(event); @@ -431,7 +440,8 @@ impl Activity for AuthActivity { // Determine input mode self.input_mode = self.select_input_mode(); // draw interface - let _ = context.terminal.draw(|f| { + let mut ctx: Context = self.context.take().unwrap(); + let _ = ctx.terminal.draw(|f| { let chunks = Layout::default() .direction(Direction::Vertical) .margin(2) @@ -483,16 +493,28 @@ impl Activity for AuthActivity { _ => {} } }); + // Reset ctx + self.context = Some(ctx); } /// ### on_destroy /// /// `on_destroy` is the function which cleans up runtime variables and data before terminating the activity. /// This function must be called once before terminating the activity. - fn on_destroy(&mut self, context: &mut Context) { + /// This function finally releases the context + fn on_destroy(&mut self) -> Option { // Disable raw mode let _ = disable_raw_mode(); - // Clear terminal - let _ = context.terminal.clear(); + if self.context.is_none() { + return None; + } + // Clear terminal and return + match self.context.take() { + Some(mut ctx) => { + let _ = ctx.terminal.clear(); + Some(ctx) + }, + None => None + } } } diff --git a/src/ui/activities/filetransfer_activity.rs b/src/ui/activities/filetransfer_activity.rs index 742dc6c..1cc0efe 100644 --- a/src/ui/activities/filetransfer_activity.rs +++ b/src/ui/activities/filetransfer_activity.rs @@ -51,7 +51,7 @@ use tui::{ style::{Color, Modifier, Style}, terminal::Frame, text::{Span, Spans, Text}, - widgets::{Block, Borders, Clear, Paragraph, Tabs}, + widgets::{Block, Borders, List, ListItem, Paragraph, Tabs} }; use unicode_width::UnicodeWidthStr; @@ -59,8 +59,8 @@ use unicode_width::UnicodeWidthStr; static mut UPLOAD_PROGRESS: f64 = 0.0; // VERY VERY BAD CODING // Types -type DialogCallback = fn(&mut FileTransferActivity, &mut Context); -type OnInputSubmitCallback = fn(&mut FileTransferActivity, &mut Context, String); +type DialogCallback = fn(&mut FileTransferActivity); +type OnInputSubmitCallback = fn(&mut FileTransferActivity, String); /// ### FileTransferParams /// @@ -200,6 +200,7 @@ impl LogRecord { pub struct FileTransferActivity { pub disconnected: bool, // Has disconnected from remote? pub quit: bool, // Has quit term scp? + context: Option, // Context holder params: FileTransferParams, // FT connection params client: Box, // File transfer client local: FileExplorer, // Local File explorer state @@ -223,6 +224,7 @@ impl FileTransferActivity { FileTransferActivity { disconnected: false, quit: false, + context: None, params: params, client: match protocol { FileTransferProtocol::Sftp => Box::new(SftpFileTransfer::new()), @@ -269,7 +271,7 @@ impl FileTransferActivity { /// ### disconnect /// /// disconnect from remote - fn disconnect(&mut self, context: &mut Context) { + fn disconnect(&mut self) { // Show popup disconnecting self.input_mode = InputMode::Popup(PopupType::Alert( Color::Red, @@ -307,7 +309,7 @@ impl FileTransferActivity { /// Send fs entry to remote. /// If dst_name is Some, entry will be saved with a different name. /// If entry is a directory, this applies to directory only - fn filetransfer_send(&mut self, ctx: &mut Context, entry: &FsEntry, dst_name: Option) { + fn filetransfer_send(&mut self, entry: &FsEntry, dst_name: Option) { // Write popup let file_name: String = match entry { FsEntry::Directory(dir) => dir.name.clone(), @@ -334,7 +336,7 @@ impl FileTransferActivity { FsEntry::File(file) => { // Upload file // Try to open local file - match ctx.local.open_file_read(file.abs_path.as_path()) { + match self.context.as_ref().unwrap().local.open_file_read(file.abs_path.as_path()) { Ok(mut f) => { match self .client @@ -383,12 +385,12 @@ impl FileTransferActivity { format!("Created directory \"{}\"", dir.abs_path.display()).as_ref(), ); // Get files in dir - match ctx.local.scan_dir(dir.abs_path.as_path()) { + match self.context.as_ref().unwrap().local.scan_dir(dir.abs_path.as_path()) { Ok(entries) => { // Iterate over files for entry in entries.iter() { // Send entry; name is always None after first call - self.filetransfer_send(ctx, &entry, None); + self.filetransfer_send(&entry, None); } } Err(err) => self.log( @@ -423,7 +425,6 @@ impl FileTransferActivity { /// If entry is a directory, this applies to directory only fn filetransfer_recv( &mut self, - ctx: &mut Context, entry: &FsEntry, local_path: &Path, dst_name: Option, @@ -456,7 +457,7 @@ impl FileTransferActivity { }; local_file_path.push(local_file_name.as_str()); // Try to open local file - match ctx.local.open_file_write(local_file_path.as_path()) { + match self.context.as_ref().unwrap().local.open_file_write(local_file_path.as_path()) { Ok(mut local_file) => { // Download file from remote match self.client.recv_file( @@ -506,7 +507,7 @@ impl FileTransferActivity { None => local_dir_path.push(dir.name.as_str()), } // Create directory on local - match ctx.local.mkdir_ex(local_dir_path.as_path(), true) { + match self.context.as_mut().unwrap().local.mkdir_ex(local_dir_path.as_path(), true) { Ok(_) => { self.log( LogLevel::Info, @@ -520,7 +521,6 @@ impl FileTransferActivity { // Receive entry; name is always None after first call // Local path becomes local_dir_path self.filetransfer_recv( - ctx, &entry, local_dir_path.as_path(), None, @@ -594,11 +594,11 @@ impl FileTransferActivity { /// ### handle_input_event /// /// Handle input event based on current input mode - fn handle_input_event(&mut self, context: &mut Context, ev: &InputEvent) { + fn handle_input_event(&mut self, ev: &InputEvent) { match &self.input_mode { - InputMode::Explorer => self.handle_input_event_mode_explorer(context, ev), + InputMode::Explorer => self.handle_input_event_mode_explorer(ev), InputMode::Popup(ptype) => { - self.handle_input_event_mode_popup(ev, ptype.clone(), context) + self.handle_input_event_mode_popup(ev, ptype.clone()) } } } @@ -606,16 +606,16 @@ impl FileTransferActivity { /// ### handle_input_event_mode_explorer /// /// Input event handler for explorer mode - fn handle_input_event_mode_explorer(&mut self, context: &mut Context, ev: &InputEvent) { + fn handle_input_event_mode_explorer(&mut self, ev: &InputEvent) { // Match input field match self.input_field { InputField::Explorer => match self.tab { // Match current selected tab FileExplorerTab::Local => { - self.handle_input_event_mode_explorer_tab_local(context, ev) + self.handle_input_event_mode_explorer_tab_local(ev) } FileExplorerTab::Remote => { - self.handle_input_event_mode_explorer_tab_remote(context, ev) + self.handle_input_event_mode_explorer_tab_remote(ev) } }, InputField::Logs => self.handle_input_event_mode_explorer_log(ev), @@ -627,7 +627,6 @@ impl FileTransferActivity { /// Input event handler for explorer mode when localhost tab is selected fn handle_input_event_mode_explorer_tab_local( &mut self, - context: &mut Context, ev: &InputEvent, ) { // Match events @@ -658,9 +657,9 @@ impl FileTransferActivity { if let Some(entry) = self.local.files.get(self.local.index) { if let FsEntry::Directory(dir) = entry { // Get current directory - let prev_dir: PathBuf = context.local.pwd(); + let prev_dir: PathBuf = self.context.as_ref().unwrap().local.pwd(); // Change directory - match context.local.change_wrkdir(dir.abs_path.clone()) { + match self.context.as_mut().unwrap().local.change_wrkdir(dir.abs_path.clone()) { Ok(_) => self.local.pushd(prev_dir.as_path()), // Push prev_dir to stack Err(err) => { // Report err @@ -671,7 +670,7 @@ impl FileTransferActivity { } } // Update files - self.local.files = context.local.list_dir(); + self.local.files = self.context.as_ref().unwrap().local.list_dir(); } } } @@ -681,10 +680,10 @@ impl FileTransferActivity { // Till a valid directory is found match self.local.popd() { Some(d) => { - match context.local.change_wrkdir(d) { + match self.context.as_mut().unwrap().local.change_wrkdir(d) { Ok(_) => { // Update files - self.local.files = context.local.list_dir(); + self.local.files = self.context.as_ref().unwrap().local.list_dir(); // Break, directory has changed break; } @@ -769,7 +768,7 @@ impl FileTransferActivity { // Get file at index if let Some(entry) = files.get(self.local.index) { // Call upload - self.filetransfer_send(context, entry, None); + self.filetransfer_send(entry, None); } } _ => { /* Nothing to do */ } @@ -786,7 +785,6 @@ impl FileTransferActivity { /// Input event handler for explorer mode when remote tab is selected fn handle_input_event_mode_explorer_tab_remote( &mut self, - context: &mut Context, ev: &InputEvent, ) { // Match events @@ -961,9 +959,8 @@ impl FileTransferActivity { if let Some(entry) = files.get(self.remote.index) { // Call upload self.filetransfer_recv( - context, entry, - context.local.pwd().as_path(), + self.context.as_ref().unwrap().local.pwd().as_path(), None, ); } @@ -1035,16 +1032,15 @@ impl FileTransferActivity { &mut self, ev: &InputEvent, popup: PopupType, - ctx: &mut Context, ) { match popup { PopupType::Alert(_, _) => self.handle_input_event_mode_popup_alert(ev), PopupType::Fatal(_) => self.handle_input_event_mode_popup_fatal(ev), - PopupType::Input(_, cb) => self.handle_input_event_mode_popup_input(ev, ctx, cb), + PopupType::Input(_, cb) => self.handle_input_event_mode_popup_input(ev, cb), PopupType::Progress(_) => self.handle_input_event_mode_popup_progress(ev), PopupType::Wait(_) => self.handle_input_event_mode_popup_wait(ev), PopupType::YesNo(_, yes_cb, no_cb) => { - self.handle_input_event_mode_popup_yesno(ev, ctx, yes_cb, no_cb) + self.handle_input_event_mode_popup_yesno(ev, yes_cb, no_cb) } } } @@ -1093,7 +1089,6 @@ impl FileTransferActivity { fn handle_input_event_mode_popup_input( &mut self, ev: &InputEvent, - ctx: &mut Context, cb: OnInputSubmitCallback, ) { // If enter, close popup, otherwise push chars to input @@ -1115,7 +1110,7 @@ impl FileTransferActivity { // Set mode back to explorer BEFORE CALLBACKS!!! Callback can then overwrite this, clever uh? self.input_mode = InputMode::Explorer; // Call cb - cb(self, ctx, input_text); + cb(self, input_text); } KeyCode::Char(ch) => self.input_txt.push(ch), KeyCode::Backspace => { @@ -1154,7 +1149,6 @@ impl FileTransferActivity { fn handle_input_event_mode_popup_yesno( &mut self, ev: &InputEvent, - ctx: &mut Context, yes_cb: DialogCallback, no_cb: DialogCallback, ) { @@ -1167,8 +1161,8 @@ impl FileTransferActivity { self.input_mode = InputMode::Explorer; // Check if user selected yes or not match self.choice_opt { - DialogYesNoOption::No => no_cb(self, ctx), - DialogYesNoOption::Yes => yes_cb(self, ctx), + DialogYesNoOption::No => no_cb(self), + DialogYesNoOption::Yes => yes_cb(self), } } KeyCode::Right => self.choice_opt = DialogYesNoOption::No, // Set to NO @@ -1185,20 +1179,20 @@ impl FileTransferActivity { /// ### callback_nothing_to_do /// /// Self titled - fn callback_nothing_to_do(&mut self, _context: &mut Context) {} + fn callback_nothing_to_do(&mut self) {} /// ### callback_force_input_mode_to_explorer /// /// force input mode to explorer - fn callback_force_input_mode_to_explorer(&mut self, _context: &mut Context) { + fn callback_force_input_mode_to_explorer(&mut self) { self.input_mode = InputMode::Explorer; } /// ### callback_change_directory /// /// Callback for GOTO command - fn callback_change_directory(&mut self, context: &mut Context, input: String) { - match context.local.change_wrkdir(PathBuf::from(input.as_str())) { + fn callback_change_directory(&mut self, input: String) { + match self.context.as_mut().unwrap().local.change_wrkdir(PathBuf::from(input.as_str())) { Err(err) => { // Report err self.input_mode = InputMode::Popup(PopupType::Alert( @@ -1206,24 +1200,24 @@ impl FileTransferActivity { format!("Could not change working directory: {}", err), )); } - Ok(_) => self.local.files = context.local.list_dir(), // Update files + Ok(_) => self.local.files = self.context.as_ref().unwrap().local.list_dir(), // Update files } } /// ### callback_mkdir /// /// Callback for MKDIR command (supports both local and remote) - fn callback_mkdir(&mut self, context: &mut Context, input: String) { + fn callback_mkdir(&mut self, input: String) { match self.tab { FileExplorerTab::Local => { - match context.local.mkdir(PathBuf::from(input.as_str()).as_path()) { + match self.context.as_mut().unwrap().local.mkdir(PathBuf::from(input.as_str()).as_path()) { Ok(_) => { // Reload files self.log( LogLevel::Info, format!("Created directory \"{}\"", input).as_ref(), ); - self.local.files = context.local.list_dir(); + self.local.files = self.context.as_ref().unwrap().local.list_dir(); } Err(err) => { // Report err @@ -1267,13 +1261,13 @@ impl FileTransferActivity { /// ### callback_rename /// /// Callback for RENAME command (supports borth local and remote) - fn callback_rename(&mut self, context: &mut Context, input: String) { + fn callback_rename(&mut self, input: String) { match self.tab { FileExplorerTab::Local => { let mut dst_path: PathBuf = PathBuf::from(input); // Check if path is relative if dst_path.as_path().is_relative() { - let mut wrkdir: PathBuf = context.local.pwd(); + let mut wrkdir: PathBuf = self.context.as_ref().unwrap().local.pwd(); wrkdir.push(dst_path); dst_path = wrkdir; } @@ -1284,10 +1278,10 @@ impl FileTransferActivity { FsEntry::File(file) => file.abs_path.clone(), }; // Rename file or directory and report status as popup - match context.local.rename(entry, dst_path.as_path()) { + match self.context.as_mut().unwrap().local.rename(entry, dst_path.as_path()) { Ok(_) => { // Reload files - self.local.files = context.local.list_dir(); + self.local.files = self.context.as_ref().unwrap().local.list_dir(); // Log self.log( LogLevel::Info, @@ -1329,7 +1323,7 @@ impl FileTransferActivity { match self.client.rename(entry, dst_path.as_path()) { Ok(_) => { // Reload files - self.local.files = context.local.list_dir(); + self.local.files = self.context.as_ref().unwrap().local.list_dir(); // Log self.log( LogLevel::Info, @@ -1365,7 +1359,7 @@ impl FileTransferActivity { /// ### callback_delete_fsentry /// /// Delete current selected fsentry in the currently selected TAB - fn callback_delete_fsentry(&mut self, context: &mut Context) { + fn callback_delete_fsentry(&mut self) { // Match current selected tab match self.tab { FileExplorerTab::Local => { @@ -1376,10 +1370,10 @@ impl FileTransferActivity { FsEntry::File(file) => file.abs_path.clone(), }; // Delete file or directory and report status as popup - match context.local.remove(entry) { + match self.context.as_mut().unwrap().local.remove(entry) { Ok(_) => { // Reload files - self.local.files = context.local.list_dir(); + self.local.files = self.context.as_ref().unwrap().local.list_dir(); // Log self.log( LogLevel::Info, @@ -1445,14 +1439,14 @@ impl FileTransferActivity { /// /// Call file upload, but save with input as name /// Handled both local and remote tab - fn callback_save_as(&mut self, ctx: &mut Context, input: String) { + fn callback_save_as(&mut self, input: String) { match self.tab { FileExplorerTab::Local => { let files: Vec = self.local.files.clone(); // Get file at index if let Some(entry) = files.get(self.local.index) { // Call send (upload) - self.filetransfer_send(ctx, entry, Some(input)); + self.filetransfer_send(entry, Some(input)); } } FileExplorerTab::Remote => { @@ -1460,7 +1454,7 @@ impl FileTransferActivity { // Get file at index if let Some(entry) = files.get(self.remote.index) { // Call receive (download) - self.filetransfer_recv(ctx, entry, ctx.local.pwd().as_path(), Some(input)); + self.filetransfer_recv(entry, self.context.as_ref().unwrap().local.pwd().as_path(), Some(input)); } } } @@ -1483,6 +1477,43 @@ impl FileTransferActivity { .style(Style::default().fg(Color::LightYellow).add_modifier(Modifier::BOLD)) } + /* + /// ### draw_log_list + /// + /// Draw log list + fn draw_log_list(&self) -> List { + let events: Vec = self.log_records + .iter() + .map(|&(evt, level)| { + let s = match level { + "CRITICAL" => Style::default().fg(Color::Red), + "ERROR" => Style::default().fg(Color::Magenta), + "WARNING" => Style::default().fg(Color::Yellow), + "INFO" => Style::default().fg(Color::Blue), + _ => Style::default(), + }; + let header = Spans::from(vec![ + Span::styled(format!("{:<9}", level), s), + Span::raw(" "), + Span::styled( + "2020-01-01 10:00:00", + Style::default().add_modifier(Modifier::ITALIC), + ), + ]); + let log = Spans::from(vec![Span::raw(evt)]); + ListItem::new(vec![ + Spans::from("-".repeat(chunks[1].width as usize)), + header, + Spans::from(""), + log, + ]) + }) + .collect(); + let events_list = List::new(events) + .block(Block::default().borders(Borders::ALL).title("List")) + .start_corner(Corner::BottomLeft); + }*/ + /// ### draw_footer /// /// Draw authentication page footer @@ -1577,20 +1608,26 @@ impl Activity for FileTransferActivity { /// /// `on_create` is the function which must be called to initialize the activity. /// `on_create` must initialize all the data structures used by the activity - fn on_create(&mut self, context: &mut Context) { + fn on_create(&mut self, context: Context) { + // Set context + self.context = Some(context); + // Clear terminal + let _ = self.context.as_mut().unwrap().terminal.clear(); // Put raw mode on enabled let _ = enable_raw_mode(); - // Clear terminal - let _ = context.terminal.clear(); // Get files at current wd - self.local.files = context.local.list_dir(); + self.local.files = self.context.as_ref().unwrap().local.list_dir(); } /// ### on_draw /// /// `on_draw` is the function which draws the graphical interface. /// This function must be called at each tick to refresh the interface - fn on_draw(&mut self, context: &mut Context) { + fn on_draw(&mut self) { + // Context must be something + if self.context.is_none() { + return; + } // Check if connected if !self.client.is_connected() { // Set init state to connecting popup @@ -1599,37 +1636,47 @@ impl Activity for FileTransferActivity { self.params.address, self.params.port ))); // Force ui draw - let _ = context.terminal.draw(|f| { + let mut ctx: Context = self.context.take().unwrap(); + let _ = ctx.terminal.draw(|f| { self.draw(f); }); + self.context = Some(ctx); // Connect to remote self.connect(); } // Handle input events FIXME: read one or multiple? - if let Ok(event) = context.input_hnd.read_event() { + if let Ok(event) = self.context.as_ref().unwrap().input_hnd.read_event() { // Iterate over input events if let Some(event) = event { - self.handle_input_event(context, &event); + self.handle_input_event(&event); } } // @! draw interface - let _ = context.terminal.draw(|f| { + let mut ctx: Context = self.context.take().unwrap(); + let _ = ctx.terminal.draw(|f| { self.draw(f); }); + self.context = Some(ctx); } /// ### on_destroy /// /// `on_destroy` is the function which cleans up runtime variables and data before terminating the activity. /// This function must be called once before terminating the activity. - fn on_destroy(&mut self, context: &mut Context) { + fn on_destroy(&mut self) -> Option { // Disable raw mode let _ = disable_raw_mode(); - // Clear terminal - let _ = context.terminal.clear(); // Disconnect client if self.client.is_connected() { let _ = self.client.disconnect(); } + // Clear terminal and return + match self.context.take() { + Some(mut ctx) => { + let _ = ctx.terminal.clear(); + Some(ctx) + }, + None => None + } } } diff --git a/src/ui/activities/mod.rs b/src/ui/activities/mod.rs index 5d675a5..2ce9ffb 100644 --- a/src/ui/activities/mod.rs +++ b/src/ui/activities/mod.rs @@ -38,17 +38,19 @@ pub trait Activity { /// /// `on_create` is the function which must be called to initialize the activity. /// `on_create` must initialize all the data structures used by the activity - fn on_create(&mut self, context: &mut Context); + /// Context is taken from activity manager and will be released only when activity is destroyed + fn on_create(&mut self, context: Context); /// ### on_draw /// /// `on_draw` is the function which draws the graphical interface. /// This function must be called at each tick to refresh the interface - fn on_draw(&mut self, context: &mut Context); + fn on_draw(&mut self); /// ### on_destroy /// /// `on_destroy` is the function which cleans up runtime variables and data before terminating the activity. /// This function must be called once before terminating the activity. - fn on_destroy(&mut self, context: &mut Context); + /// This function finally releases the context + fn on_destroy(&mut self) -> Option; }