So, the main reason no one has reversed this is because the EULA explicitly says you're not allowed to, which should be a red flag right off the bat. I'm a pretty awesome person, so I reversed it anyway. Considering the downloads are currently 1,000,000 - 5,000,000, it's probably about time. I'm not modifying, redistributing, or profiting off of it, so whatever.
I usually post here under a different name, for what it's worth.
I aquired the apk from the google market
here and used some common tools unpack the dalvik bytecode. I normally do stuff like this for work, and it's pretty rare that my hobbies overlap, so I was excited. Apks can actually be unpacked just like a zip file, so I encourage anyone to go take a looksy themselves. Check out dex2jar if you want some Java to look at.
Anyways, the permissions of the app are another red flag. There's nothing special, just ordinary advertising perms, and the fact that this app runs on tablets that lack typical smartphone sensors is odd. Guess it's not analyzing too many magnetic fields or passive audio, eh? On top of this, it needs access to your "device status and identity." Keep that in mind if you think the ghosts know who you are.
In the apk, the res/values/strings.xml file contains the wordlist that the program uses to randomly select its words. There are 2130 words that you can read here:
http://pastebin.com/SL0L3b7i.
The app includes the following software packages:
-amazon (ads)
-google (ads and text2speech)
-pocketchange (shopping and rewards)
-revmob (ads)
-spudpickles (the app itself) (lol @ the name)
So, let's take a gander at spudpickles. The main activity is RadarActivity, which initilizes ads, pipes out random "coordinates" around the screen, makes the radar animation, and gets a GRCApp class. GRCApp handles most of the functionality.
In GRCApp, populateWords() picks your words the "ghost" says using:
Random random = new Random();
int l = random.nextInt(2130);
activeWordsIndices[l];
Yep, just random numbers. The amount of ghosts that are tracked is decided from your sensitivity settings:
maxGhosts = (int)appDelegate.sensitivitySettings.getUseValueFor(4);
ghostList = new ArrayList();
int i = 0;
while(i < maxGhosts) {
Ghost ghost = new Ghost(appDelegate);
ghostList.add(ghost);
i++;
}
Yup, that's seriously it. Wish I was making it up. Afterwards, we repeatedly step through the ghosts and see if we should show 'em or hide 'em:
while(true) {
if(!iterator.hasNext()) {...}
((Ghost)iterator.next()).checkForActivity();
}
checkForActivity() checks current time minus last time moved, and shows the ghost if it's greater than the sensitivity settings:
public void checkForActivity(){
long l = (long)(1000F * SensitivitySettings.getSingletonObject().getUseValueFor(3));
if(System.currentTimeMillis() - dateLastMoved > l)
hidden = true;
}
How does the ghost move around?
numberStream = new Random();
public void run(){
move(numberStream.nextFloat(), numberStream.nextFloat(), numberStream.nextFloat());
}
At scheduled intervals, random numbers are picked, then passed to a move method, which just takes the numbers and puts a blip somewhere in the radar circle. For those curious, check out the Movement class's move(float f1, float f2, float f3) method.
That's the meat of it. A bunch of math is also done to make polar coordinates, and calculate blip and word frequencies.
TL;DR version:
At scheduled intervals, random numbers are generated to pick a ghost's coordinates, whether or not they appear, and the words that are displayed. The amount of ghost objects created is literally your sensitivity settings. No sensor input is read to determin any of this.
Mirrored
here.