Are you looking to add profile picture upload functionality to your Android app using Kotlin? Firebase Storage is a powerful tool that can help you achieve this goal quickly and easily. In this blog post, we'll walk you through the steps for implementing Firebase image uploads for Android Kotlin, specifically for adding profile pictures to your app.
Click here to Watch Video on Youtube
Step 1: Set up Firebase in your Android project
Before you can start uploading images to Firebase Storage, you need to create a Firebase project and add the necessary dependencies to your Android project. Here's how you can do that:
Create a Firebase project in the Firebase console and add an Android app to it.
Add the Firebase SDK to your Android project by following the instructions in the Firebase documentation.
Step 2: Add the necessary UI elements to your app
To enable users to upload their profile pictures, you need to add UI elements to your app. Here are some examples of UI elements you can add:
An ImageView to display the user's current profile picture
A Button to trigger the profile picture upload process
A ProgressBar to indicate the progress of the upload process
Add the following UI elements/codes to your project:
border.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/transparent" />
<stroke
android:width="2dp"
android:color="@color/blue" />
<padding
android:bottom="2dp"
android:left="2dp"
android:right="2dp"
android:top="2dp" />
</shape>
user png |
google png |
color.xml
<color name="blue">#459DE4</color>
<color name="red">#FF0000</color>
<color name="green">#80FF00</color>
build.gradle
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.8.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'com.google.firebase:firebase-auth-ktx:21.3.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
implementation platform('com.google.firebase:firebase-bom:31.5.0')
implementation 'com.google.firebase:firebase-auth-ktx'
implementation 'com.google.android.gms:play-services-auth:20.5.0'
implementation 'com.google.firebase:firebase-firestore:23.0.0'
implementation 'com.google.firebase:firebase-auth:20.0.1'
implementation 'com.github.bumptech.glide:glide:4.12.0'
implementation 'com.google.firebase:firebase-storage-ktx:20.0.0'
implementation 'com.google.firebase:firebase-database:20.0.0'
implementation 'com.soundcloud.android:android-crop:1.0.1@aar'
}
Manifest activity
<activity android:name="com.soundcloud.android.crop.CropImageActivity"
android:theme="@style/Base.Theme.AppCompat"/>
Manifest Permissions
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
themes
build.gradle(project)
dependencies {
classpath 'com.google.gms:google-services:4.3.14'
}
Step 3: Implement the image upload process
Once you have set up Firebase in your Android project and added the necessary UI elements, you can start implementing the image upload process. Here are the steps you can follow:
Create a reference to the Firebase Storage location where you want to store the image.
Use an image picker library to allow the user to select an image from their device.
Upload the selected image to the Firebase Storage location using the reference created in the previous step.
Save the download URL of the uploaded image in Firebase Realtime Database or Firestore, depending on your app's requirements.
Step 4: Display the uploaded profile picture
Now that the user's profile picture is uploaded to Firebase Storage, you can display it in your app. Here are the steps you can follow:
Retrieve the download URL of the user's profile picture from Firebase Realtime Database or Firestore.
Use an image loading library to display the profile picture in the ImageView you added earlier.
Create New Empty Activity and name it as SignInActivity
Add the following codes in your activities
activity_main.xml
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/titleTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/teal_700"
android:text="User Profile"
android:textColor="@color/white"
android:textSize="24sp"
android:textAlignment="center"
/>
<Button
android:id="@+id/logout_button"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/red"
android:text="logout"
android:textColor="@color/white"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:gravity="center"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp">
<ImageView
android:id="@+id/profile_picture_image_view"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_gravity="center"
android:scaleType="centerCrop"
android:src="@drawable/man_pro" />
<ImageView
android:id="@+id/crop"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_gravity="center"
android:scaleType="centerCrop"
android:src="@drawable/circleframe" />
</FrameLayout>
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="39dp"
android:padding="5dp"
android:text="Loading.."
android:textStyle="bold"
android:textSize="20dp"
android:textColor="@color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.031"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/upload"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="@color/green"
android:text="Upload" />
<Button
android:id="@+id/remove"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="16dp"
android:background="@color/teal_200"
android:text="Remove" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
activity_sign_in.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:padding="16dp"
tools:context=".SignInActivity">
<LinearLayout
android:layout_centerInParent="true"
android:layout_marginTop="50dp"
android:background="@drawable/border"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_gravity="center"
android:layout_marginLeft="11dp"
android:layout_marginRight="11dp"
android:background="@color/white"
android:src="@drawable/google" />
<Button
android:id="@+id/signInButton"
android:textAllCaps="false"
android:layout_width="200dp"
android:layout_height="50dp"
android:background="@color/blue"
android:text="Sign up with Google"
android:textColor="@color/white"
android:textSize="20sp" />
</LinearLayout>
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="181dp"
android:text="Click below button to Sign in"
android:textAlignment="center"
android:textSize="40dp" />
</RelativeLayout>
mainActivity.kt
import android.app.Activityimport android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.provider.MediaStore
import android.util.Log
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.bumptech.glide.Glide
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInClient
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
import com.google.android.gms.tasks.Continuation
import com.google.android.gms.tasks.Task
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.ktx.auth
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.ktx.Firebase
import com.google.firebase.storage.FirebaseStorage
import com.google.firebase.storage.StorageReference
import com.google.firebase.storage.UploadTask
import com.google.firebase.storage.ktx.storage
import com.soundcloud.android.crop.Crop
import java.io.File
class MainActivity : Activity() {
private lateinit var mGoogleSignInClient: GoogleSignInClient
private lateinit var mAuth: FirebaseAuth
private val PICK_IMAGE_REQUEST = 1
private lateinit var imageView: ImageView
private lateinit var storageRef: StorageReference
private lateinit var profilePicRef: StorageReference
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.statusBarColor = ContextCompat.getColor(this, R.color.teal_700)
}
mAuth = FirebaseAuth.getInstance()
val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.default_web_client_id))
.requestEmail()
.build()
mGoogleSignInClient = GoogleSignIn.getClient(this, gso)
val textView = findViewById<TextView>(R.id.name)
val auth = Firebase.auth
val user = FirebaseAuth.getInstance().currentUser!!
if (user != null) {
val userName = user.displayName
textView.text = userName
} else {
// Handle the case where the user is not signed in
}
// Inside onCreate() method
val sign_out_button = findViewById<Button>(R.id.logout_button)
sign_out_button.setOnClickListener {
signOutAndStartSignInActivity()
}
imageView = findViewById(R.id.profile_picture_image_view)
val uploadButton = findViewById<Button>(R.id.upload)
// Get a reference to the Firebase Storage location where the profile picture will be stored
storageRef = FirebaseStorage.getInstance().reference
// Get a reference to the current user's profile picture in Firebase Storage
val currentUser = FirebaseAuth.getInstance().currentUser
val userId = currentUser?.uid
profilePicRef = storageRef.child("images/$userId.jpg")
// Check if the user has a profile picture in Firebase Storage
profilePicRef.downloadUrl.addOnSuccessListener { uri ->
// If the user has a profile picture, load it into the ImageView using Glide
Glide.with(this).load(uri).into(imageView)
}.addOnFailureListener { exception ->
// If the user doesn't have a profile picture, do nothing
Log.d("ProfilePic", "Profile picture not available", exception)
}
// Set up the button to open the image picker
uploadButton.setOnClickListener {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "image/*"
startActivityForResult(intent, PICK_IMAGE_REQUEST)
}
val deleteButton = findViewById<Button>(R.id.remove)
deleteButton.setOnClickListener {
if (userId != null) {
storageRef = FirebaseStorage.getInstance().getReference("images/$userId.jpg")
storageRef.delete().addOnSuccessListener {
val databaseRef = FirebaseDatabase.getInstance().getReference("users/$userId")
val updates = HashMap<String, Any?>()
updates["profile_pic_url"] = null
imageView.setImageResource(R.drawable.man_pro)
Toast.makeText(this, "User profile removed successfully", Toast.LENGTH_SHORT).show()
databaseRef.updateChildren(updates).addOnSuccessListener {
// User profile updated successfully
}.addOnFailureListener {
// Failed to update user profile
}
}.addOnFailureListener {
// Failed to delete image file
Toast.makeText(this, "Failed to delete image file", Toast.LENGTH_SHORT).show()
}
}
}
}
private fun signOutAndStartSignInActivity() {
mAuth.signOut()
mGoogleSignInClient.signOut().addOnCompleteListener(this) {
// Optional: Update UI or show a message to the user
val intent = Intent(this@MainActivity, SignInActivity::class.java)
startActivity(intent)
finish()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null && data.data != null) {
// Get the URI of the selected image
val imageUri = data.data
// Start the crop activity
Crop.of(imageUri, Uri.fromFile(File(this.getExternalCacheDir(), "cropped_image.jpg")))
.asSquare().start(this)
Handler().postDelayed({
// Upload the cropped image to Firebase Storage
val file = File(this.getExternalCacheDir(), "cropped_image.jpg")
val uploadTask = profilePicRef.putFile(Uri.fromFile(file))
uploadTask.continueWithTask(Continuation<UploadTask.TaskSnapshot, Task<Uri>> { task ->
if (!task.isSuccessful) {
task.exception?.let {
throw it
}
}
return@Continuation profilePicRef.downloadUrl
}).addOnCompleteListener { task ->
if (task.isSuccessful) {
val downloadUri = task.result
Glide.with(this).load(downloadUri).into(imageView)
} else {
// Handle errors here
Log.d("UploadError", task.exception.toString())
}
}
}, 5000) // 5000 milliseconds = 5 seconds
}
}
private fun loadProfilePicture() {
// Get a reference to the current user's profile picture in Firebase Storage
val currentUser = FirebaseAuth.getInstance().currentUser
val userId = currentUser?.uid
profilePicRef = storageRef.child("images/$userId.jpg")
// Check if the user has a profile picture in Firebase Storage
profilePicRef.downloadUrl.addOnSuccessListener { uri ->
// If the user has a profile picture, load it into the ImageView using Glide
Glide.with(this).load(uri).into(imageView)
}.addOnFailureListener { exception ->
// If the user doesn't have a profile picture, set a default image
imageView.setImageResource(R.drawable.man_pro)
Log.d("ProfilePic", "Profile picture not available", exception)
}
}
}
SignInActivity.kt
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.LinearLayout
import android.widget.Toast
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
import com.google.android.gms.common.api.ApiException
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.GoogleAuthProvider
class SignInActivity : AppCompatActivity() {
companion object {
private const val RC_SIGN_IN = 9001
}
private lateinit var auth: FirebaseAuth
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sign_in)
auth = FirebaseAuth.getInstance()
val currentUser = auth.currentUser
if (currentUser != null) {
// The user is already signed in, navigate to MainActivity
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
finish() // finish the current activity to prevent the user from coming back to the SignInActivity using the back button
}
val signInButton = findViewById<Button>(R.id.signInButton)
signInButton.setOnClickListener {
signIn()
}
}
private fun signIn() {
val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.default_web_client_id))
.requestEmail()
.build()
val googleSignInClient = GoogleSignIn.getClient(this, gso)
val signInIntent = googleSignInClient.signInIntent
startActivityForResult(signInIntent, RC_SIGN_IN)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == RC_SIGN_IN) {
val task = GoogleSignIn.getSignedInAccountFromIntent(data)
try {
val account = task.getResult(ApiException::class.java)
firebaseAuthWithGoogle(account.idToken!!)
} catch (e: ApiException) {
Toast.makeText(this, "Google sign in failed: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
}
private fun firebaseAuthWithGoogle(idToken: String) {
val credential = GoogleAuthProvider.getCredential(idToken, null)
auth.signInWithCredential(credential)
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
val user = auth.currentUser
Toast.makeText(this, "Signed in as ${user?.displayName}", Toast.LENGTH_SHORT).show()
startActivity(Intent(this, MainActivity::class.java))
finish()
} else {
Toast.makeText(this, "Authentication failed", Toast.LENGTH_SHORT).show()
}
}
}
}
Manifest file
android:name=".MainActivity"</activity>
android:exported="false" />
<activity
android:name="com.soundcloud.android.crop.CropImageActivity"
android:theme="@style/Base.Theme.AppCompat" />
<activity
android:name=".SignInActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
Conclusion
In this blog post, we showed you how to implement Firebase image uploads for Android Kotlin specifically for adding profile pictures to your app. By following these steps, you can add profile picture upload functionality to your app quickly and easily. If you have any questions or feedback, feel free to leave a comment below.