How to implement an (auto) Logout feature in MAUI? - logout

I have another question, please.
I have a shell-based navigation:
<Shell ...
<Shell.Items>
<ShellContent x:Name="Login" ContentTemplate="{DataTemplate Login:Login}" Route="Login" Shell.FlyoutBehavior="Disabled" Shell.NavBarIsVisible="False" ></ShellContent>
</Shell.Items>
<TabBar Route="Login">
<ShellContent ContentTemplate="{DataTemplate Login:Login}" Shell.FlyoutBehavior="Disabled" Shell.NavBarIsVisible="False" ></ShellContent>
</TabBar>
<TabBar x:Name="MyTabBar" Shell.NavBarHasShadow="true" Route="Home">
<Tab Title="Tab1" ... >
<ShellContent Title="Page1"
</ShellContent>
<ShellContent Title="Page2"
</ShellContent>
</Tab>
<Tab Title="Tab2" ... >
<ShellContent Title="Page3"
</ShellContent>
<ShellContent Title="Page4"
</ShellContent>
</Tab>
<Tab Title="Tab3"... >
<ShellContent Title="Page5"
</ShellContent>
</Tab>
</TabBar>
</Shell>
I need to implement a Logout feature.
If I create a Flyout containing just this Logout button I am afraid that it won't be at all visible or intuitive or obvious for the users.
I would like to set it up in some linkButton residing to the right of my TabBar, thus showing, for instance,
Page1 Page2 Logout
and
Page3 Page4 Logout
and
Page5 Logout
but unfortunately I do not know how to insert a button there.
I could maybe set it up it on the last page, Page5, somwehere (that's what I've been told to do by my manager - I do not agree with that position - again, it's not at all obvious).
But no matter where I set this button, I don't know how to clear the nav history (to kill that back arrow).
I have tried to write this code on that button:
[RelayCommand]
private static async void Logout()
{
try
{
Globals.loginStatus = Globals.LoginStatus.loginRefused;
await Shell.Current.Navigation.PopToRootAsync();
await Shell.Current.GoToAsync(nameof(Login), true);
}
catch (Exception ex)
{
Debug.WriteLine($"Unable to logout: {ex.Message}");
Utils.ErrorHandling.HandleError(ex);
}
}
together with this (in Shell.cs):
protected override void OnNavigating(ShellNavigatingEventArgs args)
{
base.OnNavigating(args);
// Cancel any back navigation.
if (args.Source == ShellNavigationSource.Pop)
{
args.Cancel();
}
}
This does not do me any good - I can still see the Back arrow one the Login tab. I can also see the three tabs on the bottom: Tab1, Tab2, and Tab3, enabled, and I shouldn't be able to see those !
By reversing the order of the calls
[RelayCommand]
private static async void Logout()
{
try
{
Globals.loginStatus = Globals.LoginStatus.loginRefused;
await Shell.Current.GoToAsync(nameof(Login), true);
await Shell.Current.Navigation.PopToRootAsync();
}
catch (Exception ex)
{
Debug.WriteLine($"Unable to logout: {ex.Message}");
Utils.ErrorHandling.HandleError(ex);
}
}
the result is even funnier: the app flip-flops between Page5 and Login.
So, my questions are:
Where should I set this Logout button up ? In the case there is a way to put it in the TabBar (my first choice), how ?
What do you think about putting it into some FlyOut on the Login page ?
What should I do to really kill the back navigation ? How could I also hide the TabBar items by code from this method in Page5 ?
I could maybe just say Application.Current.Quit(); but this is not quite elegant, is it ? Besides, this might not work in iOS.
Since this app works with sensitive data, I would like to write an auto-logout feature, to be executed after, say, one minute of inactivity. It's very simple to do this in Windows (using Windows api) but how would one do it in MAUI ?
Thank you !
Alex

Related

.NET Maui pushing a new content page from a Toolbar Item

I've got a toolbar item in the navigationbar of a Maui app. This is a shell app. I've got a toolbar item in the nav bar. When I touch the Add toolbar item, the click event happens, but nothing happens in the app. I don't see my CreateUser() page. Any suggestions in how to view the page are appreciated.
My content page:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ScanAndScoringMauiApp.Pages.UserSearch"
Title="UserSearch">
<Shell.TitleView>
<StackLayout>
<Button Text="ADD" x:Name="Add" Clicked="Add_Clicked" HeightRequest="50" WidthRequest="100" HorizontalOptions="End"></Button>
</StackLayout>
</Shell.TitleView>
My Add_Clicked event is various iterations of the code below. I've tried iterations of this without using InvokeOnMainThreadAsync and on and on.
private async void Add_Clicked(object sender, EventArgs e)
{
await MainThread.InvokeOnMainThreadAsync(() =>
{
Navigation.PushAsync(new CreateUser());
});
}

redirect to list page after clicking on add button in JSF and html page [duplicate]

I have three XHTML pages;
index.xhtml
page_1.xhtml
page_2.xhtml
In the index.xhtml page, I have a commandButton which sends the user to page_1.xhtml. All this is done in the navigation rule in faces-config.xml.
How would I redirect the user to page_2.xhtml from the index.xhtml using another commandButton assuming that both commandButtons' actions are linked to a backing Java class?
Just bind the buttons to different action methods which each return a different outcome.
<h:commandButton value="Go to page 1" action="#{bean.goToPage1}" />
<h:commandButton value="Go to page 2" action="#{bean.goToPage2}" />
with
public String goToPage1() {
// ...
return "page_1";
}
public String goToPage2() {
// ...
return "page_2";
}
Navigation cases are not necessary. JSF 2.0 supports implicit navigation. The navigation outcome can just be the path/filename of the desired target view. The file extension in the outcome is optional.
If you don't necessarily need to perform any business action on navigation, or you can do it in the (post)constructor of the backing bean of the target page instead, then you can also just put the outcome value in the action directly.
<h:commandButton value="Go to page 1" action="page_1" />
<h:commandButton value="Go to page 2" action="page_2" />
A <h:commandButton> will however not perform a redirect, but a forward. The enduser won't see the URL being changed in the browser address bar. The target page isn't bookmarkable. If you can, I'd suggest to use <h:button> instead.
<h:button value="Go to page 1" outcome="page_1" />
<h:button value="Go to page 2" outcome="page_2" />
Or if you really need to invoke a business action, but would like to perform a real redirect, then append faces-redirect=true as query string to the outcome value.
public String goToPage1() {
// ...
return "page_1?faces-redirect=true";
}
public String goToPage2() {
// ...
return "page_2?faces-redirect=true";
}
See also:
How to navigate in JSF? How to make URL reflect current page (and not previous one)
When should I use h:outputLink instead of h:commandLink?
You can also do this, in any part of your code to be redirected to "example.xhtml"
ExternalContext ec = FacesContext.getCurrentInstance()
.getExternalContext();
try {
ec.redirect(ec.getRequestContextPath()
+ "/faces/jsf/example.xhtml");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Add two navigation cases as shown below. In the action methods, return outcomes corresponding to the buttons.
<navigation-rule>
<from-view-id>index.html</from-view-id>
<navigation-case>
<from-outcome>page1</from-outcome>
<to-view-id>page_1.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>page2</from-outcome>
<to-view-id>page_2.xhtml</to-view-id>
</navigation-case>
</navigation-rule>

SAP Fiori: Back button in fragments

I am working on a SAP Fiori app and I am stuck in this problem for about 2 weeks with no result. I have a "create" fragment which is attached to "Detail" view.
When I open the "create" fragment and want to go back to the main detail view, the back button doesn't work therefore I have to refresh the app.
I guess the back button doesn't work the same way between views & between fragments.
Here's my back button function :
cancel: function() {
var oHistory = sap.ui.core.routing.History.getInstance(),
sPreviousHash = oHistory.getPreviousHash();
if (sPreviousHash !== undefined) {
// The history contains a previous entry
history.go(-1);
}
},
Here when I display sPreviousHash, it's undefined. Why?
Where is the back button that you are referring to?
I would expect that the fragment is a dialog and therefore that there is no back button. A close button is needed which will result in the fragment being closed. Depending on the requirements, the back navigation could then be done from the controller where the fragment was created.
Fragments are a technique to reuse UI parts, but are not part of the MVC concept. You cannot navigate directly to/from a fragement and this has to be done using views. Therefore the history is not available in a fragment. The BROWSER back button will take you to the previous screen in the BROWSER history.
Controller of the view in which the fragment as a dialog is opened.
_initializeReviewDialog: function() { this._oReviewDialog = sap.ui.xmlfragment(this.getView().getId(), "ReviewDialog", this);
this.attachControl(this._oReviewDialog);
},
Event in View that triggers the dialog opening
onEditReviewPressed: function(oEvent) {
if (!this._oReviewDialog) {
this._initializeReviewDialog(); }
}
this._oReviewDialog.open();
},
onReviewDialogOKPressed: function(oEvent) {
this._oReviewDialog.close();
},
Add fragment as a Dependent, so that models and events from owning view/controller are know
attachControl: function(oControl) {
var sCompactCozyClass = this.getOwnerComponent().getContentDensityClass();
jQuery.sap.syncStyleClass(sCompactCozyClass, this.getView(), oControl);
this.getView().addDependent(oControl);
},
FragmentDefinition
<core:FragmentDefinition id="ReviewDialogFragment" xmlns="sap.m" xmlns:l="sap.ui.layout"
xmlns:core="sap.ui.core">
<Dialog id="reviewDialog" >
<content>
... </content>
<beginButton>
<Button id="btnOK" text="{i18n>xbut.ok}" press="onReviewDialogOKPressed"/>
</beginButton> </Dialog>
</core:FragmentDefinition>

javafx how to reuse custom component in different FXML

After several searching i could find any solution for this and would like to ask for help. Thanks in advance.
I have a FXML with HBox with several buttons (new, show, edit). Each button has onAction method. It is used in other 2 FXML (A & B) and I would like to reuse such HBox.
Other 2 FXML (A, B) have some controls (tableview, ...)
This is what i tried:
1) import HBox FXML is not an option as changes must be performed in all FXML (a & b).
2) include using fx:id: I will have a dedicated controller for HBox and I would like to have controls and methods on A, B controllers.
3) include using fx:root: I think this is the way to achieve this but i have errors.
Navigation.fxml file:
<fx:root spacing="5.0" type="HBox" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Button fx:id="btnOpen" mnemonicParsing="false" onAction="#onActionOpen" text="Open" />
<Button fx:id="btnSave" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#onActionSave" text="Save" />
<Separator visible="false" HBox.hgrow="ALWAYS" />
<Button fx:id="btnClose" layoutX="62.0" layoutY="10.0" mnemonicParsing="false" onAction="#onActionClose" text="Close" />
</children>
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</padding>
</fx:root>
Pane1controller.java:
public Pane1Controller() {
System.out.println("Pane1_Controller");
NavigationController nav = new NavigationController();
System.out.println("NavigationController ... done");
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("pane1.fxml"));
System.out.println("setting root");
fxmlLoader.setRoot(this);
System.out.println("setting controller");
fxmlLoader.setController(this);
try {
System.out.println("loading fxml");
pnlMain = fxmlLoader.load();
System.out.println("done");
} catch (IOException exception) {
System.out.println("exception");
pnlMain = null;
throw new RuntimeException(exception);
}
System.out.println("done");
} ...
}
using setRoot ....
Caused by: java.lang.RuntimeException: javafx.fxml.LoadException: Root
value already specified.
file:/C:/Users/ecejdap/data/DEV/dev.java.nb/Testfx_fxroot/dist/run573616745/Testfx_fxroot.jar!/testfx_fxroot/pane1.fxml
removing setroot
Caused by: java.lang.RuntimeException: javafx.fxml.LoadException: Root value already specified.
file:/C:/Users/ecejdap/data/DEV/dev.java.nb/Testfx_fxroot/dist/run2127482879/Testfx_fxroot.jar!/testfx_fxroot/Navigation.fxml
Which is the correct way to achieve this?
Thanks for your support.
BR
This must be caused due to the <fx:root> tag
I've created a sample project for the above task. Visit GitHub for the project. Clone it and refer. I think you can understand it. Link

