Rob J http://www.robj.me/ Website of an Android Ninja Wed, 07 Sep 2022 09:45:19 +0000 en-US hourly 1 https://wordpress.org/?v=5.4.11 Software Developers + Time = Burnout (& How to avoid it!) https://www.robj.me/uncategorized/software-developers-time-burnout-how-to-avoid-it/ Wed, 07 Sep 2022 09:44:42 +0000 https://www.robj.me/?p=862 I was a guest on Vasiliy Zukanov’s Youtube show a few days ago & amongst the many thing we talked about, one of the topics that came up was burnout. It’s a big topic in the software development industry, mostly brought up in the context of the demands of working in big tech (think FAANG), but it affects… Read More »Software Developers + Time = Burnout (& How to avoid it!)

The post Software Developers + Time = Burnout (& How to avoid it!) appeared first on Rob J.

]]>
I was a guest on Vasiliy Zukanov’s Youtube show a few days ago & amongst the many thing we talked about, one of the topics that came up was burnout. It’s a big topic in the software development industry, mostly brought up in the context of the demands of working in big tech (think FAANG), but it affects a lot of developers— especially since remote working has become the norm.

I’ve seen developers on the same projects as me working overtime, evenings, weekends, answering non urgent phone calls at 8pm, sending Slack messages at 11pm — getting stressed and slowly working their way towards burnout, without any tangible benefit if they didn’t do all those things.

Meanwhile I’m working multiple contracts a year (sometimes simultaneously!) whilst intentionally avoiding burnout, producing the same amount and quality of work as my colleagues, with none of the added stress some of them endure.

So how do you avoid burnout when working remotely? Here’s my top 5 tips.

1 — Set boundaries.

This is easily the number 1 thing to avoid burnout. Boundaries.

Firstly, there needs to be a cut off time after which, you’re not working. You’re not on Slack. You’re not answering phone calls. You’re just not working.

Now I know some people, especially those who’ve already formed this bad habit of always being available to their employer, will find this hard to do — so here’s a little hacks to make it a bit easier.

In Slack (or Teams if you have the misfortune to use that 😀 ) — set your Do Not Disturb to come on at the time you finish work. So for me, DND comes on at 5.30pm & goes off at 8am weekdays, while on the weekends, it’s always on. The beauty of this is when people go to message you, they’ll see a message that says you have DND on, and they’ll know whatever they send, you won’t see until tomorrow. They’ll also have the option to push a notification through and bypass DND, meaning that person will have to think to themselves is this message worth disturbing you outside of your working hours, so most people will generally only push the message through if it absolutely can’t wait until tomorrow. All the while, no one feels like they’re being ignored because they know, until you start work tomorrow, you won’t have even seen that message let alone have ignored it.

Secondly, don’t answer work calls on your personal phone. I’ve worked for people who will call at 7.45pm to tell you they’ve just found a bug, while testing your work, for the current sprint. Obviously not an urgent call. Now while my colleagues would answer these calls — I would not. In fact, I would never answer any call on my personal number from work because I feel like it sets a bad precedent. We have Google Meetup, Slack, Zoom, Teams etc for work calls, my personal phone number is not one of them.

2 — Take time off.

Taking breaks away from your screen is something that was easy when we all worked in an office, people would go get coffee or have a chat with colleagues. Working from home oftentimes means getting coffee is a 2 min activity to the kitchen, and chatting with colleagues happens on the exact same screen where work happens — so we never really have a break.

My solution to this as a contractor is to take long sustained breaks between contracts, a month at a time usually, either to travel or to just take time off.

However if you’re not in a position to do that then simply take regular breaks from your laptop, whether it’s a 20 min break to watch some Netflix or it’s a 20 mins walk round the block (I advocate for the latter but have been known to do the former) — anything to get you away from “work” and give yourself a moment to relax.

3 — Don’t work where you live.

Now this may seem counter intuitive, we are after all, working from home. However what I’ve found extremely useful when working from home to have a dedicated space where you work, and it shouldn’t be where you also relax.

The reason being it’s much easier to switch off from work when you’re not at work, and by having a space in your home which is dedicated to work, it makes it easy to switch off from work when you’re not in that space. Plus it also makes it much easier to get into work mode when you have a dedicated space for work.

If you have a home office, then you work there, and only there, and when you leave your office — you’re no longer at work. If you don’t have a home office, maybe pick the dining table, or the kitchen. Anywhere where you wouldn’t normally be using a laptop, so when your work day is over, you can close your laptop, put it away and be done for the day. In our previous home I worked at the dining table, which was in our living room, but the fact that I worked at the dining table meant when I was done for the day, and I went and sat on the sofa, I didn’t feel like I was at work because I never worked on the sofa. And no one’s going to mistake their plate of food for a laptop at dinner time!

The key thing is if you can separate your work space, from your living space, you’re much less likely to just check that email or just finish that piece of work.

You can relax.

4 — Finish on time. There’s no prize for finishing early.

Many times I’ve had the experience where as a team, we’re asked to estimate a piece of work, let’s say we decide it’ll take 5 days to complete. Then, when it comes round to doing the work, the project manager has decided we’re going to finish it in 3 days because someone has moved the goalpost to get it released, and the PM has promised it’ll be ready on time.

Now for a lot of the developers I work with, this is a stressful situation that’ll involve working evenings and maybe even weekends to get it done. Not for me.

Why? Let’s say you’re buying a house, and your offer of $100,000 is accepted. You go through all the lawyers and paperwork and on the day you’re about to transfer the money and take ownership of the house, the seller informs you they’ve decided they’re going to buy a bigger house than they originally planned, so you need to give them $150,000 for the house, instead of the originally agreed $100,000.

In this situation, I imagine most people’s response wouldn’t be to scramble to find an extra $50,000 to give to the seller. So why do most developers scramble to meet a deadline because someone in the company decided it needed to be done faster, after it was agreed it would take a specific amount of time?

Assuming you didn’t dramatically overestimate the initial work then there’s no reason for anyone to assume it can be done faster, and therefore it’s not going to be done faster without the detriment to your work/life balance and your mental health.

If you agree to a deadline, meet the deadline. If your higher up’s decide you need to work faster because they’ve moved the deadline — tough.

5 — Other people’s job pressures are not your own.

This is also something I see ALOT. Your manager is stressed because their manager is putting pressure on them to make you work faster because their manager has decided this 6 week project is going to be delivered in 4 weeks because their manager has pulled a 4 week release date out of their ass.

So now the developers are stressed because they’re taking on the pressure from the PM etc etc. It’s not a good place to be. However it’s pretty common, especially in startups, and the main thing here is — don’t take on someone else’s job pressure.

If you work hard, during reasonable work hours, and that means a deadline is still going to be missed. That is not your fault, or your problem.

You’re responsible for coding to the best of your abilities. You’re not responsible for the products overall success. Or whether your manager looks like a rockstar. Or their manager looks like a genius. Or the founder looks like the next Zuckerberg.

Be responsible for what you’re responsible for. Leave the rest.

The post Software Developers + Time = Burnout (& How to avoid it!) appeared first on Rob J.

]]>
From Beginner to Junior Android Dev: 10 things you need to learn https://www.robj.me/uncategorized/from-beginner-to-junior-android-dev-10-things-you-need-to-learn-551702ae543f/ https://www.robj.me/uncategorized/from-beginner-to-junior-android-dev-10-things-you-need-to-learn-551702ae543f/#respond Thu, 29 Aug 2019 00:00:00 +0000 https://www.robj.me/from-beginner-to-junior-android-dev-10-things-you-need-to-learn-551702ae543f/ When it comes to Android Development I think it’s safe to say that there’s the way things are taught, and then there’s the way that things are done. And in my 8+ years of Android development experience it’s safe to say that every Junior Android Developer I’ve ever worked with has known how to do… Read More »From Beginner to Junior Android Dev: 10 things you need to learn

The post From Beginner to Junior Android Dev: 10 things you need to learn appeared first on Rob J.

]]>
When it comes to Android Development I think it’s safe to say that there’s the way things are taught, and then there’s the way that things are done. And in my 8+ years of Android development experience it’s safe to say that every Junior Android Developer I’ve ever worked with has known how to do things the way things are taught, but not the way things are actually done.

Now that’s not a slight against anyone, we all have to start somewhere.

But no matter how you’ve learnt to build Android app’s -whether you learnt on a coding boot camp or have just finished a degree, whether you’ve been following a Udemy course or have been following online tutorials..

