Categories
Android

Troubleshooting Common Android Studio Bugs: Tips and Solutions

Here are a few common issues that users face when working with android studio, along with some potential solutions:

1. Gradle sync failed: This error often occurs when android studio is unable to download the required dependencies to build your project. To fix this, try the following steps:

  • Make sure you have a stable internet connection
  • Check your project’s build.gradle file and ensure that all dependencies are properly specified
  • In android studio, go to File > Invalidate Caches / Restart and click on “Invalidate and Restart”
  • If the issue persists, try updating android studio to the latest version

2. Emulator not starting: If your emulator is not starting or is showing an error message, try the following steps:

  • Make sure you have installed the latest version of the android emulator and have set up a virtual device
  • Check that your system has the required hardware and software specifications to run the emulator
  • In android studio, go to Tools > AVD Manager and verify that the virtual device is properly configured and working
  • If the issue persists, try restarting your computer and android studio

3. APK not installing: If you’re having trouble installing your app’s APK on a device or emulator, try the following steps:

  • Make sure your device or emulator is running the same or higher version of android as the one your app is targeting
  • In android studio, go to Build > Build Bundle(s) / APK(s) and select “Build APK(s)” to generate a new APK for your app
  • On your device or emulator, go to Settings > Security and enable “Unknown sources” to allow installation of apps from sources other than the Google Play Store
  • If the issue persists, try restarting your device or emulator and installing the APK again

I hope these suggestions are helpful in resolving your android studio bugs. If the issue persists, you may want to check the android studio community forums or contact the android studio support team for further assistance.

Categories
Android

Custom bar chart for android – tutorial

In our Skenit app, used for gym management, we wanted to improve user experience by providing them with statistics that show monthly users per gym, the number of users per training type, financial reports for every month, etc. By doing this we would give our users more insight into the gyms they own, their employees, their business growth, and income.

One of the best ways to represent this data to users is through data visualization. Data visualization offers an easy approach to observing and analyzing trends, outliers, and patterns in data by utilizing visual components like charts, graphs, and maps. Data visualization is essential to analyze massive amounts of information and allow you to make data-driven decisions that can improve your business even more. 

Using MPAndroidChart for data visualization

For this task, I have chosen MPAndroidChart which allows us to create different kinds of data visualization. This library is easy to use and charts are highly customizable, the library is well documented and there is a vast amount of implementation examples online.

In this tutorial, we will give an example of a bar chart that shows income per month, as shown on the image.

Preparation

1. Import this library into build.gradle (Module: app):

<code class="language-kotlin">dependencies {
     implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
 }</code>

2. Import this library into build.gradle (Project: …):

<code class="language-kotlin">repositories {
    maven { url 'https://jitpack.io' }
}</code>

3. Go to fragment.xml and declare the BarChart view in the layout:

<code class="language-xml">
&lt;com.github.mikephil.charting.charts.BarChart
       android:id="@+id/bar_chart
       android:layout_height="150dp"/>
</code>

Be sure to add layout_height or you won’t be able to see anything because the chart will be collapsed.

The style/look of the chart has to be made programmatically, inside of a fragment or activity.

Stylize bar chart

1. Make a function with a parameter of BarChart type. This method will determine the look of a chart and will initialize it. You can make multiple functions for different styles.

<code class="language-kotlin">private fun initializeBarChart(barChart: BarChart) {}</code>

2. Stylize the chart by adding different parameters. I recommend checking out the documentation for the list of all the parameters you can adjust. You can experiment until you get the look you want. Feel free to copy these parameters if you want the same look we have:

<code class="language-kotlin">private fun initializeBarChart(barChart: BarChart) {
   barChart.description.isEnabled = false
   barChart.axisRight.isEnabled = false
   barChart.animateY(1500)
   barChart.legend.isEnabled = false
   barChart.setTouchEnabled(false)
   barChart.xAxis.setDrawGridLines(false)
   barChart.xAxis.position = XAxis.XAxisPosition.BOTTOM
   barChart.xAxis.setDrawLabels(true)
   barChart.xAxis.granularity = 1F
   barChart.xAxis.valueFormatter = AxisFormatter()
   barChart.axisLeft.isGranularityEnabled = true
   barChart.axisLeft.axisMinimum = 0F
   barChart.axisLeft.valueFormatter = LargeValueFormatter()
}</code>