Actionbar up navigation with fragment and activity

I'm going to use home button (Action bar App icon) as back button. I got it to work but not in the way i intended.
My MainActivity is an activity which holds (1) a drawer that shows a list of categories. And a Fragment that displays a list of items in the category chosen in the drawer.
when a item in the list is clicked, a new DetailActivity is started to show the details.
here starts the problem:
From the DetailActivity when i press Back button, it returns to the MainActivity as it was before clicking the item to show details. That is what I expect. However, when use home button as Up navigation, it starts the MainActivity as if I opened the app again. Not showing the list that was previously being shown.
I read in developer documents that for fragments I have to use: .add(detailFragment, "detail") And .addToBackStack() then commit.
But what am I suppose to add in add(---,"---"). And then how should I use it?!
this is my codes:
the method is the MainActivity that shows the content:
public void refreshDisplay(Context context, View view, String category, int i) {
List<Lesson> lessonByCategory = datasource.findByCategory(category, i);
final ListView lv = (ListView) view.findViewById(R.id.listView);
final ArrayAdapter<Lesson> adapter = new LessonListAdapter(context, lessonByCategory);
lv.setAdapter(adapter);
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick (AdapterView<?> parent, View view, int position, long id) {
Log.i(LOGTAG, "onListItemClick called");
ArrayAdapter<Lesson> m_adapter = adapter;
// get the Lesson object for the clicked row
Lesson lesson = m_adapter.getItem(position);
Intent intent = new Intent(MainActivity.this, LessonDetailActivity.class);
intent.putExtra(".model.Lesson", lesson);
intent.putExtra("isStared", isStared);
startActivityForResult(intent, LESSON_DETAIL_ACTIVITY);
}
});
}
In my LESSON_DETAIL_ACTIVITY that shows the detail content I have this code to enable up navigation for home button:
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// I have some other cases here
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
And finally in the Manifest I used the code below to introduce MainActivity as the parrent of LessonDetailActivity:
<activity
android:name=".LessonDetailActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.MainActivity" />
</activity>
I want the Home button as up navigation to behave like back button so that when its clicked it takes me to the MainActivity as it was before opening the LessonDetailActivity. The code above doesn't do that and every time I press Home in the action bar it starts the MainActivity from scratch.
Could anyone help me with this please?
I also should say that I'm new so I'd appreciate it if the answers were detailed.