If you’re truly serious about taking your Android programming skills to the next level. If you want to go from beginner to professional. If you want to go from hobby to career. Here’s a list of 10 things to learn that will put you light-year’s ahead of the competition.

And that’s not hyperbole either. I often get asked to help interview candidates and if your resume crossed my desk for a Junior position with the following skills — you’d be in 💥 (or maybe better put, if your resume crossed my desk without these skills.. 🚮 🤷🏽‍♂️ Real talk.)

1 — Don’t reinvent the wheel

If you’ve any experience in programming you’ve probably heard this phrase before, if Android Development really is your first foray into programming then let me be the first to tell you this. Don’t reinvent the wheel.

9/10 times whatever it is you’re trying to do has been done by someone else. There’s either a library for it or a gist for it or a tutorial explaining how to go about it. So before you start rolling your own, have a Google, have a look on SO, and benefit from others mistakes.

2 —Learn Retrofit for networking

Sure there are other networking libraries like Volley or Fast Android Networking, but if you’re looking to become a hire-able Android Developer then you need to know how to use Retrofit.

Learn how to do the basic’s like perform GET’s & POST’s, learn how to add headers to specific calls, learn how to hook it up to OkHttp for performance, learn how to create some basic interceptors, specifically you’d benefit for using one that let’s you log the request/response of calls verbosely (in the vain of not reinventing the wheel I’ll give you a hint, there’s an interceptor you can import that’s built just for this!).

3 — Know how to POJO from Json

JSON is the most widely used format of transferring data across API’s. You will come in to contact it working as an Android developer so you need to know how to work with it & convert it into your POJO’s (Plain Old Java Object’s).

The most commonly used libraries for this are Jackson, GSON & Moshi. You can use any that you like the look of, they’re all very similar & they all hook into Retrofit easily. You’ll wanna learn how to parse Json into your objects, how to annotate them correctly, how to override setters so you can control the way the data is parsed & you’ll probably also want to get a basic understanding of custom serializers & deserializers as they’ll almost definitely come to play at some point in your career.

4 — Get to grips with some Image libraries

I’d advice either Picasso or Glide, Glide being Google’s unofficially endorsed choice. They’re both very similar so knowing how to use one will let you get up to speed on the other pretty quick. You want to understand caching strategies, how to handle errors with fallback images & how to apply some transformations. A good example being how to take an image & make it circular to display as a profile picture.

5 — Learn some patterns

There’s plenty of architectural patterns that you can use when building your Android apps. MVP, MVC, MVVM, etc etc.. My preference is MVP which is why I’ve highlighted it here, it’s also the pattern I’ve seen most commonly used when I start on a contract where the app is already in progress so already have it’s architecture predefined. For that reason I’d say learn this pattern & you can use the most commonly used Mosby library to do a lot of the work for you also.

If you’re really against MVP though or something else interests you more I’d say learn that. The main takeaway here should be while you may have learnt to build your app’s with everything in 1 activity or 1 fragment — no one build’s apps like that. Scratch that. Some people do. Let me rephrase — no one is going to hire you because you can do that, but they’ll hire you if you have worked with one of the more commonly used pattern they’re looking to use.

6 — Kotlin

If you’ve just started learning & have been learning using some older Java tutorials then you need to get to grips with Kotlin. Kotlin is officially supported in Android Studio, it has numerous benefits over Java & it’s pretty much a prerequisite now when hiring Android developers. You don’t need to be a pro, and tbh the learning curve is not steep at all if you know Java. You just need to know enough to get started on a project immediately & learn the rest as you go.

7— Java

If you’ve just started learning & have been learning using newer Kotlin tutorials, fantastic! You’re ready for the future. Unfortunately a lot of companies still live in the past so if you’re serious about becoming an employble Android developer then you need to be able to pick up their legacy code, meaning you need to know some Java. Again, if you’ve started with Kotlin then you just need to know the basic’s in Java to be able to get started. I would highly recommend you get somewhat competent though because if you can’t work with Java code then you’re pretty restricted in what Android developer roles you’d be able to take.

8 — Unit testing

When I started programming I hated unit testing. In fact, every gig I’ve had in my 8 year career so far has shied away from testing in favor of pumping out code faster. Luckily I’ve not seen many major issues doing things that way however, a serious Android developer knows how to write a unit tester whether they’re required to or not.

Just know the basics. How do you set one up, how do you run it, how do you verify the result. You can later learn the more complicated things as those scenarios come up.

9 —MockitoMockk

Following on from #7, one of the most useful things in the testing world is mocking. Imagine you have a Presenter you want to test & it has 6 other dependencies. You can go ahead & create all of those dependencies in your test code, maybe those dependencies have dependencies so you’ll have to create those too – suddenly your test class has more code in it setting up the test than it does executing the test. OR.. you can mock those dependencies.

Mockito & Mockk would be my two go to classes to implement mocking, with Mockk being my go to if you’re writing a Kotlin based test. Learn how to mock objects, mock results, spy objects — which is where the object behaves as normal but you can override methods to mock specific aspects of it. Mock’s also let you run code that might rely on an Android code inside a unit test as you can simply mock that object.

10 — Espresso testing

Espresso tests are essentially Android tests. You’re testing code in Android itself, either on a device or on an emulator. You can test headless code like services or you can run UI tests where, for example, you “click” on things & verify the right action is taken. You want to know how to write one, how to perform simple actions like click’s or scrolling & you want to know how to verify the results so your test can pass or fail.

(Bonus) — Butterknife

There are very few scenario’s where you’d actually want to use findViewById. Personally I’m not longer a huge fan of Butterknife as if you’re writing Kotlin code it’s just not needed, one of the many benefits of Kotlin on Android. However if you’re serious about becoming a hireable Android dev than you need to know how to use Butterknife. Before Kotlin became more widely used Butterknife was pretty much included in every project to reduce boilerplate code so if you get a gig working on a Java codebase, 99% chance it’s using Butterknife.

It’s really not that hard, I’d steer away from learning things like the OnClick annotations as I find them to be bad practice when you’re working on a multi person code base (why are they always linted as unused!) & instead just learn how to Butterknife.bind & the BindView annotation & you’re good to go.

There you have it. My top 10 things you need to learn to go from being a beginner to having some serious Android game!

As always – question, feedback, thoughts.. all would be much appreciated in the comments below 👇🏽

The post From Beginner to Junior Android Dev: 10 things you need to learn appeared first on Rob J.

]]>
https://www.robj.me/uncategorized/from-beginner-to-junior-android-dev-10-things-you-need-to-learn-551702ae543f/feed/ 0
Android Pro Tip: How to determine if you’re running in a Firebase Test Lab https://www.robj.me/uncategorized/android-pro-tip-how-to-determine-if-you-re-running-in-a-firebase-test-lab-503142950297/ https://www.robj.me/uncategorized/android-pro-tip-how-to-determine-if-you-re-running-in-a-firebase-test-lab-503142950297/#respond Wed, 28 Aug 2019 00:00:00 +0000 https://www.robj.me/android-pro-tip-how-to-determine-if-you-re-running-in-a-firebase-test-lab-503142950297/ In this entry of my Android Pro Tip’s series I’m going to fill you in on an easy, one line piece of code you can use to detect if you’re running in a Firebase Test Lab. Why would you want to do this? There are a number of reasons you might need to detect if you’re… Read More »Android Pro Tip: How to determine if you’re running in a Firebase Test Lab

The post Android Pro Tip: How to determine if you’re running in a Firebase Test Lab appeared first on Rob J.

]]>
In this entry of my Android Pro Tip’s series I’m going to fill you in on an easy, one line piece of code you can use to detect if you’re running in a Firebase Test Lab.

Why would you want to do this?

There are a number of reasons you might need to detect if you’re running inside a Firebase Test Lab, a few of them being:

  • You might want your app to react a little differently than it would when being run by a real users.
  • You might want to disable analytics in test lab so as not to dilute your analytical data.
  • You might want to disable access to certain features that cost you money such as uploading to image buckets or running certain backend requests.
  • You might want to add a “test” header to certain backend requests so you can differentiate real data from test lab data.

You get the idea.

So how do you do it?

fun isInTestLab(contentResolver: ContentResolver): Boolean {
return Settings.System.getString(contentResolver,     
"firebase.test.lab") == "true"
}

Easy right? Firebase Test Lab returns true when querying for this system settings string, so you can now detect when you’re running inside the test lab inside your code & react accordingly. Simple.