Don’t worry if AxisFormatter() and LargeValueFormatter() are red, we will solve that next.

Formatters

1. X and Y-axis inputs can only be numbers, we cannot make January an input for the X-axis. That is why we have to make an axis value formatter, and add it as a value formatter for the X-axis (barChart.xAxis.valueFormatter = AxisFormatter()). Axis formatter will change the number on the X-axis (which represents one X-axis entry) to the corresponding name of the month.

Make an inner class that extends IndexAxisValueFormatter(), and override function getAxisLabel(). This function will take input from X-axis (value) in form of a number, and get a string for it from the list of strings (monthLabels). Variable monthLabels will be populated with month names when we start iterating through our data, later in this article.

<code class="language-kotlin">val monthLabels: ArrayList<String> = ArrayList()

inner class AxisFormatter : IndexAxisValueFormatter() {
    override fun getAxisLabel(value: Float, axis: AxisBase?): String {
        val indexOfBar = value.toInt()
        return if (indexOfBar - 1 < monthLabels.size) {
            monthLabels[index - 1]
        } else {
            ""
        }
    }
}</code>

2. We also want to show the value of each bar on top of it. Make another inner class that extends ValueFormatter(), that will take the value of each bar and create a string that can be shown on top of every bar:

<code class="language-kotlin">inner class IntValueFormatter : ValueFormatter() {
    override fun getFormattedValue(value: Float): String {
        return value.toInt().toString()
    }
}</code>

Create bar chart

1. Create a function that will take data and show it in form of a bar chart. This function will take a map of String and Int, where String stands for the name of the month and Int for the income amount for that month:

<code class="language-kotlin">private fun createIncomePerMonthChart (Map<String, Int>) {}</code>

2. Declare two variables. The first variable (barChartValues) will be the list of all the bar chart entries (containing X and Y-axis values) and the second one (xAxisBarEntry) will just increment after each entry creating X-axis entry values for the first variable (barChartValues).

Function forEach will iterate through data and for each entry it will add a new BarEntry to the list, using data of each month as Y-axis values, and xAxisBarEntry value for X-axis values. It will also store the names of months in the variable monthLabels, and the AxisFormatter that we made earlier will use that variable/list to swap X-axis values with the corresponding month names.

<code class="language-kotlin">private fun createIncomePerMonthChart (incomePerMonth:  Map<String, Int>) {
    val barChartValues: ArrayList<BarEntry> = ArrayList()
    var xAxisBarEntry = 0F

        incomePerMonth.forEach { month ->
                yAxisValues.add(BarEntry(xAxisBarEntry, month.value.toFloat()))
                monthLabels.add(month)
                xAxisBarEntry++
            }
}</code>

3. Now we will pass the data to the BarDataSet (set of bars in this chart). We also have to choose a color for the bars, and in this case, we used the same color for all the bars. We also added value formatter (IntValueFormatter()) to the barDataSet so that we can see numbers on top of the bars:

<code class="language-kotlin">private fun createChart (incomePerMonth:  Map<String, Int>) {
…
val barDataSet = BarDataSet(barChartValues, "")
barDataSet.color = getColor(requireContext(), R.color.orange)
barDataSet.valueFormatter = IntValueFormatter()

val data = BarData(barDataSet)
binding.bcIncomePerMonthsEUR.data = data
binding.bcIncomePerMonthsEUR.invalidate()
}</code>

4. Initialize the bar chart in onViewCreated method. This will assign the defined style to the view from the layout:

<code class="language-kotlin">initializeBarChart(binding.barChart)</code>

5. Finally, create the bar chart by providing data to the createChart method. This method should be also called in onViewCreated method below/after the initializeBarChart method:

<code class="language-kotlin">createChart(data)</code>
Categories
Android

How to update fragments in the ViewPager?

Five indubitably easy steps

