Write a PDF Reader Application in Android Studio


Hello and welcome to this new Android development course. This is part one on how to make a PDF Reader application for Android.

If you will enjoy reading and contributing to the discussion for this post, will you please join us on the YouTube video above and leave a comment there because I read and respond to most comments on YouTube?

If you find anything helpful in this video or funny, will you please leave a like because you will feel great helping other people find it?

Let’s start. We will open our Android studio IDE and we will click on start a new Android studio project. We will select Empty Activity, we will click on next.

As the name of our application we will call it PDF Reader. We want to make sure the selected language is Java and this time we will use the API 16 in the list. If you have the option to check this use androidx artifacts check it. If you don’t have that option don’t worry, you don’t need it. We will click on finish and we will wait for the IDE to setup our project and create our files for our PDF Reader.

I will be running the application on an emulator. You can do that too or you can also run it on a real Android device. To create an emulator you will go into tools and AVD Manager, and you just create an emulator that fit’s your needs like a screen size and so on.

The project has been created and I want to run the application to check if it is working. I will click on use same selection for future launches, and as you can see as connected device I have my Android PI emulator. I will click on okay and I will wait for the IDE to compile my code to pack it up into an apk and to send it to my emulator.

What we’re going to do here, we’re going to use our external library that will enable us to read PDF files. We’ll be setting up permissions, we will be reading them, we will be listing those files in ListView, and we will be opening them.

See right now, the current state of the app is this with this tiles that are visible PDF Reader title and the Hello World! that is generated by the IDE.

What we want to do is we got to go inside the res folder under the layout folder, and open this activity_main.xml file. This is the file that contains our xml layout. As you can see it has a constraintlayout root element which we don’t want that because we don’t use that, and we need something kind of simpler like a linear layout.

What we’re going to do is we’re going to go to the left side right here, and we will right click on activity_main.xml and we will click on delete. If you have this box checked please uncheck them and click on OK.

The file has been deleted and we have a kind of error right here that says that we cannot resolve this file. That’s okay we will create it again but with a different root element.

To do that we will right click on the layout folder and we will go to new layout, resource file.

Inside here the name of this will be the same as it was previously which is activity_main and as root element make sure you have LinearLayout. That’s all we have to do. We click on okay and now we have our new layout. Right here at the bottom we can switch between design and text mode. If we go to text mode we will see our LinearLayout right here which is ready for us to work on.

If we run the app again just to check that everything is still working we have the same as previously but this time we don’t have the Hello World text because we don’t add it yet and we won’t actually because we don’t need it.

That was just an example.

How Will This App Work?

It will start by showing a ListView of all the files or actually all the PDF files we can find on the device. A ListView is a widget that enables us to have scrolling elements on it, so let’s define it inside linear layout.

We will open an element right here and we’re going to say ListView the width of this element will be match_parent and the height will also be match_parent. We can close this element right here. We have to set this view an ID in order for us to be able to access this view from our Java code.

I’m going to say ID and I’ll call it ListView. We have created the ListView right here. What we want to do next is we want to access that ListView from code, so we got to go to our MainActivity.java file. Inside this as you can see we have an OnCreate method which was set up by the IDE. After we setcontentview(R.layout.activity_main);, we have to initialize our ListView. To do that we’re going to say final ListView – we import, of course, the ListView class – and I’ll call this element ListView, and I will set it to findViewById(R.id.ListView);.

I have accessed my ListView from Java code which is great. What I have to donext is I have to create an adapter – which is a mechanism – is a class that enables us to display content in our ListView.

How Can We Create Our Adapter?

It’s straightforward. We will create that adapter as an inner class. We’re going to create a class inside this MainActivity class. To do that we’re going to type class and I’ll call this class Adapter and this class will extend, so Adapter extends_Base Adapter. This class will extend the base adapter class. What we have to do inside this class we will have to override some method in order for us to be able to use the adapter appropriately. Either if we use them or not.

We wanted to store our data which will be the file paths as strings, so how can we do that? We’re going to say List<string >. Of course we have to import this list class, so List<string> data = new ArrayList<>. I’m going to create a method that will enable me to set data into that list. I’m going to say void setData() and the parameter I will pass will be the list I’m going to set.

Void setData(List<string>)_mData, and this will be called mData in order to avoid confusions with the previous list of strings we created which is called just data.

Inside this what we’re going to do is we’re going to say data.clear because we want to clear anything that was stored previously on the data. Then we’re going to say data data.addAll. We will add all the items of mData into data. We first clear all the values and data and now we’re adding all the values mData contains into just  data. Now, that we have our new information, we have to tell the adapter that we have got this new information, so we have to tell it to display it – to update. We’re going to say notifyDataSetChanged();. That’s our set data method.

Below that we have to override a couple of methods. The first one that we will override will be the getCount method, so we’re going to say override public int getCount() without any parameters. This method will return the number of items that exist on an adapter and on our ListView so what we’re going to return here will be return data.size(). That’s how we return the amount of date. It knows how many cells it needs to write.

I’m going to override a couple of more methods that we won’t really use but we have to override because of how this thing is designed.

We’re going to say override public Object getItem(int position). It will receive an integer called position. If we open this we will just return null because we won’t be using this method.

Next, we have to override the get item ID which again we won’t to be using, so we’re going to say override public long getItemId() and it will again receive a position as an int. This time what we will return will be just 0 because it’s a long. We will just return 0’ because we won’t to be using it.

We have to override the most important method of any adapter which is the getView method. Let’s do it. I have to import the view class. @Override public View getView (int position, view convertView, ViewGroup parent). We will open this method and well start looking at this. We want to create the convert view only when it’s null. In order for this to work more efficiently in our code. Actually, to recycle views so the adapter won’t have to be creating views every time.

It’s kind of a 10x improvement roughly. We’re going to say if (convertView == null). Only when it is equals null, we will inflate the view. To inflate the view we will create a layout inflator, so I got to say LayoutInflator inflator = (Layoutinflator) MainActivity.this – which is the context I’ m working on and on the next line I’m going to say getSystemService(Context.LAYOUT_INFLATOR_SERVICE). That’s how we create a simple layout inflator. We’re going to actually inflate the view in to convert view. To do that we’re going to say convertView = inflater.inflate(R.layout.item, parent, false), but we haven’t defined that item. This is how inflate it but as you can see we have an error here because we haven’t created the item yet.

We have to create the item. How do we create it? We will go inside our layout folder right here. We will right click on it, well click on new, and Layout resource file. The file name will be item. The root element will be Textview because we want to show text. We click on OK and the IDE will create this file.

We switch to text mode and as you can see this is what we have created. We don’t need to extend it too much, so we can do this and delete this. We can improve the way this is ordered. Here I have to set the ID so I’m going to say “@+Id/item”. You can have another ID it doesn’t have to be the same id as the file name. You can have different one but for simplicity reasons I’m just setting it to item. The layout width will be set to match parent but the layout height will be set to how big you want to make. In this case, I’m going to set it to 50dp. I’m going to set the gravity of this to center vertical and I’m going to set the padding to 16dp. These settings are just to make it visually appealing, kind of nice to look at.

If you want to preview how this will look like you click on preview and as you can see we have a white rectangle at the top. This is how big each row of our ListView will be.

That’s it for this item.xml file. We can close it and go back to our MainActivity.java.

As you can see now it finds this item right here.

What we are going to do we have to keep writing our get view method, so the previous lines we’re in the case that the convert view is null. Outside the if statement, we’re going to do the following we’re going to create a Textview textview = convertview.findViewById(R.id.item). That’s the Textview. We are using the convert view saying get this view from the convert view, now we can change the textview text or set the text of the textview. To do that we’re going to say textview.setText in this case, we’re going to set the corresponding file name PDF file name. How do we get them? Well, the corresponding PDF file name is stored on the data array list. We have to do and say (data.get( position). That’s how we update the text on textview and, of course, since this is a view we have to return a view, so we’re going to say return convertView. That’s how we create an adapter. A very, very simple adapter.

We only have a way to set data, get count, this two are ignored because we we’re not using them and the most important one is the getView method. Now that we have an adapter, we have to use it. So how do we use? We go to our OnCreate method and just after we created our ListView object, we’re going to create an adapter object – the adapter that we just made – so an object of the type we just created. To simplify this more we’re going to do that as a global variable so I’m going to set adapter and I’ll call it adapter as a global variable. Here after I initialize the ListView I’m going to say adapter = new Adapter().

Why make this a global variable? Well, because I want to access it from outside the onCreate method right, so I have to make it global in order for me to be able to access it. You can make this private if you want. That’s a good practice too. We already have our new adapter here and now we have to connect the adapter with our ListView. We have to connect it after with ListView so you can say ListView.setAdapter(adapter) because that’s the adapter we want to connect it to which is, of course, great.

We have our ListView ready but we still haven’t set the data.

If we want to set the data a kind of an example data, we will also create that data object as global variable so you can do it private if you want.

Private List<String> data = new ArrayList<>(). When I want to set the data I have to first fill the data so I will create for loop. You’re going to say for(int I = 0; I < 100) if I want 100 items and I’ll set I++. Inside this for loop right here I have to fill data into my data ArrayList, so I got to say data.add (“hello i am” + I);. I am number so when we concatenate a integer with a string the system automatically converts the integer into string, so we don’t have to say string.value or anything like that.

We have a hundred entries on our data and we have to tell the adapter that we have that new data. To do that, we’re going to say adapter.setData(data). We will run the app to check if it is working as we expect. Here it is and here I have my PDF Reader and I have the data inside here.

As you can see the first element says hello I am 0, hello I am 1, hello I am 2, and as I was telling you we are able to scroll through this ListView.

We have all the way up to hello i am 99. That’s basically how we create an array list.

Every entry here we can modify its behavior or whatever the case may be, for example, I want to make the text color a bit darker, so how can I do that?

Well, I’m going to go to our item.xml file right here and I’m going to define a new color. To define a new color I have to go inside the values folder down here and here I’ll open the color.xml file.

Inside here we have a three colors that the IDE generated by default but we want to add another color. We want to create the black color so I am going to say color name=”black”. To define black color in RGB hex we do a number sign and then we follow it with 000000. We have 000000 there and this means black as you can see right here you can see kind of a small preview of the color you’re creating.

Now we have created this black color which can be used from any part of your application. We’re going to go to item.xml file and we’re going to tell the system that we want the text of every cell of every row view to be black. Because we like the black color. We’re going to say textColor=”@color / black”. That’s how we set the text color to black.

If want to check that it worked we’re going to run the app again and check it. As you can see now we have a darker color, a black color and the text is centered vertically as we saw it right here. The pattern which is the space between the borders on the text is 16dp because that looks nice. That’s just design kind of aesthetics but you can change it if you want.

What we’re going to do maybe we say hey, we want to have a cleaner look, we don’t really need this two bars at the top or maybe we want to show only one of them. I will teach you how you can hide them.

I’m going to go to styles.xml under the values folder and here is the theme of our application, so if we want to hide the status bar which is the bar at the top which contains the clock and the battery. We don’t need that for our app. If you decide you don’t need you can leave it if you want but in the case you don’t want it you can say windowFullscreen and we will set it to true. If we run the app it will go in full screen mode so it will hide the top bar.

As you can see the top bar is now hidden. You can do it if you want if not that’s okay. If you want to also hide this bar right here that says PDF Reader because you don’t want that bar maybe, so you can do that too from the theme configuration.

We’re going to create a new item and this time we’re going to say windowNoTitle and we will set that to true.

We will run the app again and we will check if it worked and as you can see here we have our new app without a title.

Right now, we have a clean app that shows a list of the things that we have. In the future this will show the list of the files we still have to ask for permissions and all of that, we will do that on the following videos.

Part Two: How To Make a PDF Reader App for Android.

In the previous tutorial, we learned how we can set up an adapter, a ListView, and we even set some text to ListView starting from hello I am 0 up to hello I am 99.

In this tutorial, we will first start by asking permissions to read the files on the system because we want to be able to read the PDF files and display it to the screen. If we want to access a special permission like reading and writing files, we’re going to go to our manifest.xml file right here and before the application tag, we’re going to define the permissions, our application we will use.

The first permission is users permission READ_EXTERNAL_STORAGE and the next permission will be users permission WRITE_EXTERNAL_STORAGE.

We only need those two permissions and we also will set the screen orientation to portrait to simplify our lives a lot. That’s how we define a permission but now we have to ask those permissions at runtime.

How do we ask those permissions at run time? We will go to our MainAactivity.java file and after the OnCreate method we’re going to override the onResume method so we’re going to type @override protected void onResume(){super.onResume().

What will we do inside the onResume method?

We will check if we don’t have permissions we want to request permissions. Outside the onResume method, we will create another method which will be a boolean method that will be called notPermissions. Let’s do it.

Private boolean notPermissions() – if we done here we don’t have permissions we will ask for them. First of all, we only want to check permissions if we’re running a Marshmallow or newer. So we’re going to say if (Build.VERSION.SDK INT >= Build.VERSION_CODES.M). If it’s greater or equal then Marshmallow, we will check it if it’s not it will just return false.

Inside the if statement or if block right here, we’re going to do the following check. We will create first of all a way or a loop to request and set the permissions, so we have to define the permissions here as global variables.

We’re going to say private static final string[ ] PERMISSIONS = {Manifest.permission.READ EXTERNAL STORAGE, Manifest.permission. WRITE EXTERNAL STORAGE}.

If we happen to use more than these permissions, we can also specify them here but since we’re only using this two storage related permissions we just specify these two.

Now we’re going to also define the permissions counts, so you got toe say private static final int PERMISSIONS_COUNT =2 because we have two permissions.

We also have to define a request permission code that will identify our permission request call to the Android API. To do that we’re going to say again private static final int REQUEST PERMISSIONS. Here we have to set it to any number. I’ll set it to one, two, three, four, just as an example. This is just an identifier.

Inside the notPermissions method we’re going to create an integer inside the if statement. We’re going to create an integer which I will call int permissionPtr which is kind of a provision pointer and I’ll set it to zero and we’re going to say the following. While permissionPtr < PERMISSIONS_COUNT). We’re going to check if we have each permission in the in a while loop fashion, so you’re going to say if( checkSelfPermission(PERMISSIONS[permissionPtr])). If check SelfPermission is different than and in any line for readability I’ll say PackageManager.PERMISSION_GRANTED). So if it’s not granted we’re going to return true which means we don’t have permissions. After this if statement don’t forget to increment the permissions pointers. You can say permissionPtr ++;.

This is the notPermissions method.

To use it, we got to go inside the onResume method right here and we are going to say if(not permissions() – if we don’t have permissions – we will request permissions. How do we request them? You got to say requestPermissions(PERMISSIONS, REQUEST_PERMISSIONS).

If you want you can put these variables before the onResume if you want it to look nicer. It’s not a must. In Java it doesn’t matter where you put it.

Here what we have a sort of warning right here that says call requires API level 23 and the current minimum is 16, but we are already checking that API here.

We can just ignore this. To suppress this we got to say suppress new API annotation because we are already checking it here. We’re only calling it if it’s Marshmallow or greater but lint is not smart enough to know that but we are.

So we set just the suppress lint to suppress that thing.

That will ask for permissions for us. If we run the app lets check if it even works. If we run the app here we have a dialog that says allow PDF Reader to access photos, media, and files on your device?

And if you click on deny it won’t work because we need that, but if you click allow then it will work. The app has permission granted to start reading files.

This is how we check for permissions but now we have to check the result of those permissions because maybe the user allowed the permission or maybe the user denied the permission.

How do we do that?

After the notPermissions method we’re going to override the following methods. We’re going to say @override public void_onRequestPermissionsResult(int requestCode, string [ ] permissions, int [ ] grantResults).

We have those three parameters and inside this we have to call this super method so we’re going to say super.onRequestPermissionsResult(requestCode, permissions, grantResults). We’re going to now check if (requestCode == REQUEST_PERMISSIONS && grantResults.length > 0)

If those two conditions are true we’re going to check again if we don’t have permissions.

You can say if (notPermissions) so if permissions we’re denied what are we going to do? Well, we want to just close the app and clear the app data.

Why do we do this?

Maybe it’s not the most friendly thing. It’s kind of extreme but this enables us to be able to keep asking for permissions every time the user opens the app. Kind of a traitor but useful actually. To do this we will open ((ActivityManager) this.getSystemService (ACTIVITY_SERVICE)).clear ApplicationUserData(). That’s how we clear the data and after this we can call recreate just refresh and close the app and recreate the app. That’s how we do it.

In the case we did have permission so the user has granted permission, we want to tell the system to start loading the data. Loading or reading the data or reading the list of the files and all of that.

We don’t have yet a method to do that so we will create one outside this thing right here which will be called void loadData(), and inside this else method we’re going to call loadData. Inside the onResume method if we don’t have permissions we do this, but if we have permissions, we also want to call loadData.

That’s basically how we request permissions, how we get the result of permissions, and now what we have to do is actually load the data.

Where will we store the data?

Right here we already have it which is a list of strings and that’s the variable that will hold our data. We can delete these lines right here. This for loop and this set data because that was just for testing purposes of the previous tutorial.

What we’re going to do is we’re going to create or make a loadData method.

So how can we do this?

Well, so we got our loadData method. You can make this method private if you want. It’s a good practice. First of all, when we loadData we want to clear the previously loaded data because we don’t want to write two lists to duplicate or many times the same repetition, and all of that, so we don’t want to repeat ourselves. First we’re going to say data.clear() to clear all the data and start over.

We will create a new file, of course, don’t forget to import the file class and we will start reading the downloads folder because that’s a good place in which we can find PDF files. We can extend that to read more than one folder later but for now, we’re going to call this File downloadsFolder = Environment.getExternalStorageDirectory(Environment.DIRECTORY_DOWNLOADS). That’s how we get the Downloads folder. As you can see it says it cannot be applied to string so this is returning a string apparently, so we have to convert this to transform this into a file. Is that correct? I think so.

We’re going to say new File(Environment.getExternalStorageDirectory(Environment.DIRECTORY_DOWNLOADS)). Will that fix this? I hope so. No it doesn’t fix it. Let’s check what’s going on here. I don’t know maybe this is an error of the warning system so for now, we will ignore this and if it persists we will fix it later.

That’s our downloads folder, but now we want to list the files of our downloads folder, so how can we do that? First we got to create a file array. We’re going to say File [ ] files = downloadsFolder.listFiles().

GET MORE OF THIS COURSE

Would you like to continue learning about the Write a PDF Reader Application in Android Studio Course? If you are interested will you please buy the complete course, Write a PDF Reader Application in Android Studio Course, on the Uthena Education Platform..

You can also get the first 1 hours and 11 minutes of the course completely for free on YouTube.

Thank you for reading the blog post or watching the course on YouTube.

I love you.

You’re awesome.

Thank you very much for checking out the Write a PDF Reader Application in Android Studio Course and I hope to see you again in the next blog post or video.

Love,

Jerry Banfield.



Posted from my blog with SteemPress : https://jerrybanfield.com/write-pdf-reader-application-android-studio/
Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!