If you have any thoughts or feedback, I’d love to hear what you think in the comments! Also if you have any requests for little hacks or tip’s like this you might be interested let me know & I’ll see what I can do!

I’d love to hear what you think!

The post Android Pro Tip: How to determine if you’re running in a Firebase Test Lab appeared first on Rob J.

]]>
https://www.robj.me/uncategorized/android-pro-tip-how-to-determine-if-you-re-running-in-a-firebase-test-lab-503142950297/feed/ 0
Android Pro Tip: Generating your app’s changelog from Git inside build.gradle https://www.robj.me/uncategorized/android-pro-tip-generating-your-app-s-changelog-from-git-inside-build-gradle-19a07533eec4/ https://www.robj.me/uncategorized/android-pro-tip-generating-your-app-s-changelog-from-git-inside-build-gradle-19a07533eec4/#respond Sun, 18 Aug 2019 00:00:00 +0000 https://www.robj.me/android-pro-tip-generating-your-app-s-changelog-from-git-inside-build-gradle-19a07533eec4/ Welcome to my Android Pro Tip’s series — where I’ll try to share the most interesting, useful & little known tips & tricks I’ve learned in my 8+ years of being a freelance Android developer. To start with we’re going to start with one of the more boring but advantageous components of your app — the changelog. I’ve always… Read More »Android Pro Tip: Generating your app’s changelog from Git inside build.gradle

The post Android Pro Tip: Generating your app’s changelog from Git inside build.gradle appeared first on Rob J.

]]>
Welcome to my Android Pro Tip’s series — where I’ll try to share the most interesting, useful & little known tips & tricks I’ve learned in my 8+ years of being a freelance Android developer.

To start with we’re going to start with one of the more boring but advantageous components of your app — the changelog.

I’ve always shipped changelog’s with my apps as they’re a nice way of keeping users updated of what’s changed in the app. Especially if it’s a bug fix they’ve been waiting for, or a feature that they might not realize has been added unless they’re explicitly informed. Hell I even built a library to reuse across all of my apps so I didn’t have to keep building the front end for it over & over again!

The annoying part of this setup however was always actually writing the changelog. Mostly because I’d forget and ship a build with an old changelog 🙈 but also because when I actually remembered I’d find it pretty tedious to go back through my commits to remember what’s changed, so I could write the damn thing.

I recently discovered a pretty nifty way to automate this process entirely using the app’s own git log.

Git -> Changelog

During a recent project there was a requirement to add a changelog to the app for internal test builds, so testers could see what changed in a build from one build to the next. However this particular project had multiple Android developer’s on it so wasn’t exactly feasible come release time to manually write the changelog without involving all the developer’s for their input on what’s changed & how we express that to the user. So instead, after a little investigation, I came up with this little solution that run’s from inside the build.gradle file – completely automating the process of generating the changelog, building it as a String in the BuildConfig file which I can then access statically from our code base & display directly to the user in our changelog UI.

The benefits of this approach

  • It uses commit messages as the changelog item so nothing is ever missed! We adopted a “Squash & Merge” approach to accommodate this setup so instead of this approach listing every single commit in a PR, which could be numerous & very techincal, we instead title our PR’s appropriately when we squash & merge with a user facing title, such as “Fixed crash when clicking on user profile”. That non-technical user readable string is what ends up in the changelog.
  • Your changelog will always be up to date. This method builds the changelog from the last tagged commit until now. So as long as you tag your release’s appropriately — your changelog will always be up to date.
  • It requires no developer intervention whatsoever. It run’s when you build your APK so every time you commit, a new changelog item will appear in the next build.
  • It’s easily customizable to meet your needs or even ignore commits based on some specific string identified. For example, if you don’t want a specific commit listed in the changelog you could prefix it with “**” & then just ignore commit’s prefixed with that when building the changelog.

Sounds easy enough right? Let’s look at the code.

The code

To get started converting our git commits into changelog items, we firstly need to get the all the commits between now & the previous app release. If you’re tagging your builds when you release (and if you’re not why the hell not!) then this is super easy. Firstly you get the last release tag using the git command:

git describe --tags --abbrev=0

Next you can generate a list of commits since that tag, prettified into a format we can use, using the git command: (where $lastTag is the result of the previous command)

git log $lastTag..HEAD --oneline --no-merges --pretty=format:"%s"

The good bit about this being you can run both commands in your terminal, outside of Android Studio, to see exactly what is going to be output without having to keep re-syncing your Gradle file to run the task & see the result.

Once we’ve got that all we need to do is loop each line, format it in a way we want & append it to our changelog string — easy!

We also need to make sure we escape special characters as we’ll be outptting the result of this Gradle task into a String inside our BuildConfig file. As this is a Java class we’ll need to make sure any special characters are escaped so the final string produced is a compliant Java String.

The final Gradle task will look something like this.. (with println used to output useful information into our Gradle build output for debugging purposes)

String generateChangelog() {
    println "Generating changelog.."
//Get the last tag
    def lastTag = "git describe --tags --abbrev=0".execute().text.trim()
//Get all the commits since the last tag
    def gitLogCmd = "git log $lastTag..HEAD --oneline --no-merges --pretty=format:\"%s\"".execute().text.trim()
//Loop each line of the commits to build your changelog
def changelog = "\""
gitLogCmd.eachLine { line ->
//Remove surrounding quotation marks generated by the git log comand
        def escapedLine = line.substring(1, line.length() - 1)
//Escape backslashes
escapedLine = escapedLine.replaceAll(/(\\)/, "\\/")
//Escape quotation marks
escapedLine = escapedLine.replaceAll('"', '\\\\"')
//Add each item to the changelog as a bullet point
changelog += "• $escapedLine \\n"

}
//Close the changelog string
    changelog = (changelog + "\"").trim()
//Useful log so you can see what was generated in the Gradle output
    println "Changelog generated, $changelog, from $lastTag to now."
    return changelog
}

Nearly done

Now that you can generate the changelog from inside your build.gradle file, you just need a way to make use of it. As I mentioned before, I wanted it to be statically accessible inside the app’s BuildConfig file so to do this, I added the following buildConfigField to the defaultConfig section inside of the app’s build.gradle file like so:

android {
...
   defaultConfig {
      ...
buildConfigField "String","CHANGELOG","${generateGitChangelog()}"
      ...
}
...
}

Et voila! You end up with a single, bullet pointed Java string, containing every change since your last release, easily accessible by calling BuildConfig.CHANGELOG. Easy.

Side note

As not everyone’s requirements are the same- if, for example, you don’t want a single bulleted string but want each item separate so you can output it into a RecyclerView (like I do in my own app’s using the open source library I made specifically for this) you could use the same approach but use something like BuildConfig.CHANGELOG.split(“\n”), to create an array of your changelog items, split on the newline.

Job done

So there you have it. An auto generated changelog — always up to date, easily customizable, easily accessible.. easy!

If you have any thoughts or feedback on this approach, or know of a better way of doing this I’d love to hear what you think in the comments! Also if you have any requests for little hacks or tip’s like this you might be interested let me know & I’ll see what I can do!

Let me know what you think! 🤓

The post Android Pro Tip: Generating your app’s changelog from Git inside build.gradle appeared first on Rob J.

]]>
https://www.robj.me/uncategorized/android-pro-tip-generating-your-app-s-changelog-from-git-inside-build-gradle-19a07533eec4/feed/ 0
How I got Android App Installs + IAP numbers mailed to my Inbox https://www.robj.me/uncategorized/how-i-got-android-app-installs-iap-numbers-mailed-to-my-inbox-4950682ce745/ https://www.robj.me/uncategorized/how-i-got-android-app-installs-iap-numbers-mailed-to-my-inbox-4950682ce745/#respond Mon, 27 Aug 2018 00:00:00 +0000 https://www.robj.me/how-i-got-android-app-installs-iap-numbers-mailed-to-my-inbox-4950682ce745/ As has become the norm for me in 2018, I’m going off again to travel for a couple of months. During which time I plan to have a little detox from coding which means, aside from bugs which need to be fixed & support emails that need to be answered – I won’t be doing… Read More »How I got Android App Installs + IAP numbers mailed to my Inbox

The post How I got Android App Installs + IAP numbers mailed to my Inbox appeared first on Rob J.

]]>
As has become the norm for me in 2018, I’m going off again to travel for a couple of months. During which time I plan to have a little detox from coding which means, aside from bugs which need to be fixed & support emails that need to be answered – I won’t be doing much else regarding work on my apps.