The problem described in the title below is found to be a very common problem Android developers face. As being so, I decided that is the problem I want to dedicate my first blog post to. In this quick read, I will show and explain to you a very simple solution.

When I was first encountered with this implementation, I failed to find much regarding it while searching the internet. What was offered in response either sounded like a very poor implementation or was quite complicated to deal with.

At that time I was on a project where I had to use EventBus library, and it proved to be a very good solution, with not so demanding implementation. That is why I decided to approach the solution in this way.

A sample project for this tutorial can be found on Github repository: https://github.com/Crystal-Pigeon/photo-manipulation.

To start the implementation, we need to add the following line of code to the build.gradle file:
<code class="language-kotlin">
implementation 'org.greenrobot:eventbus:3.2.0'
</code>

We start from the fact that we have one activity, in my case that was MainActivity. It contains a ViewPager with 3 tabs, which means that we have 3 fragments and a field for entering a query, which is going to be used for the search of the data from the aforementioned fragments. To display the data each of the fragments contains a RecyclerView.

Then we need to create an event that will be observed. We create a SearchEvent class that includes the following:
<code class="language-kotlin">
class SearchEvent(var query: String, var pageTitle: CharSequence?)
</code>
The next step is to create a Publisher that will broadcast the event itself.

In this case, the event is broadcasted every time something is entered in the search field. First, we create an object of class SearchEvent and broadcast the event to which we forward the created object.

<code class="language-kotlin">
et_search.doAfterTextChanged {
      val searchEvent = SearchEvent(
            et_search.text.toString(),
            searchViewPagerAdapter.getPageTitle(vp_search.currentItem)
      )
      EventBus.getDefault().post(searchEvent) //publish the event
}
</code>
Moving forward, it is necessary to register the Bus in fragments.

We will do this by overriding the onStart and onStop methods as follows:

<code class="language-kotlin">
override fun onStart() {
    super.onStart()
    if(!EventBus.getDefault().isRegistered(this)) {
        EventBus.getDefault().register(this)
    }
}

override fun onStop() {
    super.onStop()
    EventBus.getDefault().unregister(this)
}
</code>
In addition to this, it is necessary to create a Subscriber.

Specifically, we have to create a method that needs to be annotated with Subscribe, which, as a parameter takes in an object of the SearchEvent class, that we previously created. This method is executed after the broadcast of each event.

<code class="language-kotlin">
@Subscribe(threadMode = ThreadMode.MAIN)
fun onSearchEvent(searchEvent: SearchEvent) {
    // add here what needs to happen, for example display data on the screen
}
</code>

I hope you found this solution-based summary useful, and good luck with the forthcoming work.

Categories
Android

Photo manipulation using Kotlin

How to simplify it

Adding photos from a gallery or camera has become one of the most popular features in Android nowadays. However, to me, when I was a junior developer starting a project from scratch, after researching existing articles on the web, the topic has seemed quite tricky. Having this in mind, I wanted to try and simplify this subject as much as it was possible.

A sample project for this tutorial can be found on Github repository: https://github.com/Crystal-Pigeon/photo-manipulation.

So, if you are someone who has come across this project demand by using Kotlin programming language, keep on reading. I hope I succeeded in making it easier for you.

Project Setup

First of all, to enable manipulating with photos, we need to add next two lines in our AndroidManifest.xml file, in order to have access to internal and external storage on our mobile device:

<code class="language-xml">
&lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
&lt;uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
</code>

In my project, I have one activity named MainActivity, and one fragment named PhotoFragment. In the fragment’s layout resource file, it is important to have an ImageView that will display the selected image.

Implementation

So now we have an ImageView with id iv_photo (the name is optional), and button with id btn_change_photo,  which is important so that we can access it from code.

In our fragment, it is important to have two request codes, based on which we can make a difference whether the user has picked a photo or captured a photo. I’ll explain this in more detail later in the text.

<code class="language-kotlin">
private val PICK_IMAGE = 1
private val TAKE_PHOTO = 2
</code>

