Step 6. Create UI to handle the callbacks
In this step, you add a UI fragment to obtain credentials from the user, and code to open that fragment when a callback is received.
The authentication journey in this quick start guide sends the NameCallback
and PasswordCallback
callbacks.
For demonstration purposes, this application uses a DialogFragment
to collect the username and password.
You also add code to populate the callback with the credentials and return it to the server, completing the authentication journey.
Create a UI fragment
-
Navigate to app > res.
-
Right-click layout and select New > Fragment > Fragment (Blank).
-
In the New Android Component dialog, enter the following values, and then click Finish:
-
Fragment Name:
usernamePasswordFragment
-
Fragment Layout Name:
fragment_username_password
-
Source Language:
Java
-
-
Navigate to app > res > layout and open
fragment_username_password.xml
. -
Select and delete the existing
TextView
element that contains the textHello blank fragment
. -
In the Component Tree pane, right-click the FrameLayout component, select Convert FrameLayout to ConstraintLayout, and then click OK.
-
In the Palette pane, from the Text category drag a
Plain Text
input element to the canvas:-
id:
inputUsername
-
text:
Username
-
-
Drag a
Password
element to the canvas:-
id:
inputPassword
-
hint:
Password
-
-
In the Palette pane, from the Button category, drag a
Button
element to the canvas:-
id:
buttonCancel
-
text:
Cancel
-
-
Drag a second
Button
element to the canvas:-
id:
buttonContinue
-
text:
Continue
-
-
Layout the elements on the canvas to your liking.
The following screenshot shows one possibility:
Show fragment_username_password.xml
source
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".usernamePasswordFragment">
<EditText
android:id="@+id/inputUsername"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:ems="10"
android:inputType="text"
android:text="Username"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/inputPassword"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:ems="10"
android:hint="Password"
android:inputType="textPassword"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/inputUsername" />
<Button
android:id="@+id/buttonCancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:text="Cancel"
app:layout_constraintEnd_toStartOf="@+id/buttonContinue"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/inputPassword"
tools:text="Cancel" />
<Button
android:id="@+id/buttonContinue"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="Continue"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/buttonCancel"
app:layout_constraintTop_toBottomOf="@+id/inputPassword" />
</androidx.constraintlayout.widget.ConstraintLayout>
Configure the fragment code
-
Open
usernamePasswordFragment.java
For example,
. -
Update the class to extend
DialogFragment
rather thanFragment
, which makes opening and closing the fragment easier:public class usernamePasswordFragment extends DialogFragment {
-
Add import statements for
androidx.fragment.app.DialogFragment
:import androidx.fragment.app.DialogFragment;
-
Within the
usernamePasswordFragment
class, initialize required variables:private MainActivity listener; private Node node;
-
Update the
newInstance
method to accept a node object as its only parameter:public static usernamePasswordFragment newInstance(Node node) { usernamePasswordFragment fragment = new usernamePasswordFragment(); Bundle args = new Bundle(); args.putSerializable("NODE", node); fragment.setArguments(args); return fragment; }
-
Insert an
onResume()
method below thenewInstance()
method. This correctly sizes the fragment dialog when displayed:@Override public void onResume() { super.onResume(); ViewGroup.LayoutParams params = getDialog().getWindow().getAttributes(); params.width = ViewGroup.LayoutParams.MATCH_PARENT; params.height = ViewGroup.LayoutParams.WRAP_CONTENT; getDialog().getWindow().setAttributes((android.view.WindowManager.LayoutParams) params); }
-
Delete the
onCreate()
function. -
Update the
onCreateView
method to capture the values from the fields in the fragment and populate the callbacks the node returned:@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_username_password, container, false); node = (Node) getArguments().getSerializable("NODE"); AppCompatEditText username = view.findViewById(R.id.inputUsername); AppCompatEditText password = view.findViewById(R.id.inputPassword); Button next = view.findViewById(R.id.buttonContinue); next.setOnClickListener(v -> { dismiss(); node.getCallback(NameCallback.class) .setName(username.getText().toString()); node.getCallback(PasswordCallback.class) .setPassword(password.getText().toString().toCharArray()); node.next(getContext(), listener); }); Button cancel = view.findViewById(R.id.buttonCancel); cancel.setOnClickListener(v -> { dismiss(); }); return view; }
-
Add an
onAttach()
method after theonCreateView()
method. This ensures the fragment is correctly connected to the main activity:@Override public void onAttach(@NonNull Context context) { super.onAttach(context); if (context instanceof MainActivity) { listener = (MainActivity) context; } }
-
Add any missing required import statements:
import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import androidx.annotation.NonNull; import androidx.appcompat.widget.AppCompatEditText; import androidx.fragment.app.DialogFragment; import org.forgerock.android.auth.Node; import org.forgerock.android.auth.callback.NameCallback; import org.forgerock.android.auth.callback.PasswordCallback;
Show completed usernamePasswordFragment.java
source
package com.example.quickstart;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.DialogFragment;
import android.content.Context;
import android.widget.Button;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatEditText;
import org.forgerock.android.auth.Node;
import org.forgerock.android.auth.callback.NameCallback;
import org.forgerock.android.auth.callback.PasswordCallback;
/**
* A simple {@link Fragment} subclass.
* Use the {@link usernamePasswordFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class usernamePasswordFragment extends DialogFragment {
private MainActivity listener;
private Node node;
public usernamePasswordFragment() {
// Required empty public constructor
}
public static usernamePasswordFragment newInstance(Node node) {
usernamePasswordFragment fragment = new usernamePasswordFragment();
Bundle args = new Bundle();
args.putSerializable("NODE", node);
fragment.setArguments(args);
return fragment;
}
@Override
public void onResume() {
super.onResume();
ViewGroup.LayoutParams params = getDialog().getWindow().getAttributes();
params.width = ViewGroup.LayoutParams.MATCH_PARENT;
params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
getDialog().getWindow().setAttributes((android.view.WindowManager.LayoutParams) params);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_username_password, container, false);
node = (Node) getArguments().getSerializable("NODE");
AppCompatEditText username = view.findViewById(R.id.inputUsername);
AppCompatEditText password = view.findViewById(R.id.inputPassword);
Button next = view.findViewById(R.id.buttonContinue);
next.setOnClickListener(v -> {
dismiss();
node.getCallback(NameCallback.class)
.setName(username.getText().toString());
node.getCallback(PasswordCallback.class)
.setPassword(password.getText().toString().toCharArray());
node.next(getContext(), listener);
});
Button cancel = view.findViewById(R.id.buttonCancel);
cancel.setOnClickListener(v -> {
dismiss();
});
return view;
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof MainActivity) {
listener = (MainActivity) context;
}
}
}
Open the fragment when receiving callbacks
-
Open the project’s
MainActivity
class file.For example, app > java > com.example.quickstart > MainActivity.
-
Update the
onCallbackReceived()
method to open the fragment to gather the credentials:@Override public void onCallbackReceived(Node node) { usernamePasswordFragment fragment = usernamePasswordFragment.newInstance(node); fragment.show(getSupportFragmentManager(), usernamePasswordFragment.class.getName()); }
Check point
You have now completed the quick start application.
You added a UI fragment to obtain credentials from the user, and code to open that fragment when the callback is received.
You also added code to populate the callback with the credentials and return it to the server, completing the authentication journey.
In the next step, you test the application by authenticating a user, checking the logs, and then logging out.