However, as an indie dev I still need to keep on top of things like installs, uninstalls, usage, IAP purchases & the like, mostly to identify any issues that might not necessarily trigger a Crashlytics email but could still substantially impact my user base. Unfortunately for me, that still requires more manual interaction then I want — I can live with the Play Developer console app but Firebase Analytics requires firing up a laptop, and sifting through the data across all my apps on the Firebase Dashboard — which definitely does not qualify as a digital detox of any stretch! Not to mention the WiFi in Asia can be pretty flakey at best & the Firebase console isn’t exactly light on resources..

Hence the reason I built a solution that would collate all the data I wanted to know about my apps, and send me a daily overview of it — straight to my inbox! Zero manual checks necessary, with a cost of $0 (or close to zero — we’ll see how the usage affects billing 🤷).

Now if you just want to get all the analytics you care about sent straight to your inbox & have zero interest in how it happens or doing it yourself, you can skip the rest of this post & just sign up here. However for those of you who might want to roll your own or are just curious about how it all came together, let me explain how I did it..

Putting the pieces in place

The first thing when you’re building something like this is to figure out all the moving parts & how they all slot together. In this case I knew I’d need:

  • a database component to store the information
  • Some server side API component to collate that data submitted across multiple installs throughout my user base
  • A way to email that data to my inbox
  • And an email template to display the data
  • Oh and I’d need to build a client side SDK I could integrate across all my apps to make my life a little easier.

And it had to cost me $0 dollars. #poorindiedev

In the end I went with the combination of Firestore for storage, Firebase Functions to act as my API layer, MailGun as the email functionality for it’s easy to use SDK & great free pricing tier, and I nabbed a free email template from SendWithUs.

I also decided that to make this as simple as possible (there’s no easy way to get detailed analytics in an email & I wasn’t going to reinvent the wheel – if I want details I’ll have to suck it up & sign in to the dashboard), the SDK was going to be type & event based. Simply put you define a type ie “Feature Used” & you record an even for that type ie “Search performed”. The SDK will increment with every event & at the end of the day I’d know my search feature was used X amount of times today across Y installs. Simple.

The API layer

Now that I knew my data structure I could go ahead & build out my Firebase Function to handle the collation & uploading of the data from my as-yet-to-be-built client side SDK. (I’m assuming you know how Firebase works but if not, here you go.)

Client side I’d be using Android’s SharedPreferences to record the data for it’s simplicity, I just needed a way to get it into the Firestore database without requiring the Firestore SDK. Mostly because I wanted to keep the SDK as lightweight as possible but also because I already use the Firestore SDK in a number of apps & I didn’t want the hassle of updating this SDK anytime they’d be a clash in version numbers. Also all we’d be doing is making 1 post request a day so a full SDK seemed kind of excessive.

Side note: If you’ve never used Firestore before, outside of creating some rules to protect your data there’s no real setup, you just start writing straight to the database.

Luckily Firebase functions provides you with the ability to expose any functions you write as a HTTP endpoint, which is exactly what I did:

exports.uploadStats = functions.https.onRequest((req, res) => {
  return uploadStats(req, res) //This function is detailed next
});

To that endpoint we’d be sending a JSON payload in the format of:

{
  some_type: {
    some_event: number_of_occurences
  },
  some_other_type: {
    some_event: number_of_occurences
  },
  ...
}

And then compile that data with all the data collected from other users by first grabbing the package name of the app, which I’d be sending in the header:

var packageName = req.get(‘packageName’)

And then using that to grab any existing stats for the app already in Firestore:

var docRef = admin.firestore().doc("appStats/" + packageName)
return docRef.get().then(snap => {
  var appStats = snap.data();
  //If no stats exist yet create an empty map
  if(isNull(appStats))
    appStats = {}
  ...
});

Next we’d loop all the type/events from our JSON payload to see if they already exist in the data we just pulled from Firestore and if so, we simply increment the count with the new user data, but if there’s types that don’t exist we add them:

//This function loops over the types
Object.keys(inputStats).map(function(type) {
  var inputTypeMap = inputStats[type];
  if(isNotNull(inputTypeMap) && isNotEmptyMap(inputTypeMap)) {
     var typeMap = appStats[type]
     if(isNull(typeMap))
        typeMap = {}

        //Inner function loops of the events per type
        Object.keys(inputTypeMap).map(function(key) {
          var value = inputTypeMap[key];
          var count = typeMap[key]
          if(isNull(count))
             count = 0
          count = count + value
          typeMap[key] = count
       });
       appStats[type] = typeMap
  }
});

And finally we write the updated values back to Firestore.

return docRef.set(appStats)...

A total of 1 database read, 1 database write & 1 function run per install. Efficient if I do say so myself.

Building the SDK

Now in the name of keeping things lightweight I also (much to your horror I know) didn’t want to include Retrofit as again — did I want to include a whole other library just for 1 Post call? So I did things the old school way.

internal fun uploadStats(context: Context) : Boolean {
   //The user has the option to disable all analytics/crashlytics from the ui
if(!SdkUtils.getCollectionEnabled(context))
return false
   val serverURL: String = BASE_API_URL + “/uploadStats”
   val url = URL(serverURL)
val connection = url.openConnection() as HttpURLConnection
   connection.requestMethod = “POST”
   //30 seconds because Triggers can be slow to start/return
connection.connectTimeout = 300000
   connection.connectTimeout = 300000
   connection.doOutput = true
//This method simply loops through my shared preferences & puts them
//into a map of type of:
//Map<String(type), Map<String(key), Long(count)>>
   val map = SdkUtils.buildMap(context)
val json = JSONObject(map)
val postData: ByteArray =
                 json.toString().toByteArray(Charsets.UTF_8)
   connection.setRequestProperty(“Content-length”,
                                  postData.size.toString())
   val outputStream = DataOutputStream(connection.outputStream)
   outputStream.write(postData)
   outputStream.flush()
   Log.d(TAG, “Response code: “ + connection.responseCode)
if (connection.responseCode != HttpURLConnection.HTTP_OK) {
val reader: BufferedReader = 
           BufferedReader(InputStreamReader(connection.errorStream))
val output: String = reader.readLine()
       Log.d(TAG,”Api threw error $output)
return false
}
return true
}

And I wrapped it in a kotlin coroutine for good measure. (I know, that is an extra library where I could just use an AsyncTask, but I’d just learnt it so it’s going in there – logic be damned!)

launch {
  uploadStats(context)
  StatManager.clear(context) //Clears daily stats regardless of success
}

Now we just need a way to trigger the upload and the SDK is complete. For that I turned to the GCMNetworkManager replacement Firebase-JobDispatcher. Again this is a bit more weight to the SDK but it greatly improves the efficiency on the device compared to the alternatives so to me it was worth it.

val dispatcher = FirebaseJobDispatcher(GooglePlayDriver(context));
val myJob = dispatcher.newJobBuilder()
             .setService(UploadService::class.java)
             .setTag(UploadService::class.java.simpleName)
             .setRecurring(true)
             .setLifetime(Lifetime.FOREVER)
             .setTrigger(Trigger.executionWindow(windowStart, windowStart + toleranceInterval))
             .setReplaceCurrent(true)
             .setRetryStrategy(RetryStrategy.DEFAULT_LINEAR)
             .setConstraints(Constraint.ON_ANY_NETWORK)
             .build();
dispatcher.mustSchedule(myJob);

The job will run in a 1 hour window every 23–24 hours from the time the app is installed, so once a day for efficiency. The timing means depending on the time zones stats will be uploaded at different times but as I just want a basic overview it doesn’t matter so much if some of yesterdays stats end up in today’s for some users. Plus the varied scheduling means 10’s of thousands of installs won’t all be trying to upload at the same time.

Oh and I used the on app updated receiver to set the job.

class AppUpdatedReceiver : BroadcastReceiver() {
@CallSuper
override fun onReceive(context: Context, intent: Intent) {
   Log.d(TAG, “app updated..”)
   SubmissionManager.scheduleUpload(context)
...

Which, if you don’t know, is declared in the manifest with the intent:

<intent-filter>
 <action android:name=”android.intent.action.MY_PACKAGE_REPLACED” />
</intent-filter>

And the SDK is complete!

Note: I purposefully omitted how I’m logging the actual stats as it’s simply incrementing a SharedPreference, but for reference I’m storing each SharedPref as a Long value with the name type:key separated by a colon, which I split at the time of upload to extra the type & key.

Building the email

So right now we have an SDK to collect the stats & a way to upload them to our API layer which will do it’s thing & then add them to our Firestore database, meaning all that’s left is generating & sending the actual email.

First things first we need an email template, for which I turned to SendWithUs’s free open source email templates.

All I did was slightly modify the template I chose to work with HandlebarsJS so I could easily populate the email template directly with the JSON pulled from FireStore. Also, as the types & keys can be absolutely anything it needed a generic solution to display how I wanted. It looks something like this:

..header of the email..
{{#eachInMap this}} //Top level type
   {{key}} //Create type title with key
     {{#eachInMap value}} //Nest event:value for each type
       Event: {{key}}
       Count: {{value}}
     {{/eachInMap}}
{{/eachInMap}}
..footer of the email..

Where eachInMap is a custom handler I s̶t̶o̶l̶e̶ borrowed from this Stackoverflow post.

var handlebars = require(‘handlebars’)
handlebars.registerHelper( ‘eachInMap’, function ( map, block ) {
   var out = ‘’;
   Object.keys( map ).map(function( prop ) {
      out += block.fn( {key: prop, value: map[ prop ]} );
   });
   return out;
});

With that done I created an endpoint in my Firebase Functions that could be hit to trigger the email. It takes a query string which is the packageName of an app, which it then uses to grab the data from Firestore.

exports.sendEmail = functions.https.onRequest((req, res) => {
   var packageName = req.query.package_name
   var docRef = admin.firestore().doc(appStatsPath + packageName)
   return docRef.get()...

Checks if there’s any data to be emailed

if(isNull(appStats))
  //If empty I throw a 400 response

If there is, it grabs out email template from the filesystem (if you keep it in the same folder as your index.js file it’ll get uploaded when you deploy), and builds the email using handlebars.

var fs = require(‘fs’); //Filesystem
var source = fs.readFileSync(“./email_template.html”,”utf-8");
const template = handlebars.compile(source, { strict: true });
var html = template(appStats);

And then we send the email with mailgun.

var data = {
   from: ‘[email protected]’,
   subject: ‘Your daily email overview!’,
   html: html,
   ‘h:Reply-To’: ‘[email protected]’,
   to: ‘[email protected]’
}
var mailgun = require(‘mailgun-js’)({apiKey : mailGunApiKey, domain : mailGunDomain})
return mailgun.messages().send(data, function (error, body) {
   console.log(body)
   send(res, 200, { message: ‘Success’, body, });
});

And we’re done!

Well, actually not quite. We still need a way to trigger this email daily so we don’t have to keep manually hitting out sendEmail endpoint. For this I went super high tech… IFTTT 😁 That’s right, I wrote me a quick recipe (using IFTTT’s webhooks plugin to hit the url):

IF ‘it’s midnight’ THEN ‘hit this url’.

Simple. 👌

And that’s how it’s done!

With that done, this morning, just like every other morning these days – I woke up to this lovely email! 🙌🙌

DroidStats email for Coffee-Working

No dashboards, no manual interaction, just an email, sent to my inbox, like clockwork! Nowww I can go off on my travels knowing that, should there be any major fluctuations in users daily use that might require some developer intervention, I’ll know about it without even having to look! (Plus I still get my Crashlytics emails so I’ll know about actual crashes also 😏)

Now if you’re reading this thinking “this is cool, I want to do the same thing” but you don’t want to/don’t have the time to put in the work to replicate what I’ve done, you might be interested to know I’m considering opening up the service that I built to all devs. One easy to integrate SDK & you’d be all set!

If that’s something that does interest you can let me know by registering here and if enough people are into it — it’ll be rolling out as soon as I’m back! 😄

And that’s that folks- thoughts, questions, suggestions.. any feedback whatsoever? I’m all ears.. 👂👂🤓

The post How I got Android App Installs + IAP numbers mailed to my Inbox appeared first on Rob J.

]]>
https://www.robj.me/uncategorized/how-i-got-android-app-installs-iap-numbers-mailed-to-my-inbox-4950682ce745/feed/ 0
From the Dashboard to the Inbox — An #IndieDev’s analytics solution https://www.robj.me/uncategorized/from-the-dashboard-to-the-inbox-an-indiedev-s-analytics-solution-cf5d154b00ca/ https://www.robj.me/uncategorized/from-the-dashboard-to-the-inbox-an-indiedev-s-analytics-solution-cf5d154b00ca/#respond Sun, 19 Aug 2018 00:00:00 +0000 https://www.robj.me/from-the-dashboard-to-the-inbox-an-indiedev-s-analytics-solution-cf5d154b00ca/ Any project I’ve ever worked on that wasn’t my own, there’s always the one project owner who wants every analytics SDK they’ve ever heard of compiled into their app. Measuring everything they can think of, mostly in case it might be useful, with no plan of how to compile, correlate or even understand the mass… Read More »From the Dashboard to the Inbox — An #IndieDev’s analytics solution

The post From the Dashboard to the Inbox — An #IndieDev’s analytics solution appeared first on Rob J.

]]>
Any project I’ve ever worked on that wasn’t my own, there’s always the one project owner who wants every analytics SDK they’ve ever heard of compiled into their app. Measuring everything they can think of, mostly in case it might be useful, with no plan of how to compile, correlate or even understand the mass of data they were unnecessarily collecting.

And then there’s my own apps. For which I’d love to collect specific analytics that help me better understand my users like, for example, which features of my app users like or use the most so I can focus my efforts there.

However, for every analytics SDK I’ve used in client apps, I’ve not found one that could tell me any of that without some over complicated setup involving filters, graphs, charts, keys, etc.. 😴😴

I mean, I do currently use the analytics dashboard, (shocker — I use Firebase Analytics) for some of the useful information it automatically collects, like making sure there’s no huge drop offs in installs because of some issue I wasn’t aware of, or on the off chance there’s a huge spike in installs as the app got featured somewhere prominent (hi Forbes 👋).

But that’s the extent of my Analytical Dashboard prowess.

Which is another thing — if I did figure out which correct combination of buttons & levers would get me to the data I want to know, am I seriously expected to visit the analytics dashboard on a daily basis to comb through all that for each one of my apps??

Dashboards I already use:

  • Firebase Analytics – as mentioned above.
  • Crashlytics – which I use daily, checking to ensure my latest update didn’t break the app for everyone. Or Google’s latest update didn’t break my app.
  • the Play Developer Console – which I check fairly regularly for new reviews to see if they require action (why do people think “leave a review” means “ask for support”?!), checking the direction my rating is going, how many IAP’s were purchased & is there a trend, etc etc..

It’s ALOT of information. And I have to manually go in check all of it. Not to mention the fact that I find myself traveling a lot these days so I don’t always have access to good Internet or the time to go through all the data. All I really want is the cliff notes – the things I deem important, to be delivered to my inbox, once a day.. is that so much to ask?

Analytics made easy

Now before you get carried away, no — I don’t have a way to compile all that information into a single daily email. Although Google if you’re reading this — pretty please? 🙏

No actually where I’m going with this is, from that thought it occurred to me — what I really really want to know are things that are specific to my app’s usage. Not just generic things like downloads, or installs, or purchases, or even Activity views.. More specific.

Take one of my recent apps, Coffee-Working, for example. It’s a user populated app of coffee shops that are great to work from all around the world (or at least, where people have submitted places). And what’s important to me specifically for this app is..

  • How many new coffee shops were submitted today?
  • How many searches were performed?
  • Which places in the world were searched the most?
  • Are people using the map view or are people using the list view?
  • Do people click through to a coffee shop’s Instagram page to check it out or do they go straight to it’s Google Maps directions?
  • And so on….

Simple things, that require minimal effort to log, zero effort to compile & delivered straight to my inbox — no dashboards!!

And so, as us developers do when there’s no existing solution in sight (or our Googling powers for a solution have failed us) I built one.

What is DroidStats

DroidStats (such a creative name!) is a simple service that lets you log events in your app, however you chose. Activity opens, feature uses, etc.. Similar to how an analytics tool would work but much much simpler — it only counts events.

Each morning (which might end up being the evening depending where in the world you are — it’s a work in progress 😅) you get an email with an overview of all the stats you logged. This feature was used X times today. 25 coffee shops were submitted. 7 searches were performed for London while 3 where performed for New York. And so on.

It’s built on a typed event system so you can use it to log literally anything you want.

For example let’s say the type of event you’re logging is FEATURE_USE, the event itself is “Coffee shop submission”. Done.

StatManager.logEvent(context, FEATURE_USE, “Coffee shop submission”)

Every time someone submits a coffee shop you fire that event, once a day DroidStats will collate all the information submitted for your app across all your user devices & email you the daily overview.

Email from DroidStats for Coffee-Working

It’s that simple.

There’s a few pre-provided Type’s in the SDK but you can create any type you like, likewise events are also completely custom. The SDK was built to only increment a count of whatever you tell it to, in fact the only thing it does by itself is count today’s app installs & today’s app updates to a particular version. And even then this is completely optional, if you have no interest in that or would prefer to check the Play Console yourself you can turn it off — personally I want to have to check it as infrequently as possible, hence why I built this in.

My Saviour — How do I get it??

Right now the service is in beta and being used in a number of apps to trial it (dw — my users have an option to turn if off per GDPR), so if you’re interested in giving it a go you can do so by registering your interest here, if there’s enough interest I plan on opening it up to everyone to use.

Now be forewarned all you indie devs, with your minimal budgets & your ingrained Android-ness of “it should be free” (I’m not judging — I’m one of you), I do plan on making it a paid service. I know — the horror! However my reasoning is not solely monetary. If we take just my user base alone, if every user was to have it enabled, that’d be around 100k writes per day to log each install’s analytics! Which equates to somewhere around a Gazillion dollars!!! Okay probably not a Gazillion, but it would cost dollars. And as much as I love the Android community there’s no way in hell I’m paying for you to use something I built. I might not be an analyst but I can just about do maths. Sorry.

However, I’d love to hear what people would think is a reasonable monthly fee to pay for something like this, to make it accessible to everyone. Assuming you want to use it — if not you can actually probably stop reading here, save yourself some extra eye movement.

If you are interested however, you can register your interest on the Firebase hosted, Jekyll built site I threw up here, you can also submit your vote on pricing there.

And that’s that! 🤷 I would love to hear people’s experiences implementing useful analytics in their apps, am I missing the boat? Did I just reinvent the email for no reason? Is there an email service that already does this? Preferably for free? I’m all ears..

And of course feel free to leave a comment, privately message me if you’re shy & donate if you’re loaded.. you know the drill. 🤓🤓

PS: In response to the inevitable question..

DroidStats does NOT collect any user identifiable information. In fact the only information it collects is the events you submit, along with a random UUID to identify that install purely so it can show you stats like “8 coffee shops submitted by 3 users”, where UUID is used to differentiate users.

Once the email is sent all of the data is wiped, both locally & remotely, and the day starts over. Meaning no stats are ever stored more than a day. Easy.

The post From the Dashboard to the Inbox — An #IndieDev’s analytics solution appeared first on Rob J.

]]>
https://www.robj.me/uncategorized/from-the-dashboard-to-the-inbox-an-indiedev-s-analytics-solution-cf5d154b00ca/feed/ 0
Why your App Developer Résumé sucks — and how to fix it https://www.robj.me/uncategorized/why-your-app-developer-r-sum-sucks-and-how-to-fix-it-f0e3f206a6fa/ https://www.robj.me/uncategorized/why-your-app-developer-r-sum-sucks-and-how-to-fix-it-f0e3f206a6fa/#respond Mon, 30 Jul 2018 00:00:00 +0000 https://www.robj.me/why-your-app-developer-r-sum-sucks-and-how-to-fix-it-f0e3f206a6fa/ I’ve been an Android developer for 7 years, I’ve had 20+ apps on the app store, hundreds of thousands of downloads, had apps featured on Lifehacker, Gizmodo & Yahoo.com (when Yahoo was still kinda cool), worked for more clients than I can count & I’m about to teach my the first comprehensive Android Developer Bootcamp… Read More »Why your App Developer Résumé sucks — and how to fix it

The post Why your App Developer Résumé sucks — and how to fix it appeared first on Rob J.

]]>
I’ve been an Android developer for 7 years, I’ve had 20+ apps on the app store, hundreds of thousands of downloads, had apps featured on Lifehacker, Gizmodo & Yahoo.com (when Yahoo was still kinda cool), worked for more clients than I can count & I’m about to teach my the first comprehensive Android Developer Bootcamp in Bali.. Along with that experience I get a lot of questions from new dev’s wanting to break into the space, the biggest one of which being, “how do I find work”.

It’s a good question. Sensible. But I don’t get it. Mobile developers of any stretch are in demand. If you’ve been at this a minute your phone is probably blowing up everyday with at least one unknown number from some recruiter on behalf of a client looking for your skill-set, your inbox is probably full of “are you looking for a change” emails from companies who want to pay you pennies to do the same job they’ll pay contractors millions. (Alright, yes, that pay scale is an exaggeration — but the wage structure of permanent vs contract really is massively out of wack!).

My response to said question always starts with “send me your résumé”.. and therein lies the rub.

Nobody cares

Nobody cares about your interests.
Nobody cares about where you went to high school.
Nobody cares about the teamwork “experience” you gained working in Starbucks during college.
Nobody cares about how “talented” you are or how well you “work under pressure” or how your biggest weakness is your “perfectionism”.
No one is even reading your CV.

In fact, I’ve literally never had a call with a recruiter (if you’re looking for contract work, the industry is pretty much run by recruiters) who seemed like they’d even read my CV. It’s usually obvious because as soon as you answer the phone they start asking if you have skills that you’ve clearly outlined in your CV. In fact, most recruiters don’t even know what it is they’re asking you. They have no idea what Dagger is. They have no idea that when they ask you if you know Java, and then ask you if you have experience with “Object Orientated Programming”, they’re asking you a ridiculous question. What they know is keywords. What they’re looking for is keywords. That’s all people are doing, scanning your résumé for keywords..

RxJava – check
Kotlin – check
Retrofit2 – check
TDD – check

Knowing that, if you want to have a better chance at finding work, or better still, work finding you — your résumé needs to become a word diarrhoea of keywords linked to your experience, industry, work history & apps you’ve built.

And that last one is key — if you’ve built any app on the Play Store (or App Store) for yourself, list them on your CV. In fact, you don’t even have to list them.. “I have 3 apps on the Google Play Store”. Job done. (But actually have 3 apps of substance published — unfortunately 3 different versions of “Hello World” isn’t going to cut it)
Companies often look for people who have experience of building apps for themselves. Not only that but if you’re just starting out, you don’t have any “experience” so to speak, however if you have your own apps – you do.

How to fix it

This is the template I recommend to have the best chance of getting a call or an email. A foot in the door. Its worked for me & it seems to have worked for everyone I’ve recommended it to before. Plus, if you’re not having any luck atm — where’s the harm in trying this?

{ Name }
{ Phone number }
{ Title } (I advocate for [insert platform/language here] “ninja” but you can go with “guru” “master” or plain old “developer”.. whatever takes your fancy)
Experience
{ No more than 4 short bullet points outlining the most important experience you have for your industry ie
 — I have built & released 7 apps on the App Store
 — I’ve worked with 17 clients including Client A & Client B
 — I have extensive experience with [insert technology/sdk/language here]
}
Work Experience
{ Client name, date, role}
{ A few bullet point outlining the most appealing thing you did for this client ie
 — Built this app from the ground up in 3 months
 — Project involved working with sdk a, technology b & language C
 etc
}
(Repeat the last section for each project/client/app etc you’ve worked on)

Most people are unlikely to read past the first page but it’s good to get it all your work experience down, (the relevant ones – not your Saturday jobs) just in case the first page is missing some keyword the client is looking for but you happen to gained that experience 4 years ago and so it’s on the second page of your CV.

And that’s it. The goal always is to get the opportunity to present yourself for the role. Whether that be on the phone, via email or in person, and your résumé is the first step to getting that chance.

How you do from there is, I’m afraid, entirely up to you.

The post Why your App Developer Résumé sucks — and how to fix it appeared first on Rob J.

]]>
https://www.robj.me/uncategorized/why-your-app-developer-r-sum-sucks-and-how-to-fix-it-f0e3f206a6fa/feed/ 0
Cease & Desist: Can using Android’s Notification Listener violate another apps T&C’s? https://www.robj.me/uncategorized/cease-desist-can-using-android-s-notification-listener-violate-another-apps-t-c-s-9bdd8d06059a/ https://www.robj.me/uncategorized/cease-desist-can-using-android-s-notification-listener-violate-another-apps-t-c-s-9bdd8d06059a/#respond Mon, 02 Jul 2018 00:00:00 +0000 https://www.robj.me/cease-desist-can-using-android-s-notification-listener-violate-another-apps-t-c-s-9bdd8d06059a/ So I recently received a cease & desist letter from a rather large company (think Google large), I won’t say who, requesting I remove one of my apps from the Play Store, an app which allowed the user to auto-reply to incoming messages from any app, for violating their terms of service. Specifically relating to… Read More »Cease & Desist: Can using Android’s Notification Listener violate another apps T&C’s?

The post Cease & Desist: Can using Android’s Notification Listener violate another apps T&C’s? appeared first on Rob J.

]]>
So I recently received a cease & desist letter from a rather large company (think Google large), I won’t say who, requesting I remove one of my apps from the Play Store, an app which allowed the user to auto-reply to incoming messages from any app, for violating their terms of service.

Specifically relating to terms which state using the accusatory app in way that:

  • Violate, misappropriate, or infringe the rights of ████████ or its users, including privacy, publicity, intellectual property, or other proprietary rights;
  • Involve sending illegal or impermissible communications such as bulk messaging, auto-messaging [emphasis is mine], auto-dialing, and the like; or
  • Involve any non-personal use of the ████████ Services unless otherwise authorized by ████████.

(At this point I should probably state upfront this post isn’t an admission nor a rejection of the claims made, I don’t have the legal knowledge to make either. It’s more a musing on the principality of such claims based on how the functionality in question was implemented & the overarching implications for indie developers if this is one more layer we have to muddle through in order for us to responsibly release our art)

On reading the perceived violations outlined above I was kind’ve pissed — how dare they limit my creativity & try to, in my perception at least, restrict what up until this point has been a very free flowing & open Android ecosystem. The more I thought about it though, the less emotional I got & the more intrigued I became with the implications this could have if they do indeed have legal standing to make this request of me.