To display a dialog that allows us to choose whether we want to pick an existing image from the gallery or we want to take the picture, we need to add a click listener to our Button in the PhotoFragment. Then, we create an array of options that we want to offer to the user – he can choose to pick a photo from the gallery or to take the photo. When the user clicks on one of those two items, the variable holds information about which option is clicked and based on that information, it calls the appropriate method. 

<code class="language-kotlin">
btn_change_photo.setOnClickListener {
   val items = arrayOf("Pick photo", "Take photo")
   AlertDialog.Builder(context)
       .setTitle("Pick from:")
       .setItems(items) { dialog: DialogInterface?, which: Int ->
           when (which) {
               0 -> this.pickPhoto(PICK_IMAGE)
               1 -> context?.let { context -> takePhoto(TAKE_PHOTO, context) }
           }
       }
       .show()
}
</code>

If the user selects the Pick photo option, method pickPhoto is called and PICK_IMAGE request code is being sent as an argument.

<code class="language-kotlin">
private fun pickPhoto(code: Int) {
   Intent(
       Intent.ACTION_PICK,
       MediaStore.Images.Media.EXTERNAL_CONTENT_URI
   ).apply {
       startActivityForResult(Intent.createChooser(this, getString(R.string.pick_image)), code)
   }
}
</code>

In pickPhoto method, we work with Intents. Intents are the way components communicate with each other. It opens the Gallery app and allows the user to pick a photo, after which we create Intent with action ACTION_PICK. Then we call startActivityForResult, in which we pass created intent and request code. This will allow executing the rest of our code once the photo is picked from the gallery. 

After the user is done with picking a photo from the gallery, the method onActivityResult is called in our fragment.

<code class="language-kotlin">
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
   if (requestCode == PICK_IMAGE && resultCode == Activity.RESULT_OK) {
       if (data != null) {
           val imageUri = data.data
           if (imageUri != null) {
               cropRequest(imageUri)
           }
       }
super.onActivityResult(requestCode, resultCode, data)
   }
</code>

This is where our previously created PICK_IMAGE request code comes to rescue. Here we check if the user has picked an image from the gallery. We can access image uri by calling data.data field, and after that, we call cropRequest method, in which we pass image uri. 

If you don’t want to implement cropping, this is where you can set picked image to ImageView. 

In order to cropping images work as expected, first we need to add dependency to build.gradle (app):

<code class="language-kotlin">
//Image cropper library
implementation 'com.theartofdev.edmodo:android-image-cropper:2.7.0'
</code>

… and CropImageActivity to our AndroidManifest.xml:

<code class="language-xml">
&lt;activity
   android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
   android:theme="@style/Base.Theme.AppCompat">
</code>

The reason why i chose to use this Image cropper library is to avoid problem with automatic photo rotation which occurs on certain Android devices.

In ProfileSettingsFragment, we must create a cropRequest method which receives image uri.

<code class="language-kotlin">
private fun cropRequest(uri: Uri) {
   CropImage.activity(uri).setCropMenuCropButtonTitle("Select")
       .setGuidelines(CropImageView.Guidelines.ON)
       .setMultiTouchEnabled(true)
       .start(activity as MainActivity)
}
</code>

CropImageActivity allows users to crop images as they want. After the user finishes cropping, MainActivity is being called, which is why we need the onActivityResult method in MainActivity also – to handle the cropped image. Here, we check if the user is finished with cropping the image, and then we process the image. To display the cropped image in PhotoFragment, we call the method from fragment and pass selected images uri.

<code class="language-kotlin">
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
   if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE) {
       val result = CropImage.getActivityResult(data)
       val inputStream: InputStream
       if (resultCode == Activity.RESULT_OK) {
           try {
               contentResolver.openInputStream(result.uri)?.let {
                   inputStream = it
                   result.uri?.let {
                       val fragment =
                           nav_host_fragment.childFragmentManager.fragments[0] as PhotoFragment
                       fragment.addPhoto(it)
                   }
               }
           } catch (e: IOException) {
               e.printStackTrace()
               Toast.makeText(this, R.string.unable_to_open_image, Toast.LENGTH_SHORT).show()
           } catch (ex: Exception) {
               ex.printStackTrace()
           }
       }
   }
   super.onActivityResult(requestCode, resultCode, data)
}
</code>

