48 Open build.gradle(module:app) and add implementation 'com.google.firebase:firebase-auth:19.3.2' Figure 10.22 build.gradle(module:app) Click Sync Now. Open activity_main.xml and modify the graphical layout shown as in Figure 10.23.
49 Figure 10.23 activity_main.xml The following Figure in 10.24 are used. Figure 10.24 Attributes
50 You may follow the id assign for each component. Figure 10.25 editText and Button id Modify your activity_main.xml by adding Sign Up Now textView. You may follow the following graphical layout. Figure 10.26 activity_main.xml
51 You may follow the id assign for each component. Figure 10.27 editText, Button and textView id The following xml file may help you in managing activity_main.xml layout. <?xml version="1.0" encoding="utf-8"?> <LinearLayout 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:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/bg_wallpaper" android:orientation="vertical" tools:context=".MainActivity"> <TextView android:id="@+id/textView" android:layout_width="250dp" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="30dp" android:fontFamily="@font/bigshot_one" android:gravity="center" android:text="Ready To Get Started?" android:textSize="36sp" /> <EditText android:id="@+id/et_email" android:layout_width="350dp" android:layout_height="50dp" android:layout_gravity="center" android:layout_marginTop="30dp" android:background="@drawable/btn_edtext" android:drawableLeft="@drawable/ic_identity" android:ems="10"
52 android:hint="Email" android:inputType="textEmailAddress" android:paddingLeft="10dp" /> <EditText android:id="@+id/et_password" android:layout_width="350dp" android:layout_height="50dp" android:layout_gravity="center" android:layout_marginTop="5dp" android:background="@drawable/btn_edtext" android:drawableLeft="@drawable/ic_password" android:ems="10" android:hint="Password" android:inputType="textPassword" android:paddingLeft="10dp" /> <androidx.appcompat.widget.AppCompatButton android:id="@+id/btn_login" android:layout_width="300dp" android:layout_height="50dp" android:layout_gravity="center" android:layout_marginTop="40dp" android:background="@drawable/btn_button" android:text="Sign In" android:textAllCaps="false" android:textSize="18sp" android:textStyle="bold" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="5dp" android:orientation="horizontal"> <TextView android:id="@+id/tvreg" android:layout_width="wrap_content" android:layout_height="wrap_content" android:fontFamily="@font/bigshot_one" android:text="Not yet registered ? " android:textSize="16sp" /> <TextView android:id="@+id/tv_register" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:fontFamily="@font/bigshot_one" android:text="Sign Up Now" android:textSize="16sp"
53 android:textStyle="bold" /> </LinearLayout> </LinearLayout> Figure 10.28 activity_main.xml source code The following java source code may help you in managing MainActivity.java. package pmujtmk.nennymelissa.managingcustomizetext; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import android.content.DialogInterface; import android.content.Intent; import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.Task; import com.google.firebase.auth.AuthResult; import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseUser; public class MainActivity extends AppCompatActivity { //step 1 FirebaseAuth mAuth; FirebaseAuth.AuthStateListener mAuthListener; AlertDialog loading_dialog; EditText et_email, et_password; Button btn_login; TextView tv_register; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //this will remove title bar requestWindowFeature(Window.FEATURE_NO_TITLE); this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FUL LSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); //-------//
54 setContentView(R.layout.activity_main); //step 2 mAuth = FirebaseAuth.getInstance(); mAuthListener = new FirebaseAuth.AuthStateListener() { @Override public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) { FirebaseUser user = firebaseAuth.getCurrentUser(); if (user != null) { // User is signed in Log.d(">>>>>>>", "onAuthStateChanged:signed_in:" + user.getUid()); } else { // User is signed out// Log.d(">>>>>>>", "onAuthStateChanged:signed_out"); } } }; //step 4 et_email = findViewById(R.id.et_email); et_password = findViewById(R.id.et_password); btn_login = findViewById(R.id.btn_login); tv_register = findViewById(R.id.tv_register); //step 9 btn_login.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { attemptLogin(); } }); tv_register.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { startActivity(new Intent(MainActivity.this, Registerlog.class)); } }); } //step 5 @Override public void onStart() {
55 super.onStart(); mAuth.addAuthStateListener(mAuthListener); FirebaseUser currentUser = mAuth.getCurrentUser(); if (currentUser != null) { startActivity(new Intent(MainActivity.this, SecondActivity.class)); finish(); } } //step 6 @Override public void onStop() { super.onStop(); if (mAuthListener != null) { mAuth.removeAuthStateListener(mAuthListener); } } //step 10 private void attemptLogin() { String email = et_email.getText().toString().trim(); String password = et_password.getText().toString().trim(); if (email.equals("") || password.equals("")) { promptDialog("", "Please enter your email and password.", "OK"); } else { loading_dialog = promptLoading("Authentication process", "Please wait.."); loginUser(email, password); } } //step 11 private void loginUser(String email, String password) { mAuth.signInWithEmailAndPassword(email, password) .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { Log.d(">>>>>>>", "signInWithEmail:onComplete:" + task.isSuccessful()); if (!task.isSuccessful()) { loading_dialog.dismiss(); Log.w(">>>>>>>>", "signInWithEmail:failed", task.getException()); promptDialog("", task.getException().getMessage(), "OK"); } else { loading_dialog.dismiss(); Intent intent = new
56 Intent(MainActivity.this, SecondActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); finish(); } } }); } //step 7 private AlertDialog promptLoading(String title, String message) { AlertDialog.Builder builder; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { builder = new AlertDialog.Builder(MainActivity.this, android.R.style.Theme_Material_Dialog_Alert); } else { builder = new AlertDialog.Builder(MainActivity.this); } builder.setTitle(title) .setMessage(message); final AlertDialog ad = builder.show(); return ad; } //step 8 private void promptDialog(String title, String message, String button) { AlertDialog.Builder builder; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { builder = new AlertDialog.Builder(MainActivity.this, android.R.style.Theme_Material_Dialog_Alert); } else { builder = new AlertDialog.Builder(MainActivity.this); } builder.setTitle(title) .setMessage(message) .setNegativeButton(button, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }) .show(); } } Figure 10.29 MainActivity.java
57 Open build.gradle(module:app) and add implementation 'com.google.firebase:firebase-database:19.3.1' Figure 10.30 build.gradle(module:app) Click Sync Now and add the following uses-permission in AndroidManifest.xml Figure 10.31 AndroidManifest.xml ** if error occur uninstall the app from the emulator or physical device and then run it again.
58 10.3 Log Out Function Adding menu Directory under res folder. Go to Android Resource Directory and set Directory name menu. Figure 10.32 Android Resource Directory Figure 10.33 New Resource Directory
59 Create file in menu Directory and name it as menu_logout. Figure 10.34 Menu Resource File Figure 10.35 New Resource File
60 Modify menu_logout.xml according to Figure 10.36 Figure 10.36 menu_logout.xml Open SecondActivity.java and modify the source code. package pmujtmk.nennymelissa.managingcustomizetext; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.cardview.widget.CardView; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import com.google.firebase.auth.FirebaseAuth; public class SecondActivity extends AppCompatActivity implements View.OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); CardView card1 = (CardView) findViewById(R.id.card1); card1.setOnClickListener(this); CardView card2 = (CardView) findViewById(R.id.card2); card2.setOnClickListener(this);
61 CardView card3 = (CardView) findViewById(R.id.card3); card3.setOnClickListener(this); } public void onClick(View view){ Intent intent; switch(view.getId()){ case R.id.card1: intent = new Intent(this, Submain1.class); this.startActivity(intent); break; case R.id.card2: intent = new Intent(this,Submain2.class); this.startActivity(intent); break; case R.id.card3: intent = new Intent(this, Submain3.class); this.startActivity(intent); break; } } //step1 @Override public boolean onCreateOptionsMenu(Menu menu) { new MenuInflater(this).inflate(R.menu.menu_logout, menu); return super.onCreateOptionsMenu(menu); } /** * step 2 */ @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { Log.d(">>>>>>", "masuk selected"); switch (item.getItemId()) { case R.id.action_logout: attemptLogout(); break; } return super.onOptionsItemSelected(item); } /** * step 3 */ private void attemptLogout() { AlertDialog.Builder builder1 = new AlertDialog.Builder(SecondActivity.this); builder1.setMessage("Are you sure want to logout?"); builder1.setPositiveButton("Logout", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) {
62 FirebaseAuth.getInstance().signOut(); Intent intent = new Intent(SecondActivity.this, MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(intent); finish(); } }); builder1.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { dialogInterface.dismiss(); } }); builder1.show(); } } Figure 10.37 SecondActivity.java Set the following android:theme in AndroidManifest.xml Figure 10.38 AndroidManifest.xml Run your app in the Android Studio and click Log out on the upper right of the app. Your app shoud return to the login page after logout.
63 10.4 Managing Users Registration On the left pane menu, click build and find Realtime database. Figure 10.39 Project Overview Click Create Database. Figure 10.40 Realtime Database Click Next.
64 Figure 10.41 Set up database I Select Start in Test mode and click Enable. Figure 10.42 Set up database II Your databse console should look like this.
65 Figure 10.43 Realtime Database Create an empty Registerlog activity. In Android Studio project, open activity_registerlog.xml and modify as in Figure 10.44 Figure 10.44 activity_registerlog.xml layout
66 The following xml source code will help you in managing activity_registerlog.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout 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:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".Registerlog"> <ImageView android:id="@+id/logo" android:layout_width="150dp" android:layout_height="150dp" android:layout_gravity="center" android:layout_marginTop="50dp" app:srcCompat="@drawable/logoreg" /> <TextView android:id="@+id/textView10" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="15dp" android:fontFamily="@font/bigshot_one" android:gravity="center" android:text="REGISTER" android:textSize="20sp" /> <EditText android:id="@+id/et_name" android:layout_width="350dp" android:layout_height="50dp" android:layout_gravity="center" android:layout_marginTop="10dp" android:background="@drawable/btn_edtext" android:drawableLeft="@drawable/ic_identity" android:ems="10" android:hint="Username" android:inputType="textPersonName" android:paddingLeft="10dp" /> <EditText android:id="@+id/et_email" android:layout_width="350dp" android:layout_height="50dp" android:layout_gravity="center" android:layout_marginTop="10dp" android:background="@drawable/btn_edtext" android:drawableLeft="@drawable/ic_baseline_email_24" android:ems="10" android:hint="Email"
67 android:inputType="textEmailAddress" android:paddingLeft="10dp" /> <EditText android:id="@+id/et_password" android:layout_width="350dp" android:layout_height="50dp" android:layout_gravity="center" android:layout_marginTop="10dp" android:background="@drawable/btn_edtext" android:drawableLeft="@drawable/ic_password" android:ems="10" android:hint="Password" android:inputType="textPassword" android:paddingLeft="10dp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="10dp" android:orientation="horizontal"> <androidx.appcompat.widget.AppCompatButton android:id="@+id/btn_register" android:layout_width="50dp" android:layout_height="wrap_content" android:layout_marginLeft="30dp" android:layout_marginRight="5dp" android:layout_weight="1" android:background="@drawable/btn_button" android:text="Create Account" android:textStyle="bold" /> <androidx.appcompat.widget.AppCompatButton android:id="@+id/btn_log" android:layout_width="50dp" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_marginRight="30dp" android:layout_weight="1" android:background="@drawable/btn_button" android:text="LOG IN" android:textStyle="bold" /> </LinearLayout> </LinearLayout> Figure 10.45 activity_registerlog.xml
68 The following java source code will help you in managing Registerlog.java package pmujtmk.nennymelissa.managingcustomizetext; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import android.content.DialogInterface; import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.Task; import com.google.firebase.auth.AuthResult; import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseUser; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; public class Registerlog extends AppCompatActivity { /** * step 1 */ FirebaseAuth mAuth; FirebaseAuth.AuthStateListener mAuthListener; DatabaseReference mDatabase; AlertDialog loading_dialog; EditText et_name, et_email, et_password; Button btn_register, btn_log; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_registerlog); //step 2 mAuth = FirebaseAuth.getInstance(); mDatabase = FirebaseDatabase.getInstance().getReference(); mAuthListener = new FirebaseAuth.AuthStateListener() { @Override public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) { FirebaseUser user = firebaseAuth.getCurrentUser(); if (user != null) { // User is signed in Log.d(">>>>>>>", "onAuthStateChanged:signed_in:" + user.getUid()); } else { // User is signed out
69 Log.d(">>>>>>>", "onAuthStateChanged:signed_out"); } } }; //step 3 et_name = findViewById(R.id.et_name); et_email = findViewById(R.id.et_email); et_password = findViewById(R.id.et_password); btn_register = findViewById(R.id.btn_register); btn_log = findViewById(R.id.btn_log); btn_register.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { attempRegister(); } }); btn_log.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { finish(); } }); } private void attempRegister() { String full_name = et_name.getText().toString().trim(); String email = et_email.getText().toString().trim(); String password = et_password.getText().toString().trim(); if (full_name.equals("") || email.equals("") || password.equals("")) { promptDialog("Ops!", "Please enter all information to continue.", "OK"); } else if (password.length() < 6) { promptDialog("Ops!", "Password must be at least 6 characters.", "OK"); } else { loading_dialog = promptLoading("Registration process", "Please wait.."); registerUser(full_name, email, password); } } private void registerUser(final String full_name, final String email, String password) { mAuth.createUserWithEmailAndPassword(email, password) .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() { @Override
70 public void onComplete(@NonNull Task<AuthResult> task) { Log.d(">>>>>>>", "createUserWithEmail:onComplete:" + task.isSuccessful()); if (!task.isSuccessful()) { loading_dialog.dismiss(); promptDialog("", task.getException().getMessage(), "OK"); } else { FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); writeNewUser( full_name, email, "", user.getUid() ); loading_dialog.dismiss(); finish(); } } }); } private void writeNewUser(String full_name, String email, String image_url, String user_id) { User user = new User(); user.setName(full_name); user.setEmail(email); mDatabase.child("users").child(user_id).setValue(user); } //step 5 @Override public void onStart() { super.onStart(); mAuth.addAuthStateListener(mAuthListener); } //step 6 @Override public void onStop() { super.onStop(); if (mAuthListener != null) { mAuth.removeAuthStateListener(mAuthListener); } } //step 7 private AlertDialog promptLoading(String title, String message) { AlertDialog.Builder builder; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { builder = new AlertDialog.Builder(Registerlog.this,
71 android.R.style.Theme_Material_Dialog_Alert); } else { builder = new AlertDialog.Builder(Registerlog.this); } builder.setTitle(title) .setMessage(message); final AlertDialog ad = builder.show(); return ad; } //step 8 private void promptDialog(String title, String message, String button) { AlertDialog.Builder builder; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { builder = new AlertDialog.Builder(Registerlog.this, android.R.style.Theme_Material_Dialog_Alert); } else { builder = new AlertDialog.Builder(Registerlog.this); } builder.setTitle(title) .setMessage(message) .setNegativeButton(button, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }) .show(); }} Figure 10.46 Registerlog.java Resolve error in writeNewUser(…) method , click on the word User and click Alt + enter. Select Create class ‘User’. Click OK.
72 Figure 10.47 Resolve ‘User’ error Figure 10.48 Create Class User Open User.java and add the manage User class. You are able to use Getter and Setter to manage User.java. Right click, choose Generate and select Getter and Setter. Figure 10.49 User.java
73 Figure 10.50 Selecting Fields to Generate Getters and Setters Figure 10.51 User.java
74 Open Firebase console Authentication to see the registered user successfully added in Users. Figure 10.52 Registered User Registered users will be written in the database. Figure 10.53 The data of Registered Users
75 Managing Simple Navigation & Applying Multimedia Application 11.1 Creating Simple Navigation Create Three (3) new activities in your project anda name the activity as Submain1, Submain2 and Submain3. The following activity may display in your packages. Figure 11.1 Project Activity 11.2 Managing Layout Manage your activity_submain1.xml, activity_submain2.xml and activity_submain3.xml layout by using Attributes Pane. The following result may display in your Layout Editor. You also may refer to the xml code to manage the design. <?xml version="1.0" encoding="utf-8"?> <LinearLayout 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:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".Submain1"> <VideoView android:id="@+id/VideoView" android:layout_width="match_parent"
76 android:layout_height="wrap_content" /> </LinearLayout> Figure 11.2 activity_submain1.xml Source Code Apply the same design layout for activity_submain2.xml and activity_submain3.xml. 11.3 Managing raw File Manage the following raw file in Folder Resources (res). Figure 11.3 raw Folder 11.4 Managing Java File The following source code for SecondActivity.java are used to managing the cardView in Second Activity. package pmujtmk.nennymelissa.managingcustomizetext; import androidx.appcompat.app.AppCompatActivity; import androidx.cardview.widget.CardView; import android.content.Intent; import android.os.Bundle; import android.view.View; public class SecondActivity extends AppCompatActivity implements View.OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); CardView card1 = (CardView) findViewById(R.id.card1); card1.setOnClickListener(this);
77 CardView card2 = (CardView) findViewById(R.id.card2); card2.setOnClickListener(this); CardView card3 = (CardView) findViewById(R.id.card3); card3.setOnClickListener(this); } public void onClick(View view){ Intent intent; switch(view.getId()){ case R.id.card1: intent = new Intent(this, Submain1.class); this.startActivity(intent); break; case R.id.card2: intent = new Intent(this,Submain2.class); this.startActivity(intent); break; case R.id.card3: intent = new Intent(this, Submain3.class); this.startActivity(intent); break; } } } Figure 11.4 SecondActivity.java Source Code package pmujtmk.nennymelissa.managingcustomizetext; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.widget.MediaController; import android.widget.VideoView; public class Submain1 extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_submain1); VideoView videoView = findViewById(R.id.VideoView); videoView.setVideoPath("android.resource://"+getPackageName()+"/ "+R.raw.mad); MediaController mediaController = new MediaController(this); mediaController.setAnchorView(videoView);
78 videoView.setMediaController(mediaController); } } Figure 11.5 Submain1.java Source Code Apply the same java source code for Submain2.java and Submain3.java.
79 Hiding Title Bar & Action Bar 12.1 Hide Title Bar You may refer to the following java code to hiding title bar. Figure 12.1 Java Source Code to Hide Title Bar 12.2 Hide Action Bar You may refer to the following method to hide action bar. If you are intended to hide the entire activity in the application you may use theme.xml to manage the action bar whereas if you are intended to hide specific activity in the application you may use AndroidManifest.xml. Figure 12.2 theme.xml
80 Figure 12.3 AndroidManifest.xml You also able to unhide the action bar by managing android:theme in AndroidManifest.xml. The following are example on how to unhide action bar. Figure 12.4 android:theme in AndroidManifest.xml
81 GUIDES 13.1 Guide and Reference The following information may guide you in developing project. The developer is using the following Gradle Version and Android Gradle Plugin Version to managing the project. Figure 13.1 Project Structure If you are intended to use the higher version of Gradle you are able to change the following dependencies. Current dependencies: implementation 'com.google.firebase:firebase-auth:19.3.2' implementation 'com.google.firebase:firebase-database:19.3.1' Change to: implementation 'com.google.firebase:firebase-auth:22.2.0' implementation 'com.google.firebase:firebase-database:20.3.0' Figure 13.2 Build Gradle (Module:app)
82 REFERENCES AB RAHMAN, R. B., AZHAR, U. A. B., & AB RAHMAN, R. B. (2020). Android Apps Development: A Hands-On-Guide for Beginner. ARfidz Ventures. O., & S. (2022, December 8). How to Create a CircularImageView in Android using hdodenhof Library? GeeksforGeeks. Retrieved September 1, 2023, from https://www.geeksforgeeks.org/how-to-create-a-circularimageview-in-androidusing-hdodenhof-library/
1