Changing Profile Image in Open Event Android

A user can create a custom Avatar in Open Event Android. We use Picasso Library for easy image loading and caching in Open Event Android. This blog post will help you understand how its implemented in Open Event Android.

These are the steps we follow :

  • Create UI element which allows the user to pick an avatar
  • Checking and requesting Read External Storage permission
  • Compress and Encode Image
  • Use Picasso to display image on UI
  • Upload it on the server on user’s command

We will discuss the above steps in detail in the following section.

Create UI element which allows the user to pick an avatar

To display the selected avatar image as well as to fire image pick action when clicked we need a UI element. We display a placeholder in case no avatar image is provide, in this case, its ic_account_circle_grey_24dp which lies in drawable folder. Rest of the code is self explanatory.

<ImageView
       android:id=“@+id/profilePhoto”
       android:layout_width=“@dimen/item_image_view_large”
       android:layout_height=“@dimen/item_image_view_large”
       android:layout_gravity=“center_horizontal”
       android:contentDescription=“Profile Photo”
       android:padding=“@dimen/padding_large”
       app:srcCompat=“@drawable/ic_account_circle_grey_24dp” />

Once we have created the UI we need to wire it with the action to pick an image, the following code snippet does that. When this ImageView is clicked permissionGranted variable is checked which keeps a track of read external storage permission if this is true file chooser is displayed to allows user to select an image else an dialog for requesting permissions is shown.

rootView.profilePhoto.setOnClickListener { v ->
           if (permissionGranted) {
               showFileChooser()
           } else {
               requestPermissions(READ_STORAGE, REQUEST_CODE)
           }
       }

Checking and requesting Read External Storage permission

As discussed in the above section permission check is performed when ImageView is clicked and if not granted a dialog request for read external storage is displayed. Initially, the boolean permissionGranted is set to false which means clicking on ImageView fires requestPermission method with permissions array and request code and by overriding the onRequestPermissionResult method we can look at request status.

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
       if (requestCode == REQUEST_CODE) {
           if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
               permissionGranted = true
               Toast.makeText(context, “Permission to Access External Storage Granted !”, Toast.LENGTH_SHORT).show()
               showFileChooser()
           } else {
               Toast.makeText(context, “Permission to Access External Storage Denied :(“, Toast.LENGTH_SHORT).show()
           }
       }
   }

onRequestPermissionsResult is called everytime requestPermission finishes, to check for which request it’s called we can check the Request code if it matches with the one we passed in our requestPermissions method that means the grant result carries information about our request. We can check the grantResult and if permission is granted we set our permissionGranted variable to true and display the file chooser menu.

Compress and Encode Image

Once the user picks the image from the file chooser we need to convert it into an input stream and we encode it into our JPEG format and specify the image quality. The following code snippet will help you understand how this happens.

val imageUri = intentData.data
           var imageStream: InputStream? = null
           try {
               imageStream = activity?.contentResolver?.openInputStream(imageUri)
           } catch (e: FileNotFoundException) {
               Timber.d(e, “File Not Found Exception”)
           }
w
           val selectedImage = BitmapFactory.decodeStream(imageStream)
           encodedImage = encodeImage(selectedImage)

           Picasso.get()
                   .load(imageUri)
                   .placeholder(R.drawable.ic_person_black_24dp)
                   .transform(CircleTransform())
                   .into(rootView.profilePhoto)
       }

The above code is executed after picking the image from file chooser. The image is first converted into input stream (this process is put inside a try catch because the conversion process may result in file not found exception) and then BItmapFactory’s decodeStream is called on it, all this is done so that we can convert URI into to bitmap now this bitmap is converted into JPEG format with 100 percent quality and then into a Base64 type string using the function encodeImage.

Finally, we use Picasso standard functions to load the image into UI we also specify placeholder image which is displayed if anything fails or image is loading.

Upload Image on the server on user’s command

If the user wish to keep the selected image as its avatar he can click on update button in open event android. This button fires a post request which uploads the image data to open event server followed by a patch request to add the URL to User information.

@POST(“upload/image”)
   fun uploadImage(@Body uploadImage: UploadImage): Single<ImageResponse>

This is the post request made to the server the body contains an UploadImage object which essentially caries a String type data variable containing the Base64 encoded jpeg bitmap. In response to the request, the server returns a URL which is then added to the User object and a patch request is made to update the user on the server

@PATCH(“users/{id}”)
   fun updateUser(@Body user: User, @Path(“id”) id: Long): Single<User>

References