To display uri inside ImageView, we can use Glide library, which we should add in build.gradle (app)

<code class="language-kotlin">
//Glide
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
</code>

Method for displaying photo in ImageView looks like follows:

<code class="language-kotlin">
fun addPhoto(uri: Uri) {
   context?.let {
       Glide.with(it)
           .load(uri).circleCrop()
           .into(iv_photo)
   }
}
</code>

That is the full process of picking an image from the gallery explained. But, what if the user wants to take a photo using the camera? Let’s go back to our dialog in which the user can choose to pick a photo from the gallery or capture one using a camera. If he chooses a second option, the method takePhoto is being called, and we pass our request code and context.

<code class="language-kotlin">
private var currentPhotoPath: String? = null
private fun takePhoto(code: Int, context: Context) {
   Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
       takePictureIntent.resolveActivity(context.packageManager)?.also {
           val photoFile: File? = try {
               createImageFile(context)
           } catch (ex: IOException) {
               null
           }
           photoFile?.also {
               val photoURI: Uri = FileProvider.getUriForFile(
                   context,
                   "com.crystalpigeon.photo_manipulation_app.fileprovider",
                   it
               )
               takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
               startActivityForResult(takePictureIntent, code)
           }
       }
   }
}

private fun createImageFile(context: Context): File {
   val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
   val storageDir: File? = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
   return File.createTempFile(
       "JPEG_${timeStamp}_",
       ".jpg",
       storageDir
   ).apply {
       currentPhotoPath = absolutePath
   }
}
</code>

In the takePhoto method, we create an Intent that will open the Camera application for taking the photo. Once a user takes the photo, onActivityResult method in our fragment is being called. But, before that, when the user takes the photo, for that photo we need to create file, in order to have access to the original photo, and not just the thumbnail. For this to work, we need to add file_paths.xml file in res -> xml folder, in which we specify root path and external path for our file.

File_paths.xml:

<code class="language-xml">
&lt;xml version="1.0" encoding="utf-8"?>
&lt;paths>
   &lt;root-path name="root" path="." />
   &lt;external-files-path name="my_images" path="Android/data/com.crystalpigeon.photo_manipulation_app/files/Pictures" />
&lt;/paths>
</code>

It is important to notice that the path variable should have your application’s package name.

Also, we must add the next few lines of code to AndroidManifest.xml. But keep in mind that here you also have to specify your package name:

<code class="language-xml">
&lt;provider
   android:name="androidx.core.content.FileProvider"
   android:authorities="com.crystalpigeon.photo_manipulation_app.fileprovider"
   android:exported="false"
   android:grantUriPermissions="true">
   &lt;meta-data
       android:name="android.support.FILE_PROVIDER_PATHS"
       android:resource="@xml/file_paths"/>
&lt;/provider>
</code>

Back to our onActivityResult method in PhotoFragment. Here we only need to add one more condition in order to proceed with the photo taken using the camera. While the file was being created in the createImageFile method, we saved absolutePath in our variable currentPhotoPath. Here, we just check if that currentPhotoPath contains some value, and if it does, we create File using that path, we create uri from file and call the cropRequest method which we pass created uri. This is our onActivityResult method now:

<code class="language-kotlin">
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
   if (requestCode == PICK_IMAGE && resultCode == Activity.RESULT_OK) {
       if (data != null) {
           val imageUri = data.data
           if (imageUri != null) {
               cropRequest(imageUri)
           }
       }
   } else if (requestCode == TAKE_PHOTO && resultCode == Activity.RESULT_OK) {
       if (currentPhotoPath != null) {
           val myFile = File(currentPhotoPath as String)
           val imgUri = Uri.fromFile(myFile)
           cropRequest(imgUri)
       }
   }
   super.onActivityResult(requestCode, resultCode, data)
}
</code>

Everything else remains the same – the cropRequest method calls CropImage activity. After the user finishes cropping, the onActivityResult method in MainActivity is called, and there we process the cropped image and send it back to PhotoFragment.

And that’s it. I hope someone finds this article useful for working with images in future Android projects.