My main qualm was due to the way in which I implemented the functionality of the app. It makes use of the Android NotificationListener to listen to all notifications. If that notification is from a package a user has pre selected as an app they wanted to reply to, and that notification implements a quick reply Android Intent, the app would use this intent to attempt to trigger a reply. Hell, I even wrote an entire post on how I implemented this using the official Android API’s & Objects.

The solution I implemented is entirely generic, it doesn’t target the app in question specifically, it works with any app that has made use of the quick reply (or Android Wear reply) Android API’s. It doesn’t interact with any app directly, it instead populates an Intent they provided in their notification to the Android system & fires it. What happens when it’s fired is beyond my app’s control.

Not to mention the user has to explicitly turn the Notification Listener on & is given a sufficient warning popup of it’s capabilities, so I wouldn’t find it likely it could be construed that the user is in any way unaware, ignorant potentially, but not unaware.

Before I continue I should note this wasn’t the exciting tale of legal cat & mouse, ending with a victory for the indie dev, that you may have hoped it would become.

The app in question has since been removed from the Play Store while I try to get my head around the implications. If I’m being completely honest it’s been hit & miss for a lot of users, it wasn’t widely in use & it never really worked in the reliable, useful way I had envisioned. It’s not so much the pulling of the app that bothered me as much as the principle.

The problem with something like this for an indie dev like myself is just that, I am an indie dev. I don’t have a lawyer on retainer. I don’t have the legal knowledge to know if this is a meritorious claim. I don’t have the money to hire legal counsel to dispute a claim I may believe to be wrong on principal, when the app in question makes zero ROI. I mean, I don’t even have an office, I’m writing this from a coffee shop. All I do have is a laptop, Android Studio, a little know how & a $25 Android developer account.

So I guess, trying to sound the least rant-y as possible (too late?), I have 2 questions:

1 — Is it possible that one app, using public Android API’s to interact with another app’s use of public Android API’s , can violate the terms of service of said app — on a platform they don’t own or control?

2 — Is it possible that an Android Notification, with it’s accompanying intent, sent from an application is therefore owned by that application & furthermore any interaction with that intent, whether it be parsing, inspecting, extracting nested objects and/or data etc, is in violation of that applications terms of service & may in fact constitute IP infringement?

And if that should be the case wouldn’t using the Notification Listener, or listening for certain broadcasted intents, in any capacity be potentially violating any app’s terms of service?

In fact, wouldn’t it be possible to write an app that inadvertently violates another app’s terms of service, due to the generic writing of code to interact with any Android Intent not sent by the Android System itself, regardless of its origin?

I guess what I’m really getting at is this..

Does any dev, working in this area of Android development, now need to start hard coding checks to make sure their app isn’t in any way interacting with an object, created by another app, for fear of violating terms of services & opening themselves to possible legal action?

I’d love to hear people’s thoughts, my thinking being if the answer to that question is in any way yes – I might just give up now.

More where this came from

This story is published in Noteworthy, where thousands come every day to learn about the people & ideas shaping the products we love.

Follow our publication to see more product & design stories featured by the Journal team.

The post Cease & Desist: Can using Android’s Notification Listener violate another apps T&C’s? appeared first on Rob J.

]]>
https://www.robj.me/uncategorized/cease-desist-can-using-android-s-notification-listener-violate-another-apps-t-c-s-9bdd8d06059a/feed/ 0
The easiest way to get 5* reviews for your app? Just ask. (RatingManager Library) https://www.robj.me/uncategorized/the-easiest-way-to-get-5-reviews-for-your-app-just-ask-ratingmanager-library-25688f4d790d/ https://www.robj.me/uncategorized/the-easiest-way-to-get-5-reviews-for-your-app-just-ask-ratingmanager-library-25688f4d790d/#respond Thu, 08 Mar 2018 00:00:00 +0000 https://www.robj.me/the-easiest-way-to-get-5-reviews-for-your-app-just-ask-ratingmanager-library-25688f4d790d/ I mentioned on a previous post that in my opinion an app’s (relative) success always comes down to the ratings. A good rating & more people will be inclined to give your app a try, a poor rating & it’s almost certainly doomed to the app landfill.. RIP. In my experience the majority of users… Read More »The easiest way to get 5* reviews for your app? Just ask. (RatingManager Library)

The post The easiest way to get 5* reviews for your app? Just ask. (RatingManager Library) appeared first on Rob J.

]]>
I mentioned on a previous post that in my opinion an app’s (relative) success always comes down to the ratings. A good rating & more people will be inclined to give your app a try, a poor rating & it’s almost certainly doomed to the app landfill.. RIP.

In my experience the majority of users aren’t inclined to leave a rating unless the app either a — blows their mind & they have to tell everyone or b — it absolutely blows & they need warn everyone “IT SUCKS!!”. Personally speaking all of my apps end up somewhere in the middle, a place where a user doesn’t even think to leave a rating — they use the app, it does what it said on the tin, job done. The only users of mine who will definitely leave a rating are those for whom the app is buggy & therefore they think it sucks (fair play), or users who think the review section of the Play Store is actually the feature request section 🙈 (this happens wayyy to much).

So how do you get users to leave a positive rating for your apps when they’re really only inclined to rate your app when they have an issue? Simple — you ask.

Mind blowing I know but it really is that simple. Back in the day, where you’d get the email address of everyone who made a purchase of your app, I’d email every single person saying thank you & asking them if they like it to leave a review. Surprise surprise, they’d email me back that they’d never had a developer email them before, thank me, and then go on to leave a glowing review. Ahh the good old days. These days you obviously can’t do that, unless you’re doing something shady in your app to get that information.. in which case, please stop doing that.

No these days what you can do is ask for a rating in the app. I realise there’s a ton of rating manager libraries that let you do this, I’ve played with a lot of them, but my gripe with all of them (that I’ve tried — if I missed a killer one do let me know!) is twofold.

1 — They miss a crucial step.. Before I ask a user to leave a review I want to know if the user is going to leave a positive or a negative review. There’s no point prompting a user to leave a review only for them to go & give you a 1* “This app sucks” review. No what I want is to ask the user a — do they like the app, and if they say yes b — ask them if they can leave a rating. If they say no to liking the app then I want to be able to find out why so I can make the app a better experience for them & hopefully convert them to a positive review.

