In medical terms, consent is a single or multi paged form signed by a participant prior to a medical procedure or survey to confirm that he or she agrees to the procedure and is aware of any risks that might be involved in survey/study.
Getting Started
Here, we will discuss how to develop an android consent app that lets the participant get information about the survey/study and agree or disagree to it at the end. The content of the consent would come from json file. Hence, if the content or the number of pages in the consent changes, the developer need not put in extra effort to change it.
1. Creating New Project
Create a new project in Android Studio from File ⇒ New Project. When it prompts you to select default activity, select Empty Activity and proceed. Name it as MainActivity. Add following code to the MainActivity.
package com.appliedinformatics.android.consentapp; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import com.appliedinformatics.android.consentapp.ConsentActivity; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i(Constants.TAG, "I am in the Main Activity"); startConsent(); } public void startConsent(){ /*Calling the Consent Activity*/ Log.i(Constants.TAG, "Starting Consent Activity"); Intent intent = new Intent(MainActivity.this, ConsentActivity.class); startActivityForResult(intent, Constants.REQUEST_CODE_CONSENT); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == Constants.REQUEST_CODE_CONSENT && resultCode == RESULT_OK) { Log.d(Constants.TAG, "Consent Completed Successfully"); } super.onActivityResult(requestCode, resultCode, data); } } |
Add another activity and call it ConsentActivity. Consent Activity would extend from an abstract activity called ConsentBrain. Create two new classes and call it ConsentBrain and DynamicFragments. ConsentBrain contains the xml layout file called activity_consent.
Replace the contents of ConsentBrain, ConsentActivity, DynamicFragments and activity_consent with the following code.
package com.appliedinformatics.android.consentapp; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.support.annotation.Nullable; import android.util.Log; import com.appliedinformatics.android.consentapp.Constants; import com.appliedinformatics.android.consentapp.MainActivity; import com.appliedinformatics.android.consentapp.R; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; public class ConsentActivity extends ConsentBrain { boolean require_name; boolean require_signature; static String TAG = "ConsentApp"; private static String DATA_JSON_PATH; SharedPreferences sharedPreferences; public ConsentActivity(){ DATA_JSON_PATH = consentJSON.json; Log.i(Constants.TAG, "JSON Path "+ DATA_JSON_PATH); } @Override public void init(@Nullable Bundle savedInstanceState) { String json = loadJSONFromAsset(getApplicationContext(), DATA_JSON_PATH); sharedPreferences = getSharedPreferences(Constants.SHARED_PREF_NAME, MODE_MULTI_PROCESS); Log.i(Constants.TAG, "Json \n"+json); try { parseJson(json); } catch (JSONException e) { e.printStackTrace(); } } private void parseJson(String json) throws JSONException { SharedPreferences.Editor editor = sharedPreferences.edit(); JSONObject jsonresponse = new JSONObject(json); String first_name = jsonresponse.getString("first_name"); String last_name = jsonresponse.getString("last_name"); String description = jsonresponse.getString("description"); String title = jsonresponse.getString("title"); String institute = jsonresponse.getString("institute"); String email = jsonresponse.getString("email"); String trial_id = jsonresponse.getString("id"); editor.putString(Constants.SHARED_PREF_TRIAL_ID, trial_id); editor.commit(); JSONObject consentObject = jsonresponse.getJSONObject("consent"); require_name = consentObject.getBoolean("require_name"); require_signature = consentObject.getBoolean("require_signature"); JSONArray consent_sections = sortJsonArray(consentObject.getJSONArray("sections")); addSlide(R.layout.consent_layout, title, description, "" ); for (int i = 0; i< consent_sections.length(); i++){ JSONObject result = consent_sections.getJSONObject(i); String type = result.getString("type"); String page_title = result.getString("title"); String short_description = result.getString("short_description"); String long_description = result.getString("description"); int order = result.getInt("order"); addSlide(R.layout.consent_layout, page_title, short_description, long_description ); } } @Override public void onDisagreePressed() { AlertDialog.Builder alertDialog = new AlertDialog.Builder(this); //Setting Dialog Logo //alertDialog.setIcon(R.drawable.bermuda_icon); // Setting Dialog Title alertDialog.setTitle("Disagree"); // Setting Dialog Message alertDialog.setMessage("You will not be able to proceed if you disagree. Press OK to continue."); // Setting Positive "Yes" Button alertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { // Write your code here to invoke Agree event Intent intent = new Intent(getApplicationContext(), MainActivity.class); startActivity(intent); } }); // Setting Negative "NO" Button alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } }); // Showing Alert Message alertDialog.show(); } @Override public void onAgreePressed() { AlertDialog.Builder alertDialog = new AlertDialog.Builder(this); //Setting Dialog Logo //alertDialog.setIcon(R.drawable.bermuda_icon); // Setting Dialog Title alertDialog.setTitle("Review"); // Setting Dialog Message alertDialog.setMessage("Do you agree with the study terms and give your consent for the study"); // Setting Positive "Yes" Button alertDialog.setPositiveButton("Agree", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { if (require_name || require_signature ) loadConsentConfirmation(false); else loadConsentConfirmation(true); // Write your code here to invoke Agree event } }); // Setting Negative "NO" Button alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { // Write your code here to invoke NO event // Toast.makeText(getApplicationContext(), "You clicked on Cancel", Toast.LENGTH_SHORT).show(); dialog.cancel(); } }); // Showing Alert Message alertDialog.show(); } @Override protected void onCancelPressed() { startActivity(new Intent(this, MainActivity.class)); } @Override public void onDotSelected(int index) { super.onDotSelected(index); } public static String loadJSONFromAsset(Context context, String fileName) { String json = null; try { InputStream is = context.getAssets().open(fileName); int size = is.available(); byte[] buffer = new byte[size]; is.read(buffer); is.close(); json = new String(buffer, "UTF-8"); } catch (IOException ex) { Log.d(TAG, "Exception Occurred : " + ex.getMessage()); return null; } return json; } public void loadConsentConfirmation(boolean show_thanks){ Intent intent = new Intent(this, ConsentConfirmation.class); intent.putExtra(Constants.REQUIRE_NAME, String.valueOf(require_name)); intent.putExtra(Constants.REQUIRE_SIGN, String.valueOf(require_signature)); intent.putExtra(Constants.SHOW_THANKS, String.valueOf(show_thanks)); startActivity(intent); } public JSONArray sortJsonArray(JSONArray jsonArray){ JSONArray jsonArr = jsonArray; JSONArray sortedJsonArray = new JSONArray(); List<JSONObject> jsonValues = new ArrayList<JSONObject>(); for (int i = 0; i < jsonArr.length(); i++) { try { jsonValues.add(jsonArr.getJSONObject(i)); } catch (JSONException e) { e.printStackTrace(); } } Collections.sort( jsonValues, new Comparator<JSONObject>() { //You can change "Name" with "ID" if you want to sort by ID private static final String KEY_NAME = "order"; @Override public int compare(JSONObject a, JSONObject b) { Integer valA = null; Integer valB = null; try { valA = (Integer) a.get(KEY_NAME); valB = (Integer) b.get(KEY_NAME); } catch (JSONException e) { //do something } return valA.compareTo(valB); //if you want to change the sort order, simply use the following: //return -valA.compareTo(valB); } }); for (int i = 0; i < jsonArr.length(); i++) { sortedJsonArray.put(jsonValues.get(i)); } Log.i(Constants.TAG, "Sorted Array\n"+sortedJsonArray); return sortedJsonArray; } } |
package com.appliedinformatics.android.consentapp; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.view.ViewPager; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.TextView; import com.appliedinformatics.android.consentapp.Constants; import com.appliedinformatics.android.consentapp.R; import java.util.List; import java.util.Vector; public abstract class ConsentBrain extends AppCompatActivity { private PagerAdapter mPagerAdapter; public ViewPager pager; public List<Fragment> fragments = new Vector<>(); private int slidesNumber; private boolean showBack = true; private boolean showAgree = true; static String json_Path; @Override final protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_consent); Intent intent = getIntent(); json_Path = intent.getStringExtra(Constants.JSON_PATH_KEY); final ImageButton backButton = (ImageButton) findViewById(R.id.back); final ImageButton nextButton = (ImageButton) findViewById(R.id.next); final TextView agreeButton = (TextView) findViewById(R.id.agree); final TextView disagreeButton = (TextView) findViewById(R.id.disagree); final TextView cancelButton = (TextView) findViewById(R.id.cancel_action); cancelButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { onCancelPressed(); } }); backButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(@NonNull View v) { pager.setCurrentItem(pager.getCurrentItem() - 1); pager.setVisibility(View.VISIBLE); } }); nextButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(@NonNull View v) { pager.setCurrentItem(pager.getCurrentItem() + 1); pager.setVisibility(View.VISIBLE); } }); agreeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(@NonNull View v) { onAgreePressed(); } }); disagreeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(@NonNull View v) { onDisagreePressed(); } }); mPagerAdapter = new PagerAdapter(super.getSupportFragmentManager(), fragments); pager = (ViewPager) findViewById(R.id.view_pager); pager.setAdapter(this.mPagerAdapter); pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { int a = pager.getCurrentItem(); Log.i(Constants.TAG,"pooooooos---pager"+a); Log.i(Constants.TAG,"pooooooos---pager"+position); if (position == 0){ showBack = false; }else{ showBack = true; } if (position == slidesNumber - 1) { if (showAgree) { nextButton.setVisibility(View.INVISIBLE); agreeButton.setVisibility(View.VISIBLE); disagreeButton.setVisibility(View.VISIBLE); } else { agreeButton.setVisibility(View.INVISIBLE); disagreeButton.setVisibility(View.INVISIBLE); } } else { backButton.setVisibility(View.VISIBLE); agreeButton.setVisibility(View.GONE); disagreeButton.setVisibility(View.GONE); nextButton.setVisibility(View.VISIBLE); } if (!showBack) { backButton.setVisibility(View.INVISIBLE); } } @Override public void onPageScrollStateChanged(int state) { } }); init(savedInstanceState); slidesNumber = fragments.size(); if (slidesNumber == 1) { nextButton.setVisibility(View.GONE); agreeButton.setVisibility(View.VISIBLE); } } public ViewPager getPager() { return pager; } public void addSlide(int layout,String title, String short_text, String long_text) { Log.i(Constants.TAG,title +"\n"+short_text +"\n"+long_text); Fragment fragment = DynamicFrgments.newInstance(layout,title, short_text, long_text,fragments.size()); fragments.add(fragment); mPagerAdapter.notifyDataSetChanged(); } @NonNull public List<Fragment> getSlides() { return mPagerAdapter.getFragments(); } public abstract void init(@Nullable Bundle savedInstanceState); public abstract void onDisagreePressed(); public abstract void onAgreePressed(); protected abstract void onCancelPressed(); public void onDotSelected(int index) { } @Override protected void attachBaseContext(Context newBase) { super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase)); } public static String getJsonPath(){ return json_Path; } } |
package com.appliedinformatics.android.researchdroid.Consent; import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; import com.appliedinformatics.android.consentapp.MainActivity; import com.appliedinformatics.android.consentapp.R; public class DynamicFragments extends Fragment { private static final String ARG_LAYOUT_RES_ID = "layoutResId"; private static final String ARG_LAYOUT_ID_CONSENT = "id_consent"; private static final String ARG_LAYOUT_TITLE_TEXT = "title_text"; private static final String ARG_LAYOUT_SHORT_TEXT = "short_text"; private static final String ARG_LAYOUT_LONG_TEXT = "long_text"; private int layoutResId; private String title_text; private String short_text; private String long_text; TextView consentHeader = null; int incremental_id = 0; LinearLayout linearLayout; public static DynamicFragments newInstance(int layoutResId,String title, String short_text, String long_text,int id) { DynamicFragments addingSlide = new DynamicFragments(); Bundle args = new Bundle(); args.putInt(ARG_LAYOUT_RES_ID, layoutResId); args.putInt(ARG_LAYOUT_ID_CONSENT, id); args.putString(ARG_LAYOUT_TITLE_TEXT, title); args.putString(ARG_LAYOUT_SHORT_TEXT, short_text); args.putString(ARG_LAYOUT_LONG_TEXT, long_text); addingSlide.setArguments(args); return addingSlide; } public DynamicFragments() {} @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); linearLayout = (LinearLayout) getActivity().findViewById(R.id.topPanel); consentHeader = (TextView) getActivity().findViewById(R.id.consentHeading); if(getArguments() != null && getArguments().containsKey(ARG_LAYOUT_RES_ID)) layoutResId = getArguments().getInt(ARG_LAYOUT_RES_ID); if(getArguments() != null && getArguments().containsKey(ARG_LAYOUT_ID_CONSENT)) incremental_id = getArguments().getInt(ARG_LAYOUT_ID_CONSENT); if(getArguments() != null && getArguments().containsKey(ARG_LAYOUT_TITLE_TEXT)) title_text = getArguments().getString(ARG_LAYOUT_TITLE_TEXT); if(getArguments() != null && getArguments().containsKey(ARG_LAYOUT_SHORT_TEXT)) short_text = getArguments().getString(ARG_LAYOUT_SHORT_TEXT); if(getArguments() != null && getArguments().containsKey(ARG_LAYOUT_LONG_TEXT)) long_text = getArguments().getString(ARG_LAYOUT_LONG_TEXT); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View v = inflater.inflate(layoutResId, container, false); View link = v.findViewById(R.id.link_view); final View main = v.findViewById(R.id.consent_main); final View learnmore = v.findViewById(R.id.relativelearmmore); linearLayout = (LinearLayout) getActivity().findViewById(R.id.topPanel); TextView title_text = (TextView) v.findViewById(R.id.consentHeading); title_text.setText(this.title_text); TextView title_text_learnMore = (TextView) v.findViewById(R.id.consentHeading_learnMore); title_text_learnMore.setText(this.title_text); TextView short_text = (TextView) v.findViewById(R.id.consent_body); short_text.setText(this.short_text); TextView long_text = (TextView) v.findViewById(R.id.longtextview); long_text.setText(this.long_text); View link_back = v.findViewById(R.id.learn_moreBack); final LinearLayout buttonStrip = (LinearLayout) getActivity().findViewById(R.id.bottom); if (link !=null){ link.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { learnmore.setVisibility(View.VISIBLE); main.setVisibility(View.GONE); if (linearLayout!=null) linearLayout.setVisibility(View.GONE); buttonStrip.setVisibility(View.GONE); } }); } link_back.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { main.setVisibility(View.VISIBLE); learnmore.setVisibility(View.GONE); linearLayout.setVisibility(View.VISIBLE); buttonStrip.setVisibility(View.VISIBLE); } }); return v; } protected void onCancelPressed() { startActivity(new Intent(getActivity(), MainActivity.class)); } } |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/relativeLayout"> <LinearLayout android:id="@+id/topPanel" android:background="#00000000" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:gravity="bottom" android:layout_alignParentTop="true" android:orientation="horizontal" > <FrameLayout android:background="#00000000" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="5sp" > <Button android:id="@+id/cancel_action" android:layout_width="wrap_content" android:layout_height="wrap_content" style="@style/SectionIdentifier" android:layout_gravity="right" android:minWidth="65dp" android:gravity="center" android:background="@null" android:text="Cancel" fontPath="font/Roboto-Light.ttf" android:textColor="@color/theme_color"/> </FrameLayout> </LinearLayout> <android.support.v4.view.ViewPager android:id="@+id/view_pager" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_below="@+id/topPanel"> </android.support.v4.view.ViewPager> <LinearLayout android:id="@+id/bottom" android:background="#00000000" android:layout_width="match_parent" android:layout_height="40dp" android:gravity="bottom" android:layout_alignParentBottom="true" android:orientation="vertical" > <FrameLayout android:background="#00000000" android:layout_width="fill_parent" android:layout_height="fill_parent" > <FrameLayout android:id="@+id/indicator_container" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_gravity="center" /> <ImageButton android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_gravity="left" android:id="@+id/back" android:minWidth="30dp" style="?attr/borderlessButtonStyle" android:background="@null" android:src="@drawable/backthin" android:layout_marginLeft="90sp" android:visibility="invisible"/> <ImageButton android:layout_width="wrap_content" android:layout_height="fill_parent" style="?attr/borderlessButtonStyle" android:id="@+id/next" android:minWidth="30dp" android:layout_gravity="right" android:background="@null" android:src="@drawable/nextthin" android:layout_marginRight="90sp"/> <Button android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_gravity="left" style="@style/NoButtonStyle" android:minWidth="90dp" android:id="@+id/disagree" android:text="Disagree" android:textColor="@color/theme_color" android:visibility="gone"/> <Button android:layout_width="wrap_content" android:layout_height="fill_parent" style="@style/NoButtonStyle" android:id="@+id/agree" android:layout_gravity="right" android:minWidth="90dp" android:text="Agree" android:textColor="@color/theme_color" android:visibility="gone"/> </FrameLayout> </LinearLayout> </RelativeLayout> |
After this, we will create one more template layout called consent_layout.xml.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout android:layout_height="match_parent" android:layout_width="match_parent" xmlns:android="http://schemas.android.com/apk/res/android"> <RelativeLayout android:layout_height="match_parent" android:layout_width="match_parent" android:id="@+id/consent_main"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="80sp" android:id="@+id/image_layout" android:layout_below="@+id/topPanel"> <ImageView android:layout_height="100sp" android:layout_width="match_parent" android:id="@+id/image" android:scaleType="fitCenter" android:layout_centerHorizontal="true"/> </RelativeLayout> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/linearLayout" android:layout_below="@+id/image_layout" android:layout_marginTop="5sp" android:layout_marginLeft="20sp" android:layout_marginRight="20sp" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" style="@style/Headline" android:text="Heading" android:id="@+id/consentHeading" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" style="@style/Body" android:id="@+id/consent_body" android:text="Body" android:layout_marginTop="5sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" style="@style/LinkStyle" android:text="Learn More" android:id="@+id/link_view" android:layout_marginTop="10sp" /> </LinearLayout> </RelativeLayout> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/relativelearmmore" android:visibility="gone"> <FrameLayout android:background="#00000000" android:id="@+id/learnmoreTopFrame" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" > <Button android:id="@+id/learn_moreBack" android:layout_width="wrap_content" android:layout_height="wrap_content" android:minWidth="65dp" style="@style/SectionIdentifier" android:layout_gravity="right" android:background="@null" android:text="Done" android:gravity="center" fontPath="font/Roboto-Light.ttf" android:textColor="@color/theme_color"/> </FrameLayout> <RelativeLayout android:layout_height="match_parent" android:layout_width="match_parent" android:layout_alignParentBottom="true" android:layout_below="@+id/learnmoreTopFrame" android:layout_marginRight="20sp" android:layout_marginLeft="20sp" android:layout_marginTop="20sp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" style="@style/Headline" android:text="Heading" android:id="@+id/consentHeading_learnMore" /> <android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="5sp" android:layout_marginTop="5sp" android:fadeScrollbars="false" android:layout_below="@+id/consentHeading_learnMore"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="LoremIpsum" style="@style/Body" android:id="@+id/longtextview" android:layout_alignParentTop="true"/> </android.support.v4.widget.NestedScrollView> </RelativeLayout> </RelativeLayout> </RelativeLayout> |
2. Adding JSON file in Assets folder
Right click on app in top hierarchy, go to New, select Folder and click on Assets Folder. You will notice that assets folder would have been added to the project adjacent to res folder. Now right click on assets folder, select new and add a Json file and call it as consentJSON. Replace the contents of this file with following content.
Note: We can change the value but keys must remain same.
{"first_name": "a", "last_name": "a", "consent": {"sections": [{"short_description": "aaa", "order": 0, "type": "overview", "description": "aaaa", "title": "aaa"}, {"short_description": "bbb", "order": 1, "type": "data_gathering", "description": "bbbb", "title": "bbb"}, {"short_description": "ccc", "order": 2, "type": "privacy", "description": "ccccc", "title": "ccc"}], "require_name": true, "require_signature": true}, "description": "a", "title": "a", "institute": "a", "email": "a@a.com", "id": 1} |
ConsentBrain contains viewPager Adapter which actually helps in traversing through the consent pages. We supply consent pages with viewpager that are generated dynamically. From each entry in consentJSON file, we create a fragment page with the help of DynamicFragments class. The pages generated gets added to the fragment stack and then the view pager traverses through the same page stack.
Here’s how the final consent app would look like: