Recently I was tasked, by myself, to update all the dependencies of project containing submodules in counts of tens, to the newest version.
This was required in order to add a new dependency that itself depended on a newer version of some of the existing dependencies, think, appcompat-v7 for example.
Now that I had lousily when creating new modules on the project just let every single project get its default version of appcompat-v7 and when shared dependencies arised, just copied from existing gradle files, there was literally hundreds of dependencies that would have needed updated.
First I thought, this should be simple, right? Use the Android Studios replace all within project feature to perform the update on regex, for appcompat-v7 I would have target regex of
no biggie, right? Okay, to replace with regex, the replacement must be regex, or some similar format too such as
next step would be to add this variable $appcompatVersion to the ext section of projects build.gradle, easypeasy!
Now imagine doing this for all dependencies! I for one have at least five different dependencies that are depended upon by at least ten modules in the project, such hassle!
Automation to the rescue
I don’t remember who was the wise man of internet who once said that you could automate something, even if it took longer time, because it would consume less mental energy, it would still be a win-win situation, but that’s what I have been living upon, and it has been much happier life.
I botched up a quick Java application to automate the tasks, it could and maybe should have been a python script, as its pretty simple but man, where does zebra get from their stripes?!
The application has a simple job!
Take a path from comamand line arguments
For that path go through each sub-folder that contains file build.gradle
For this build.gradle file go through each dependency and keep track of each depency by their group, name and version
Make sure versions don’t differ across project, if they do, throw exception
Now we’ve got list of all dependencies and their versions!
Go through all collected dependencies and create a variable name by using the group and name of the dependency, also keep track of the value of this variable, so it can be saved later on
Go again through each sub-folder that contains file build.gradle
This time replace all dependencies version with variable we created before
backup this build.gradle and replace it with the newly created
Add variable to projects build.gradle, copy and replace it
Cross fingers that everything went well!
For me, this ended up creating 39 badly named variable in my projects build.gradle and much boring work was saved.
Hmm, could this naming scheme be improved?
Hmm, could this naming scheme be improved?
Now every dependencys version is defined in the project, awesome! This could and maybe should have been done by beginning of the time, but, oh well, humans ain’t perfect!
Best of luck to my fellow Android Devs and if you’re interested in this little piece of code, here’s the GitHub link!(be aware, Microsoft inside)
Today I am going to share you an extremely wild story about how I automated taking a screenshot of every single Pokémon in game!
The journey starts with a problem of not having every Pokémon in game. I mean.. Who does?
After all.. I am not going to ‘catch ’em all’, now am I? No! I am a programmer, not a Pokémon trainer..
First step was of course to get renders of all Pokémons.. Shouldn’t be too hard, right? Just manipulate, or more precisely.. Man in the middle the connection between my phone and the server, fake that the server sent every Pokémon in game and.. there we go, right?
After quick research I found this implementation in GitHub called Pokemon Go MITM Proxy by a nickname rastapasta, so I tried to setup it in Windows environment.. No luck.. obviously.. Its also documented in the repo that its hard to compile..
After recovering from the disappointment I cloned the repo on my Linux server and ran it there, installation went smoothly and I was ready to go!
Unfortunately there’s still need for root or removal of certificate pinning, but gladly from my last post I had a patch ready to go! So no worries! I installed the certificate as documented from the server and ran an example script that would replace all my pokémons, and I was good to go!
When I ran Pokémon Go after this, I ended up getting a list like this, however it wasn’t enough.. The example would only replace the Pokémons I had with others, but what I really needed is it to clear my Pokémons and add all Pokémons in increasing ids..
So the smallest modification I could get through with is remove all the code touching the pokemons, and craft a piece of code that replaces whole inventory with inventory cotaining an item with PlayerStats and all available Pokémons.
As funny as it sounds, PlayerStast too are passed as an InventoryItem.. Smart..
The minimal implementation I was left with is as follows:
I am not even sure if all of this is required.. But its pretty straightforward.. Replace inventory_items with array containing just PlayerStats, then add 151 Pokemons with ids 1-151 and be fone with it!
The end result is.. well the same as the previous as I was too lazy to run the initial version of replacePokemons to get the real initial result sooo.. Now we’re left with just taking a screenshot of every Pokémon.. Should be easy.. right!
For our good you can scroll the list of Pokémons by swiping so capturing screenshot should be easy as taking screenshot with Pokémon id as name, then swipe predefined amount, sleep 10 seconds to let the model load, and repeat!
A little Windows shell script I made is as follows:
adb shell input touchscreen swipe400350200350
Its pretty simple!
Repeat from 1 to 151
Take a screenshot with naming it with looper number
Pull the screenshot from the sdcard to the working directory
Evaluate a swipe, from right to left, changing the Pokémon on the screen
Sleep for ten seconds, because this is not implemented in a simple sleep fashion, ping 11 times and get approximately a 10 second delay
After running this for.. let me calculate.. approx 30 minutes.. we’re done, and have a folder full of images!
As you can see, we’re good, and as far as this glimpse tells me.. we did fine! Now its time to get on with these pictures.. but that’s a topic for another blog.. Thanks for reading!
Last time I posted about patching Pokemon Go without touching the native side, posted it on reddit and gained some good upvotes in /r/pokemongodev, and initial thought in the post was, should this be automated, and given the popularity, I’d say hell yeah!
That’s what I have been up for previous couple of days, writing a fool proof command line based application that lets users patch APK’s with any set of changes, currently it only supports complete file replacements or additions, but that’s easily expandable if required.
The basic task the tool was geared towards is to use APK Tool, as runnable Jar to unpack the APK, then apply some kind of patches to the unpacked folder and repack, sign and align it.
Requirements of running the tool is to have Android SDK, APK Tool and a JDK Installation, which are pretty common, if you’re into this kind of stuff!
The setup is quite simple, when you run the released jar from command line, the program looks for ANDROID_HOME environment variable, JVM parameter and the configuration file, created after the first run to see if its been set, and if it hasn’t it asks user to input it.
ANDROID_HOME here is the root folder of Android SDK, which could automatically be detected to some extent, but as I have other things to do.. for now.. It just looks for the environment variable and command line flag.. You can see where Android SDK is located from Android studio, but at least in Windows environment its usually located in
Where PasiMatalamaki is replaced with your epic us3rname.
Aftere setting up ANDROID_HOME, we’re not done yet.. The next thing to set is path to APK Tool.jar which can be downloaded from here. Just place it anywhere and copy the path to the jar and paste it in.. after that is the final step, before getting to the real deal! The most important part, which hopefully you’ve already set! Setting JAVA_HOME environment variable or just inputting it..
As written in the first Google result, If you didn’t change the path during installation, it will be something like this: C:\Program Files\Java\jdk1.8.0_65
And that is the final step before getting into the tool, after that you will be asked for build tools version you’d like to use, if you’ve got multiple.
All should work, but I am myself using the newest one, this is other thing, that could be modified to automatically pick the newest one, but man.. the effort!
Choose your buildToolsDir
So I input 5 and I’ll be asked for the apk to be used, or if the folder you’re working in, as in the folder you ran the jar in, contains none, you will be asked for a directory where the apk is, or just a full path to the apk.
Today we’re removing certificate pinning from Pokemon Go to man-in-the-middle the connection between my phone and the server, which is now required.
Main difference from similar posts, such as Eaton’s method where he removes it from the native code, is that, we’re not stepping to the dark side, and the main reason for that, for me, its much easier to patch the applications Dalvik code than the native ARM code, and I don’t feel too comfortable poking around in the native code, also I don’t have the tool required, Ida Pro with ARM..
Anyway! Lets get to the real deal, right!
First step, we need to take apart the APK, which is simple! Just use your favourite APK Extractor from Google Play, dump the APK on your machine and use APK Tools to unpack it.
This creates a pokemon_ua.apk file. ua stands for Un-Aligned, if you’re into Android Dev, you might now that the apks needs to be aligned before shipping! Which is a matter of running a zipalign on them, which is also described in the manual signing process article.
And finally we got the final ‘release’ build! Lets shove that into our device! Make sure you uninstall previous installations of the app!
adb install pokemon_release.apk
Run the app on the device, throw in your age, sign in aaand… In like a flynn!
Open up your favorite MITM proxy, such as Charles, which is what I am going to use, and configure your device to connect to the proxy instead, continue using the app and you will see what will make angels hum.
There we go, unencrypted version of the packets! However this is in a undecoded protocol buffer, which descriptor you can probably find online.. In Github or somewhere..!
It’s been a long time, but I am back with yet another Android packed problem I was recently solving!
I was implementing an SMS gateway for Android, which sound like a quite solved task, but apparently is not!
I tried the first Google Play results but couldn’t get any of the listed apps work.
Some of the apps required setting them to default sms app which made messenger not work, and for untold reasons I had to run this on my primary phone!
There’s an open market, go ahead and make one of your own, that actually works on Android 6.0!
So the problem sounds simple, I’ll create a BroadcastReceiver which listens for a action SMS_RECEIVED, which as the name says, listens for sms receiving broadcasts.
It seems like an easy task to hook into the sms system, unfortunately there have been couple of bigger changes after the original API has been updated(or at least all the documentation for it?) and that code, which blindly listens for SMS_RECEIVED with the RECEIVE_SMS permission, works on device thats running Android version lower than kitkat, which the device I originally tested on was.
Unfortunately when the code was ready for its prime time and I uploaded it on my primary phone(Nexus 6P <3) and as I received a test message, nothing happened, I was stumbled, but after a quick google I remember, the phone of mine had the new fancy runtime permissions, easy beasy, right?
After setting the sms reading permission, or whatever it is called on the runtime permission management UI and fired another test message, but as I look into the screen of my trusty Nexus 6P, still nothing appeared, I was getting frustrated!
The testing was getting quite costy, what are those messages here, 8 cents a pop? I have to apply for a loan before the app is finished, I guess..
Allows an application to broadcast an SMS receipt notification.
Not for use by third-party applications.
This doesn’t sound right, but can a blog post be wrong, mine isn’t so lets try!
Unfortunately it was still no help, no broadcasts were received by my app whatsoever! Not to say that this permission could solve some kitkat problem, I don’t know, I haven’t tried, but lets figure out whats wrong with my receiver!
Time to look into some working apps! I am actually using one of those sms receive listening apps, namely ‘Fonecta Caller‘, probably not familiar for my foreign readers, but its basically a call ID app which shows the caller name if you don’t have it on the address book, so whats better way to find out what I am doing wrong than to decompile one of these apps, right?
I looked into the sources of Fonecta Callers manifest and source but couldn’t find anything exciting, and I was stumbled, but then it hit me, maybe there’s some hidden ‘protection feature’ to annoy us, developers, that one needs to declare an activity in order to be able to receive sms broadcasts, it surely would be weird, but that’s really the only difference between mine and the caller apps source, the receiver wise, and to my surprise it worked!
And after all this hard work, spending couple of hours working on the stupidest problem, I decided to create minimal implementation with a working broadcast receiver of which requirements I am listing below
Launcher activity with action ACTION_MAIN and category LAUNCHER (other combinations might work, I am not sure which is the exact requirement, but without a home visible activity I couldn’t receive any broadcasts)
As of Android 6.0 a runtime permission request for RECEIVE_SMS or manually setting the permission on
To receive SMS_RECEIVED broadcasts you must have an activity with action ACTION_MAIN and category LAUNCHER combination, RECEIVE_SMS permission declared in manifest and a runtime permission if you’re running Android 6.0 or newer!
I am working on an application that needs to display certain amount of items in a a scrollable view that have headers on top of every one of them, so as the title says, RecyclerView.
However, my data that is coming from SQLite backed database didn’t fit too well on the mould of just using different kind of view types, because the data was coming from SQLite on demand and if I were to use view types to support headers I would have to process the whole database before showing it and add some kind of layer that provides me with the headers.
But first I needed to make my own SpanSizeLookUp implementation to separate different sections of the grid.
So the basic idea is that last item of each section is given with a span that makes it fill the rest of the row, so that the next sections items are dropped to the next row.
First I needed the information of which item belongs to which section, which I made my StickyGridHeadersAdapter in similar fashion as in timeshops implementation.
So the primary function of the adapter is to provide the header id for each position and its corresponding view.
So now that I got the information I am ready to extend the SpanSizeLookUp!
SpanSizeLookUp has one important function of determining each items span size, which is done by implementing getSpanSize method that takes the position of them item as parameter.
My implementation of getSpanSize first determines whether the item in question is the last item before next header, and if so, it calculates the remaining amount of columns in that row, that it needs to fill in order to bump the following item to next row.
So first we implement the last item before next header check, which is quite simple, take current and next item, and if their ids are different, they belong to different categories, meaning that the item in question is actually the last item before next header! Pretty simple huh?
//gets number of extra columns in range of 0..spancount - 1
//is just any ordinary item, takes one column width..
So first we use our lastly implemented method and check if it actually is the last item within the section, and if not, just return one.
But if the case is different, aka if it is the last item, we first calculate the offset of this item in question within the section, which is needed to calculate the remaining columns within the row, but for that we need to implement a way to calculate those preceding items.
Fortunately for us, its quite simple chore, given the fact that we can query the header id of each item, so basically what we do is we loop through each item preceding the current item til we find a change in header id!
Implementation of getNumberOfItemsBeforePositionInCategory
And to account for the case that this happens to be within the first category, we just return the given position, as it is the offset to the start of the list.
So with that info we can now calculate the column index..
column index calculation
//gets column index in range of 0..spancount - 1
which is just taking modulus of the offset within category and the span count
and to get the amount of items there are left on this row after the particular item, we just substract the column index + 1 from the span count, accounting for the the range of 0.. span count – 1 of the columnIndex instead of 1 – span count
calculation of extra columns
//gets number of extra columns in range of 0..spancount - 1
so then we can just return the amount of columns that this is supposed to fill, meaning one plus the amount of extra columns
returning required columns to fill the remaining space
So now if we run the code, what we can see, is that each category is withing its own boundaries, cool!
This may not be obvious from the preview above, but if you look closely the last item within each section(read: color) is spanning to fill the rest of the row.
Now we only need to add the headers for each section, let the fun begin!
So we need to implement RecyclerView.ItemDecoration, which has two important methods, onDrawOver to draw over the RecyclerView, which handles the drawing of the header view, and getItemOffsets, which provides the offsets to the views, giving enough space for the header in this case, but the possibilities are limitless!
So first lets give enough space for the headers by implementing getItemOffsets!
//checks if header id at given position is different from previous header id and if so, returns true to indicate this item belongs under the header
We just loop the range of span count and check if there’s any item that has different header id than the given position, and if so, we know that this item belongs under header and needs to be given space!
now we can implement the drawing of the header, this function is a bit more exotic to handle the stickiness of headers, meaning moving the topmost header upwards when it is pushed by the following header.
InstantAutoCompleteTextView solved some of these problems, but after that I still had couple of them left to overcome, and most important of them, the AutoCompleteTextView wouldn’t offer auto completion if you haven’t inputted any characters, or that after filling field completely, it would still show the user as hint.
I was using ArrayAdapter as AutoCompleteTextViews username adapter, to list matching usernames, but ArrayAdapters filter uses start of each word for matching prefixes, but in my case, as I was matching for usernames, I would need to match whole username, so basically I had to extend the ArrayAdapter to perform this operation.
Unfortunately ArrayAdapter stores the objects that it has as private field, so my extending adapter wouldn’t have access to them, without reimplementing most of its logic, so what I did, was open the ArrayAdapters source and reimplement most of its business, and reimplement the Filter it provides.
How I changed the Filter is that instead of matching for each word, I matched the whole string, and if the inputted text was exact to the adapters object, it wouldn’t be included, as when user had an exact match, it wouldn’t be needed to be shown anymore.
Now I have my Filter that makes the popup disappear after matching input is typed into.
Then I just have to implement the piece of code that handles the password autofill, and how I did that, is I stored set of user credentials as plain text(very secure) in JSON string to preferences, and now I could just when the AutoCompleteTextViews value changes, look up if there’s a already saved password and use that.
So what that basically meant is to add an TextWatcher and on afterTextChanged perform this check and set a fake password on the password field and store the selected password, or “override password” on other field to be used on login, so the potentially blackhat user can’t use the number of characters in the password to gain access to the password more easily.
Also now that I knew if the password was “fake” and “overriden” I could add a TextWatcher and another afterTextChanged callback that would reset the password if override was present, to imitate usual autofill behaviour.
ALL THIS MATERIAL IS PROVIDED FOR STUDYING PURPOSES ONLY AND MAY NOT USED TO PERFORM ANYTHING ILLEGAL
Yesterday my friend told me about a certain Android application that had a voucher for one lunch place, so as software engineers, a question quickly popped up – how would one work around it, and reuse the voucher.
Its surely connecting to a API of some kind to fetch the details of available vouchers, and it must somehow mark the voucher used after the cashier presses the “Use” button.
So working around it should be simple, right? We could use a proxy and just filter the consume requests? That would require extensive amount of work to do inside the phone and possibly rooting.
What about changing the devices DNS or setting the proxy to external server which would do the processing for us? Unfortunately as it turns out after setting a simple proxy, as the application happens to use certificate pinning, which basically means that doing a Man In Middle Attach is slightly harder, we would need to first change the bound certificate withing the application and then make our proxy sign the requests with the certificate inside the application.
So the next thought as a hobbyist reverse engineer is to export the APK to PC, decompile it and patch the request so it is not actually never sent to the server, but the completion listener is fired and the application thinks that we have actually received a positive answer from the server.
So first I had to figure out how hard is it to patch applications? Exporting the APK is simple as downloading an app from Google Play and fetching it over adb to PC.
Working with the APK itself is slightly harder because the APKs have compiled the Java source to different format than usual Java classes, meaning the dex file.
There is a classes.dex file in the root of the .apk file after you open it with your favourite zip tool(WinRAR anyone?)
So we take the classes.dex out from the APK and go ahead and figure what can we do with it?
We need to use some kind of tool to turn this into class files to better work with it.
So as I have previously worked with dex files I knew there was a tool called dex2jar(I like the naming btw) that would let me convert the classes.dex file(or the whole apk) to a .jar package containing the classes, but I had never before repackaged the outcoming classes into classes.dex, so this would be something new.
which meant that possibly I could do the unpacking and repacking with just these tools, right?
So I transferred the classes.dex to the containing folder and fired couple of commands to turn the classes.dex into classes-dex2jar.jar which I could then work with
As it turns out after a minute, I had the .jar file within the same folder and was ready to get my hands dirty!
After unpackaging the jar I needed a tool I could work on the .class files with, I had recently bought DJ Java Decompiler but I thought I could try something new this time, as I have recently stumbled upon a Bytecode Viewer, and decided to give it a go!
The tool was supposed to have inline bytecode edition, but I couldn’t get it working, but I could find what I was looking for, the place where the HTTP request is made for the item to be consumed.
It have been a while since I have last time got my hands dirty with Java Bytecode or with decompiled code.
This piece of code is a anonymous class of anonymous class, which you can see from the naming of the class, Java adds postfix to class names that have been compiled from anonymous classes(classes within classes).
But from this code I can see that it is calling some method consumeCoupon, which after checking from its definition sends the HTTP request, and also it takes a OnCouponConsumedCallback as a parameter, so we know it is also passed here, even if its hard to judge from the decompiled source.
So what we can do now, is see what methods does this callback have and see if we can make this method call the completion callback straight, instead of going to the HTTP Server first, which ultimately means the coupon is never actually consumed in the server side.
The interface has two methods, but we only care about the other, onCouponConsumed, it takes a int, which possibly is the position within some list, or somekind of id, and a CouponItem.
The server request passes also a position along in the request, so we can try to plug that in and see what happens, unfortunately at this moment I have to change tool, because I am unable to edit the bytecode within this tool.
After looking at this beauty for a while I was ready to perform some patching
First of all I had to remind myself a little how the bytecode worked and in which order things had to be in order for this to work
So first I removed the isAdded check, because it is not needed, and I don’t even know what it checks for, which was matter of just trimming the code down a little
Then i started the real work, first I had to load the object into the stack, which I am calling the method on, so I had to figure out how is the OnCouponConsumedCallback added to the stack, which was relatively easy, I just tracked it down by the knowledge of the argument order.
I moved it to the top of the bytecode list, so it was first loaded on stack, as it is the object I am calling the method on.
Then I needed the parameters, the method I am calling is taking first the int and then the CouponItem as parameter, so I needed to figure out exactly how those were loaded to the older function call and move them after the loadage of listener.
Now I only need to call the method with the given pameters, so its time to stretch my fingers and check up the opcode names
Ultimately I ended up with the following code
And to check that the code is valid before getting it back to the apk and trying it on my device I first open the saved class file on the ByteCode Viewer
And boom! Its just like I expected
Now its time to get this goodness into the apk and actually be able to install it into the device! So first I added this class back to the .jar that the dex2jar outputted originally
Then I ran jar2dex on it
And there I had it, a shiny new classes.dex ready to be loaded into my precious devices memory!
So I moved the new classes.dex back to the apk, and now it was only matter of resigning the apk and installing it
So I had to create a new keystore for this purpose, so I fired up keytool
And ran it with -genkey -alias pasi -keystore key arguments
and set unknown values to all the questions asked, and was ready to go!
Now its time to sign the apk using jarsigner, so I ran the following command
C:\Users\PasiMatalamaki\Desktop>jarsigner -verbose -keystore key -signedjar “app_ua.apk” “app.apk” pasi -storepass helloworld
and that ultimately generates a signed versio of the “app.apk” apk file signed with keystore with name key(the one we just created) using the alias pasi and password helloworld
Now I have to align it using zipalign to make it work with android devices so I ran
zipalign -f 4 “app_ua.apk” “app_release.apk”
And ended up with app_release.apk that can now finally be installed on my phone and after a quick try, it worked like a charm!
Of course this is not to be used in real world, because that would be illegal, this is just proof of concept.
But thanks for reading and hit me up in comments if you’re interested in more of this kind of material.
Today I was stuck with yet another Android related problem, I was creating a edit text with auto completion that always shows the drop down to help the user remember the last choices.
So the ultimate requirement is to on activity open show list of all items in the auto completion adapter in a drop-down, which should be simple right?
The obvious choice is to use AutoCompleteTextView, which provides the feature of listing possible choices after inputting certain amount of characters, however I knew that it wouldn’t show the listing before inputting certain amount of characters.
First thing to do was of course to open the documentation which didn’t appear to contain any helpful information, however I threw together the activity with one of these view with a dummy adapter and tested how it works normally.
Normally it takes two matching characters to get the drop down to appear, and there is actually a option to set the threshold, so I decided to set it to 0 and checked what would happen if I focused the field
Judging from the documentation of setThreshold I know that if the threshold is set under one, the threshold is actually being set to minimum of one.
When threshold is less than or equals 0, a threshold of 1 is applied.
I looked for the conditions which needs to be fulfilled in order for the popup to be shown and found out that there is actually a flag on the ListPopupWindow, isDropDownAlwaysVisible, which also has a setter, setDropDownAlwaysVisible, so I was sure I was onto something!
Unfortunately the setDropDownAlwaysVisible setter had a @hide javadoc annotation, which removes the method from public with the following comment
Only used by<ahref="http://developer.android.com/reference/android/widget/AutoCompleteTextView.html">AutoCompleteTextView</a>under special conditions.
So I decided to find out those conditions, but there was only single method call to this setter within AutoCompleteTextView which was just a delegate setter which too had a @hide javadoc annotation, but this time the comment declared that it was pending API council approval, which meant this was a dead end.
This basically meant that I needed to hook on the onFocusChanged by either subclassing or setting a listener and within that hook make the popup visible.
Luckily there was public showDropDown method within AutoCompleteTextView which meant that subclassing wouldn’t be necessary and this could be a matter of a simple View.OnFocusChangeListener, the day was saved!
I thought to myself, okay.. so if we add a callback to the view so I’ll know when the containing view is visible, right? Unfortunately the View doesn’t provide an API or a mechanism to do attach a callback to the onAttachedToWindow method, so I had to scrape that idea..
Hello dear readers, or should I say, Hello World? Hue hue!
This is third time if I am calculating correctly that I am starting a blog, so don’t expect too much.
I’ve always stopped writing due to lack or quality of content, but this time I am sure I have something quite interesting here!
I am currently in progress of writing a internal application for Android that is supposed to manage the calls in logging manner.
So the ultimate task I am trying to solve here is to log every call and mark their type to the database.
However today I was stuck with a problem!
So as a Android developer(?) you might know about the Broadcast system Android uses to notify applications about events system wide.
As I started to develop the application the obvious choice was of course to hook to the broadcast system using a BroadcastReceiver and listening for android.intent.action.PHONE_STATE and android.intent.action.NEW_OUTGOING_CALL broadcasts, which basically notifies the receiver about telephony state changes such as phone ringing or phone not ringing anymore.
As I started to implement the BroadcastReceiver and checking for possible outcomes I noticed on the documentation the following text:
The EXTRA_STATE extra indicates the new call state. If the new state is RINGING, a second extra EXTRA_INCOMING_NUMBER provides the incoming phone number as a String
That’s when I got worried, what if the system doesn’t provide me with the number in other states except for the RINGING state, how can I possibly know when which call had ended when the state is updated..
I began my quest to crawl through the docs thoroughly and quickly found out that the TelephonyManager has a listen method, which accepts a PhoneStateListener, which on part has a method onCallStateChanged with parameters of phoneState and incomingNumber, I thought this was it!
But after firing up the app and doing some calls my worst nightmare came to true, the incomingNumber is actually only incomingNumber, and what I mean is that it is only provided when a call is receiving, AKA ringing.. So using this listener I could only receive the same amount of data as with the broadcast receiver..
I was just about to give up handling multiple calls for the API limitations but as we visited chinese restauraunt on our lunch break we came up with a ‘foolproof’ way of handling multiple calls.
As we’re receiving the IDLE event as a broadcast when all the calls have ended, we know then to query the call log for any recent calls, and see which are new and how long had they lasted and let the user handle logging of those.
And that’s what I ended up with. When the BroadcastReceiver is first started, it checks if the processed call ids have been initialized, and if not it setups a list of phone calls that were pre-existent before the installation of the app, to figure out which of the calls are new.
Then the app just sits and waits for the EXTRA_STATE_IDLE to be broadcasted and once it is done it starts listening for changes in CallLog with a ContentObserver, and once the change happens we know that there’s been a write in the call log(hopefully..) and proceed to process the calls!
Phew! that was quite an adventure! but these are the kind I walk up to once in a while, and hope to write about.
So if you’re interested more about such horror stories as a Android Developer, drop me a comment.