2 — They force me to handle the metrics I used to determine WHEN to show the prompt myself. Some libraries let you set how many days since install before it will prompt them to leave a rating, some have a few other similar metrics you can set but what I really want is to be able to set my own custom rules. For example, with my app ReadItToMe I only want to show a rating prompt to users who’ve had a certain amount of messages read to them. For Remindee maybe I only want to show the prompt to users who’ve set a reminder in the past month, otherwise when those users who haven’t used the app recently next open it, the first thing they’d see is a prompt asking for a rating. That just doesn’t feel like good UX to me.

So, in case the title of this post didn’t give it away, I built a library to handle all of that for me.. 🤓

My RatingManager Library

The first thing to note is this library provides 3 different prompts a user might see. The first one is the initialPopup which is where you ask “do you like this app” (obviously be a little more “human” with your copy 😂).

Then if the response to that is yes, the user will see a prompt asking if they’d like to leave a review to which they can respond “yes”, “not now”, “don’t ask me again”. (I should note all of the dialog text is fully customisable so the text displayed can be anything you like.) If the user responds “no” they don’t like the app there’s an optional dialog (you can toggle this feature) which offers them an option to email you to tell you why they don’t like the app. I’ve found this personally to be very useful in converting potentially negative reviews into positive ones by resolving whatever their issue was.

How do I implement it?

Firstly you need to create an instance of RatingManager. You do this by using the builder – RatingManager.Builder(context)

From that you can set your rules for when the prompt should show. The options provided are setMinDaysSinceInstall & setMinDaysSinceFeedback (the amount of days after they sent you a feedback email before the prompt displays again), setMinDaysSinceAskLater (the amount of days after they chose the “ask me later” option before the prompt displays again). That covers the basics. Then if you want to add your own metric for example maybe you don’t want the prompt to show until the user has played your game at least 50 times you can do add a custom rule such as:

new RatingManager.Builder(context)
.setMinDaysSinceInstall(MIN_DAYS_SINCE_INSTALL)
.setMinDaysSinceFeedback(MIN_DAYS_SINCE_FEEDBACK)
.setMinDaysSinceAskLater(MIN_DAYS_SINCE_ASK_LATER)
.showFeedbackOption(true, {feedback email address})
.addCustomRule(() -> DataManager.getGameCount() >= 50)

Furthermore you can completely customise the dialog displayed by using the RatingDialogOptions.Builder. An example being:

new RatingDialogOptions.Builder(context)
 .setDialogThemeResId(R.style.AppTheme_Dialog)
 .setInitialPopupMessage("Do you like this app?")
 .setInitialPopupPositiveBtnText("Yes")
 .setInitialPopupNegativeBtnText("No")
 .setInitialPopupLaterBtnText("Ask me later")
 .setRatingPopupTitle("Leave us a review!")
 .setRatingPopupMessage("You can help others discover this app by leaving us a 5* review")
 .setRatingPopupPositiveBtnText("Rate now")
 .setRatingPopupLaterBtnText("Maybe later")
 .setRatingPopupNeverBtnText("Don't ask me again")
 .setFeedbackPopupTitle("We're sorry to hear that")
 .setFeedbackPopupMessage("We'd love to know why so we can help improve the app for you & others")
 .setFeedbackPopupPositiveBtnText("Email us")
 .setFeedbackPopupLaterBtnText("Maybe later")
 .setFeedbackPopupNegativeBtnText("Not right now")

You can then apply this to the rating manager via setRatingDialogOptions.

Once you’ve built your rating manager you can simply call mRatingManager.showDialogIfRequired() from anywhere your app (I call it in the MainActivity’s onResume method) & if the rules are met it’ll show up, if not it won’t. It’s that simple.

You can find the library on GitHub here and you can add it to your project from Gradle via the JitPack repository (instructions on Jitpack if you don’t know how).

Feel free to use it, pick it apart, improve upon it.. And let me know what you think in the comments 🙂

A few things to note:

  • There’s a few other methods in the RatingManager such as shouldShowRatingDialog which can be used to check if the dialog should be shown, this is useful when you don’t want to show a dialog but want to do some other action but still want to use the logic within the library to determine when a rating prompt should be shown (ie maybe you want to show a notification instead).
  • There’s also a RatingManagerMockBuilder class which can be used for testing against the rules you’ve set etc.. An example would be:
new RatingManagerMockBuilder(context)
        .setDaysSinceInstall(7)
        .setDaysSinceAskLater(13)
        .setDaysSinceFeedback(45)
        .setFeedbackBuild(BuildConfig.VERSION_CODE - 1)
        .setNeverAsk(false)
        .setRated(false)
        .build();

You can mock when the prompt was last shown, if a rating was already left, which build the user last emailed you with feedback about etc..

The post The easiest way to get 5* reviews for your app? Just ask. (RatingManager Library) appeared first on Rob J.

]]>
https://www.robj.me/uncategorized/the-easiest-way-to-get-5-reviews-for-your-app-just-ask-ratingmanager-library-25688f4d790d/feed/ 0
Boilerplater Libraries: Device Utils https://www.robj.me/uncategorized/boilerplater-libraries-device-utils-7d8763e6bb7f/ https://www.robj.me/uncategorized/boilerplater-libraries-device-utils-7d8763e6bb7f/#respond Thu, 01 Mar 2018 00:00:00 +0000 https://www.robj.me/boilerplater-libraries-device-utils-7d8763e6bb7f/ A lot of my apps make use of things like the users contact list, evaluating bluetooth & wifi connections to trigger some particular action, displaying a list of the users currentlyinstalled apps.. Many basic things that have a few common pitfalls (OOM app icons anyone?) & that are pretty tedious to write over and over… Read More »Boilerplater Libraries: Device Utils

The post Boilerplater Libraries: Device Utils appeared first on Rob J.

]]>
A lot of my apps make use of things like the users contact list, evaluating bluetooth & wifi connections to trigger some particular action, displaying a list of the users currentlyinstalled apps.. Many basic things that have a few common pitfalls (OOM app icons anyone?) & that are pretty tedious to write over and over again across multiple apps. So I put them into a library 🤓

Nothing overly fancy, just a few util classes that I use in numerous production apps that might just save you a bit of time (& potential headache). They’re also (for the most part) wrapped in RxJava2 observables so if you’re using Rx in your app (& if you’re not I’d highly recommend you should be) things like getting the currently connected Bluetooth device is now a ton easier to implement cleanly into your app.

They’re all very straightforward &sensibly named (at least to me 😛) so I’ll just give a quick overview over some of the most useful methods so you can determine if they might be of any use to you..

AppUtils

getInstalledApps(Context context) — Observable that returns a list of the currently installed apps on the device with the intent “android.intent.action.MAIN” & the category of android.intent.category.LAUNCHER.

This class also provides method to get a bitmap of the app icon for the provided package name (getAppIcon) as well as another straightforward method to get the app label for a particular package (getAppLabel).

ContactUtils

This class provides numerous methods to query contacts based on their name, their contact id, their phone number, their uri (supplied via the extra People field in some chat apps), as well as methods to get a list of all the contacts on a users device (getContactsObservable).

As well as that it also provides methods to retrieve a contacts avatar & another to retrieve all phone numbers for the specific contact.

WifiUtils

getSavedWifiNetworks — Returns a list of all the saved wifi networks on a users device.

getConnectedWifi — Returns the currently connected wifi network.

This class also contains numerous other method to retrieve the wifi name from an id & vica versa, pretty useful when a broadcast receiver for wifi only returns an id.

BluetoothUtils

getPairedDevices — Returns a list of all the bluetooth devices paired to a device.

getDeviceAliasName — Easily retrieve the name of a bluetooth device based on it’s bluetooth address, pretty useful as it’ll return the custom name a user might have set for their bluetooth device if there is one i.e. “Kitchen Speaker”.

getConnectedBluetoothDevice — Returns the currently connected bluetooth device if there is one.

Simples.

There’s a few other methods in each class not mentioned but they’re all pretty sensibly named so feel free to take a look & see if there’s anything useful to you 🙂

You can find the library on GitHub here and you can add it to your project from Gradle via the JitPack repository (instructions on Jitpack if you don’t know how).

Feel free to use it, pick it apart, improve upon it.. And let me know what you think in the comments 🙂

Side note: This library will be continuously updated as my apps are built upon & I find needs for other functionality but as it stands this is the library currently used in ReadItToMe, Can’t Talk & Remindee so I’m pretty confident in it for production use.

The post Boilerplater Libraries: Device Utils appeared first on Rob J.

]]>
https://www.robj.me/uncategorized/boilerplater-libraries-device-utils-7d8763e6bb7f/feed/ 0