Showing posts with label android. Show all posts
Showing posts with label android. Show all posts

Wednesday, June 18, 2014

Android and iOS app discovery and search engines overview

Introduction

Recently a friend of mine asked how to check if an app or type of app with certain functionality already exists. He had a great idea for an app (he thinks :) so wanted to figure out: does such an app already exist?
Entering the name of an app is not sufficient of course, you want to search on offered functionality too, since the name of the app might not match the functionality. E.g Tinder is a dating app, but the name Tinder doesn't show that (at least not in its early days!).

I knew there are a couple of websites and apps that provide that, but had a hard time coming up with more than one. This list is pretty ok, but already partly out of date.
So I decided to create this blogpost with a list of the current recent websites and their apps and some(!) app-only search apps that provide app discovery.

Main focus of this post will be on app search engine websites, not apps! Mainly because the research for an existing app you'd probably like to do behind a computer (laptop or PC), not a phone or tablet, since it requires quite some typing. E.g all types of ways of describing the functionality.
Also most focus will be on Android, but also some iOS search engines will be mentioned.

Below is a list of app search engines I found, including a screenshot and my short take on it. If any search engine is missing, please leave a comment and I'll check it out. If appropriate I'll add it to this page!

App discovery and search engines

1 Standard stores

Of course the standard app stores have their own search engine, either online and/or on the platform itself, e.g:


There are quite some more 3rd party app stores like SlideMe, but usually these are considered less important than the "big ones", so chances are minimal that there's an app in those stores which is not in the big app stores. But for adult content you mighthave to check those.

2 AppBrain

URL: http://www.appbrain.com/
Platforms: Android
Has app: yes


3 Quixey

URL: https://www.quixey.com/
Platforms: Android, iOS, Windows, Blackberry
Has app: yes


4 App Annie

URL: http://www.appannie.com/
Platforms: Android, iOS, Amazon, Mac
Has app: yes but can't tell from the screenshots if it includes searching etc



This is more intended for app analytics and ASO (app store optimization) and SEO but still usuable for checking what type of apps exist, e.g by keyword.
App Annie recently purchased Distimo, another well known app statistics/analytics platform.

Most parts are for free, "enterprise" is not.

5 AppGravity

URL: http://appgravity.com/
Platforms: Android, iOS
Has app: yes



6 AndroLib

URL: http://www.androlib.com/
Platforms: Android
Has app: no


Still refers to "Android Market" instead of Google Play.

7 AppsZoom

URL: http://www.appszoom.com/
Platforms: Android, iOS
Has app: yes


8 MobileDevHQ (formerly known as AppStoreHQ)

URL: http://www.mobiledevhq.com/
Platforms: Android, iOS
Has app: no


Similar to AppAnnie more intended for ASO and analytics and keywords optimilization, but can still come in handy. 

9 Regular search engines

More and more regular search engines are optimizing for finding apps. For for example Google, Apple and Amazon it's of course in their own interest to help people find apps for their platform.

10 App only

There are also many apps out there trying to help you find apps. I did not investigate those, but a query you could run to find a list is this one

11 DIY

If none of the above work for you, you could consider writing your own search engine/crawling engine. Here's a script for the Google Play store.


Wednesday, July 3, 2013

Integrating Twitter 1.1 api in Android with oauth including app-only authentication

Introduction

One major change is that all API calls now have to be authenticated (and authorized) via OAuth: "user authorization (and access tokens) are required for all API 1.1 requests". That means any action on Twitter that is related to a user has to happen this way.

Luckily there is also a so called application-only (app-only) authentication for actions that don't require a user as a context. Those are requests on behalf of an application. Think about search and getting a timeline for a user. One thing that's definitely different from the other calls is that this app-only authentication has to use OAuth 2.0, while the user-context based authentication has to use OAuth 1.0a!

In this post I'll describe how to implement both, including a full working Android code example.




App-only Twitter 1.1 Authentication OAuth 2.0

Information about this principle can be found here. As said above, you can use it for accessing the Twitter API 1.1 endpoints that do not require a user action. For example searching for tweets falls in this category; just like getting a tweet from a user's timeline. Tweeting a status update does not fall in this category, because a user has to give permission for this. You can see if a Twitter API call supports application-only authentication if there's a rate-limit specified with it. E.g for the search it says on the right: 450/app. So that's accessible via the app-only API. But getting a user's direct messages isn't, it only has 15/user specified.

The problem with this userless-context API solution I had was my app was using the Signpost library for the Twitter 1.0 API. I preferably would have used that lib for this app-only authentication too, but the Signpost still does not support OAuth 2 yet...
Then I tried to use the Scribe library, which supports OAuth 2.0, but I couldn't get that working quickly. 
Therefore I went back to the basics and used plain HTTP POST and GET methods, using this blogpost as a starting point. All pieces to get it working are in that post, but not available as one complete code set, that's why I made this post. Plus you might come along a few problems too... also described below.

Making it work

Easiest is to refer to the above mentioned Crazy Bob's blogpost to get started and what's happening why.
In my attached example code (see bottom of this post) most of the logic occurs in OAuthTwitterClient.java. In OnCreate() the UI is set up, including the consumer, provider and client for the OAuth 1.0a using Signpost (see next section for details on that part).

When a user clicks on the top button of the application, named '1a) Request bearer-token via app-only oauth2') the getBearerTokenOnClickListener.onClick() method is invoked. There you see the bearer token getting retrieved via requestBearerToken(), as described in Crazy Bob's blogpost.
That should give the bearer token which you need as mentioned here
Then to get a tweet from a Twitter user (in the code I'm using 'twitterapi'), click on the second button named '1b) Get tweet from user via app-only oauth2'. That invokes its listener which invokes fetchTimelineTweet(), which in turn gets the tweet using the bearer token, after which the tweet is displayed on the screen.

Troubleshooting 

I ran into a few issues:
  1. When fetching the tweet, the getInputStream (in readResponse()) was giving a FileNotFoundException. Turns out that since Android 4 ICS (Ice Cream Sandwich), this can occur because the conn.setDoOutput(true) causes the request to be turned into a POST even though the specified request method is GET! See here. The fix is to remove that line.
  2. When invoking requestBearerToken() two times in a row on Android 1.5, the responseCode showed as -1 sometimes; mainly when waiting about 30 seconds before invoking it again. That turned out to be a known issue for Android before 2.2 Froyo. For that the method disableConnectionReuseIfNecessary() is put in place. 
  3. It seems a bearer token lasts "forever", until it is revoked/invalidated. Twitter can do that but you can do it yourself too, via the invalidate_token API call.
PS 1: no the code is not optimal, for example the error handling can be done a lot better
PS 2: and no the UI won't win any "Best Interface" prizes
PS 3: yes all network calls should be done on a non-UI thread really. Yup that's some homework for you too :)


User Context Twitter 1.1 Authentication OAuth 1.0a

For updating a user's status (i.e. tweeting), you have to be authenticated and the user has to have authorized you to perform those type of actions. Usually the user authorization happens by switching to a browser in which the user has to log in to Twitter and say whether the application is allowed to perform the requested actions.
Luckily, for this type of authentication still the Signpost library can be used since its based on OAuth 1.0a.

Making it work

After upgrading to the signpost-core-1.2.1.2.jar and signpost-commonshttp4-1.2.1.2.jars, everything almost worked immediately. Especially make sure to use CommonsHttpOAuthProvider as it says on the Signpost homepage in the Android section.

Since my app still runs on Android SDK 1.5 (yes!) I tested the solution on a 1.5 emulator.
But then I ran into my first problem, authentication failed with: javax.net.ssl.SSLException: Not trusted server certificate.
To get this working, I had to make sure all the Twitter Root CA certificates are known. For that I used this blogpost. See the class CrazyBobHttpClient for the implementation. After putting in all VeriSign certificates one by one (search for the string 'class 3 Public Primary Certification Authority - G' in the .pem file mentioned in this Twitter Developers Forum post to find all 5 of them).
In short, issue the following command to have each one added to your own keystore:

C:\>C:\jdk1.7.0_17\jre\bin\keytool -import -v -trustcacerts -alias 3 -file verisign_cert_class3_g5.cert -keystore twitterkeystore.bks -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-jdk16-145.jar -storepass ez24get

So in the above I'm adding the VeriSign G5 certificate. The -alias 3 you have to increase each time. And notice the password used: 'ez24get', you'll find that in the CrazyBobHttpClient class.

If you see:

Certificate already exists in system-wide CA keystore under alias
Do you still want to add it to your own keystore? [no]:  

answer with: yes

And do the same for the DigiCert certificates, you only need the 3 Root ones. You can find the separate certificates and keystore in the res/raw directory of the example application. The separate certificates don't have to be provided, I just left them there for reference.

After performing all these steps, I was able to authenticate and even tweet. Just as a test I switched back to the DefaultHttpClient(). And the authentication now goes fine using that implementation too! Strange, because AFAIK that one just uses the default system keystore, and before it gave the "not trusted server certificate error"... Strange to me. If anybody knows the reason why, please leave it in the comments!

PS: maybe even cleaner is to add your own created keystore to the existing keystore(s). See the comments at the top in the CrazyBobHttpClient.java.


Outstanding Issues

Well, issues.... more like questions: 
  • Why does the DefaultHttpClient() suddenly work (see previous section)?
  • It would be nice to have an example for app-only authentication using an OAuth 2.0 library like Scribe instead of using this low level POST/GET code.
  • In the LogCat I see these messages appear after each API call:

    07-01 18:59:03.575: W/ResponseProcessCookies(732): Invalid cookie header: "set-cookie: guest_id=v1%3A137270514572832152;  Domain=.twitter.com; Path=/; Expires=Wed, 01-Jul-2015 18:59:05 UTC". Unable to parse expires attribute: Wed, 01-Jul-2015 18:59:05 UTC
    Why do these appear?
  • It should be possible to get the Twitter Timeline for a given user using your application's private access tokens (and thus not requiring the app-only authentication). Of course this is not recommended, because that would mean you'd have to put these tokens in your application. But for some situations it could be an option. See this PHP code on how that can be done.


The Sample Code Application

You can find the complete source code here
It should run on anything from Android 1.5 and higher.
It has been tested by me on:
  • Android 1.5 emulator
  • Android 4.0 Samsung S2
  • Android 4.0 emulator
You might want to remove the scribe-oauth2.0-1.3.0.jar from the libs directory, just left it there for reference. Same goes for the certificates in the res/raw/ directory. Removal is not necessary, it just uses unnecssary storage.

How to run it

  1. Load the project in your favorite IDE or whatever you use to build Android applications.
  2. In OAuthTwitterClient change the values of CONSUMER_KEY and CONSUMER_SECRET to your own application keys.
  3. Also modify the CALLBACK_URL to your application's callback URL. Note: you can also make an OOB (out-of-bounds) call that doesn't require a callback URL, but that's for you to figure out, didn't spend time on that.
  4. Deploy it. You should see a screen like this:


  5. Press button 1a) to get a bearer token. After pressing it should look like:


  6. Press 1b) to get the tweet. That should look like:


  7. Press button 2a) to get authenticated and authorized to post a tweet on behalf of a user. When the browser opens, enter the credentials under which account the tweet should be performed (no screenshot for that one). Then authorize the app (click Authorize app):




    It should get back to:




    Note: I am getting a message that the certificate is not ok. This is I think because the quite old 1.5 emulator does not have that root certificate:




    Because the certificate looks fine: 
  8. Enter a tweet text.
  9. Press button 2b) to tweet the text above it. That should give you:


  10. At the bottom of the screen you can see some statuses of what's going on. If you see any error message, check the LogCat.
  11. That's it!
And here two examples of what it can look like after integration in an app, here in the free Android SongDNA app.
Showing all tweets with the words 'shakira' and 'underneath your clothes' in it.



And the different actions possible on each tweet:



Sunday, May 20, 2012

BlackBerry Playbook Tablet for Android porting

Houston, we got it in the mail: a free RIM Blackberry Playbook Tablet because I submitted on time an Android app to the BlackBerry App World. The Android app runs within the Playbook Android emulator (yes so the app is native Android! Pretty cool I'd say!

Booting
Below a few bootup screenshots:

The bootup time for the device is quite long, definitely longer than the Samsung Galaxy Tab 10.1 for example.

Home-screen and taskmanager
This is what the home-screen looks like:
This is what the taskmanager looks like:
The way to go back to the home-screen or switch to another app is by swiping from the button of the screen up.

The simpel app running
And the app runs fine on the real device too :) Those Android figures are also checking it out as you can see...
Those four buddies are called: Don Pablo Calaveroid, Longevity, Fortune and Blessing. The last three were released because of the chinese Year of the Dragon. Too bad they sent one with still the USA power plug attached to it. Luckily I had a converter (as the globetrotter I am ;)...
This is how the tablet 7" screen compares to the Samsung 10.1 (note both screens are very clear, the picture is only trying to show the difference in size of 10" vs 7"):
Tetris just runs fine on it (but that's a native app), the screen is very clear:


Some generic feedback:
  • The tablet smells rubbery because it has rubber on the back. Probably done as anti-slip, but even your fingers start to smell like it.
  • A bit annoying was that you have to watch the Basics tutorials when doing the setup.
  • Like the virtual keyboard, feels like lot less typos than similar sized touch screens


Porting
During converting the app to the PlayBook Android 2.3.3 or higher runtime, I ran into a few issues:
  • For emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, emailBody) statements needed to add .toString() to emailBody because otherwise you get an exception telling that EXTRA_TEXT is not a String.
  • After each re-install of the app on the emulator, nothing seems to be remembered of the preferences. E.g had to accept the EULA each time after a new deploy, while this does not happen on the "regular" Android emulator. Also works fine on the real Playbook device.
  • In my app the user can swipe left to right and vice versa to browse through tips. On the Playbook emulator the text on bottom of the screen was not always correctly updated. It was lagging behind.
  • When trying to open the browser with an Intent and URL in the simulator, it says 'No network connection'. Strange, the pre-installed browser on the home screen works just fine... Seems similar to this reported issue.

Saturday, April 7, 2012

Why cancel-button now also first on Android?

Just wondering about this big time...

Why is Android switching the standard buttons order for Android 4.0 and higher? See for example this screenshot, the cancel button is now the first button!
I was always taught that what the user most likely wants to do should be first, and so the dismissive button last.
And Cancel is usually not what to user wants to do!

Apple is doing it also in their iOS, is Android now just plainly copying it?
Definitely prefer Cancel at the end, (western) reading occurs from left to right, so now one always has to read/skip the dismissive button!

From the expert the conclusion is just to be consistent. That means at least within the Android platform it is not consistent between pre 4.0 and 4.0+... I think an unneeded and unnecessary change.

Thursday, February 16, 2012

Summary of presentation with tips on creating visually appealing Android applications that scale to various screen sizes

Interesting points from a presentation in which Eric Burke shares tips on creating visually appealing Android applications that scale to various screen sizes.
The session focuses on custom views, rounded corners, scalable drawables, gradients, shine, holograms etc.
Eric is the St. Louis Engineering Manager and Android Tech Lead with Square.

06:00 Use alpha compositing with anti-aliasing, not canvas.clipPath() because you get jagged pixelated corners

17:25 triangular shine

29:00 Check the imeOptions for EditTexts/keyboard input. E.g imeOptions="actionNext" for example. Or "actionDone". NOTE: does not work on HTC Sense UI! So "actionNext" seems safest (might work, might not, but no harm done). For "actionDone" you still need a button for it on your screen anyway.
31:00 SafeViewFlipper code because the standard one crashes your app eventually (guaranteed) on pre-HoneyComb!
35:00 Holograms! Using the accelerator it acts as if it's shiny. Cool!
39:00 demo of cards sliding in/out and hologram on emulator

Wednesday, February 1, 2012

Android Tablet project lessons learned

A little while ago I had the opportunity to work on a tablet-only app, running Android 3.1 or higher.
The app consists of only one screen, on which everything can be done.Therefore the app basically consists of one Activity and several Fragments (about 4 to be precise).
The Activity controls the Fragments for potential re-use of the Fragments, as recommended. As a basis the official Honeycomb Gallery project was used. Quite unique to this app was though that it has to have 3 columns instead of the usual 2...

To get a feeling for the app, here are some screenshots:


The very first time the user has to log in, after which a sync with the server is done. Note the progress bars showing in the background.


Synchronisation in progress... Notice that the title is indented for some reason. Needs some investigation why it shows here, and not in the Login DialogFragment...


After the data has been loaded, the user can filter all data by selecting from the dropdowns, and selecting items in the lists. The rightmost column then shows a list of questions, dependent on the selected filters. Many types of questions and actions had to be supported; beside the ones you see, also built were dropdowns, multiple radiobuttons, view images and PDFs, etc.

Lessons learned

  • Prevent EditText in a ListView from losing focus/text disappearing:

    android:windowSoftInputMode="adjustPan"



  • Sometimes Eclipse/DDMS loses the connection with the emulator, it just can't "see it" anymore. For example when you want to run an app in the emulator, the start dialog does not show your running emulator. To fix that run 'adb kill-server', that restarts the server. After this the emulator should be detected again.

  • You can't disable a RadioGroup such that it disables it contained radiobuttons. You have to disable each radiobutton within that radiogroup seperately.

  • Note that all Activities in your app might have had onDestroy() called, but the Application instance might still be around! So watch this when you have static variables, like a reference to the database... it might still be open when your Activity's onCreate() is called again!

  • The 4.0 emulator was taking ages to start up (gave up after about 30mins). Then I compared the settings of my 3.1 AVD with the new 4.0 AVD. It turns out that the hardware property 'Abstract LCD density' setting of the 4.0 AVD is set by default to 240, which means high density. That implies a lot of pixels to draw. Read somewhere that that's one of the issues of a slow starting AVD. So I changed that property to be the same as of the 3.1 AVD, so made it 160. After that, the 4.0 AVD started about as fast as the 3.1 AVD (several minutes).
    I also reduced the 'Device ram size' setting from 512 to 256, but don't think that was the one that fixed it. See also here.

  • The monkeyrunner recorder script records really slow on a 160 density Android 3.1 AVD. Playback is a lot faster. Here's a bunch of instructions, including 2 scripts for recording/playback which you can also find here and here.

  • Had a problem trying to show the progress indicator in a ListFragment with the correct top margin. Tried adding addHeaderView() on the fragment to show some text above a fragment list. That works fine, except: when showing the progress indicator, it does not take into account any (top margin) LayoutParams being set! The app needs to set that to stay below the Action Bar which is in "overlay" mode. As soon as the adapter is set, the margin is taken into account correctly. I found the following here: in onCreateView() there is also a similar problem, they set the layoutparams explicitly again:


    // For some reason, if we omit this, NoSaveStateFrameLayout thinks we are
    // FILL_PARENT / WRAP_CONTENT, making the progress bar stick to the top of the activity.
    ... code where LayoutParams are set ...

    Tried several combinations of setting the LayoutParams, but those didn't work either. As a workaround now I have added a FrameLayout above the <fragment> in the main Activity xml. It is a <fragment> with a FrameLayout with a LinearLayout with some TextViews in it. For that one the top margin is set, and since the ListFragment is below it, it is pushed down correctly, even when the progress indicator is shown. Maybe the FrameLayout is causing the problems? Maybe a custom list fragment element would fix it.

  • How to open a PDF from an app's local private storage:


    // Assuming you have stored a file like this (note the mode):
    FileOutputStream f = context.openFileOutput("theFilenameYouStoredThePdfAs", Context.MODE_WORLD_READABLE);

    ... write bytes to f, do other stuff...

    // Then you can get a reference for it for an external app via:
    File file = activity.getFileStreamPath("theFilenameYouStoredThePdfAs");
    if (file.exists()) {
    Toast.makeText(activity, activity.getString(R.string.openingFile), Toast.LENGTH_SHORT).show();
    Uri path = Uri.fromFile(file);
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(path, "application/pdf");
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    activity.startActivity(intent);
    }


  • Definitely take a look at the latest Google I/O app, that's always a good reference for figuring out how to do specific things; I consider it a best-practices app (I've also seen it being referred to in blog posts/presentations from the Android team, can't find them now :).

Android invasion of the Mini Collectible - Series 02!


They are 16 in total, but sort of left out the doubles: Bernard, Hexcode, Cupcake, GD-927, Iceberg, Greeneon, ???, Blackbeard, Racer, Bluebot, Hexcode, Rupture, Noogler.

Monday, October 31, 2011

Android UI Development: Tips, Tricks Techniques from Devoxx 2010 Summary

Quick summary of the most important/new tips from the Parleys talk Android UI Development: Tips, Tricks Techniques at Devoxx 2010. Now free for viewing.

13:40: tool for tracking allocations in Android via Debug.setAllocationLimit(int)

25:15: example showing the use of DDMS, Heap Analysis Tool, hprof, adb shell dumpsys meminfo

36:14: don't over-invalidate when redrawing. Only redraw what you have to. Demo shows use of traceview to detect too much being re-drawn.

44:20: (new) hierarchy viewer example. You can use Annotations to export your own values, i.e. the ones you want to check in the viewer.

48:09: you can use the ViewStub to not create your Views too early. Kindof lazy-instantiation. Uses android:inflatedId.

51:15: better way of using an ImageView (e.g icon) with a TextView inside a LinearLayout: android:drawableLeft(Top/Bottom/Right). Much more efficient because not using 3 Views.

51:45: shows use of the static analysis tool 'layoutopt'. It needs an layout XML file and it tells you what can be improved.

53:45: the one you should know by now :) Don't inflate() views in ListView.getView(), re-use. Shows a graph of performance differences if you do vs if you don't.

54:24: tip on View.setDrawingCacheEnabled(true), and explains quicky how Android uses it when you flick a ListView.

Monday, September 26, 2011

Segmented Button Control in Android

For an Android project I did a little while back I needed a pair of so-called segmented buttons, as they are named in iOS (see for example the official iOS API for it).

The idea is that when you select one of the buttons, it usually shows just about the same related data but in a different way (at least that's my translation of what it should do).
The Android version of it which I provide below looks currently most like the third and fourth example in the above image. But of course you can change the code to style it like the others too.

Android does not have this control out of the box. Luckily, I found a reference to the source of the Foursquared app.
In there, a segmented button control is created! Big thanks for the creators of Foursquared for sharing the code and thus of the segmented button control :)

Since the Foursquared app is quite big, I extracted the segmented buttons code from it and made it an Eclipse project, for your and my convenience. It's now just one Activity and the widget.
You can get it from here and use it anyway you like, following the Foursquared source Apache License 2.0 rules of course.
Here's two screenshots of what it will show:


Above shows at startup by default the Recent button is selected


Above shows after clicking on the Nearby button

Please not that I kept it as basic as possible; for example you'll keep seeing the progress indicator from layout/empty_loading.xml, which of course should be replaced with actual data. I also did not go through all the drawables etc to see which ones are needed and which are not, so you'll still have to clean those up.
Code has been tested on 2.1-update1.

Saturday, August 27, 2011

Using dependency injection and annotations in Android for cleaner code

Interesting overview of possibilities for cleaner code using dependency injection and annotations by using the RoboGuice and/or


Android Annotations


frameworks:

Clean code in Android applications

Definitely interesting possibilities, but these frameworks are quite young. And is it really worth the effort, since Activities and Intents can also be used quite effectively to keep the app loosely-coupled? Also the libraries will increase the app's size.

Not convinced yet :)

Friday, August 19, 2011

Houston we got them: Android Mini Collectible Special Edition Summer 2011



1st: "Greentooth is a master of mind control technology."
"One shake of his hand and you are under his command."

2nd: "The Hidden Task waits in the shadows, always prepared to set things right."
"The Hidden Task makes no sounds and leaves no traces."

3rd: "El Poderoso fights for justice, equality.. and fancy belts."
"El Poderoso is ready to wrestle the worst villans and pin down the biggest kingpings."

4th: "Cycle-On is a power-hungry Android who will stop at nothing."
"Cycle-On's tough metal casing makes it a formidable opponent."

Thursday, August 18, 2011

Android project lessons learned

Quick post with some bullet points of lessons learned during my last Android project:

  • To be able to keep strings in strings.xml apart, prefix them with the name of the Activity they belong to. So listOrdersOrderNr instead of just orderNr.

  • If you inflate() a view without the parent, it won't take into account the parent's attributes like padding! Sadly apparently that trick does not seem to work in the getView() method. For that you'd have to compute the padding in dp and then set the padding of the view. E.g:

    
    
    float scale = getContext().getResources().getDisplayMetrics().density;
    float dpSize = 10 * scale; // So 10 would be the number of dp you want
    buttonRow.setPadding((int) dpSize, (int) dpSize, (int) dpSize, (int) dpSize);



  • When using a RelativeLayout, make sure you refer to other elements using "@id/someId", and not "@+id/someId". If you use the latter, Android will just create a new id named "someId" and will place the layout starting from the top of your Activity. Pretty obvious this one (the docs say the "+" creates a new id) but still an easy one to miss... So don't do:

    
    
    android:layout_below="@+id/feedbackEmailLogsSeparator"


    But use:

    
    
    android:layout_below="@id/feedbackEmailLogsSeparator"



Monday, July 4, 2011

Android pro tips for advanced expert Android developers

Short summary of the Google I/O 2011 session "Android Protips: Advanced Topics for Expert Android App Developers". Full presentation can be found here.

6:35: the parallel Activity pattern: based on SDK version, start another "parallel" code line. Easy check to see what your SDK version is so you can distinguish use of more advanced APIs as for example available since HoneyComb.
8:20: use interfaces for backward compatibility
9:09: get real user feedback before launching. Note you can also of course use Analytics to log an exception!
10:38: example of doing A/B testing
11:35: use Android Market for beta testing. It seems there's no special facility for this in the market, you just have to control it yourself... e.g password protect, obscure the name.
12:31: But don't forget to protect your package name before someone else takes it! So upload and save it but don't publish it.
13:43: don't assume the default (natural) orientation on a device is portrait, e.g most tablets start in landscape. The orientation sensor's default is also 90degrees "off"
15:00: Don't track devices to detect unique installations. Give your app a unique id using a UUID, store it in the prefs.
20:00: the app's data has to be always fresh if possible, the user does not have to wait. See also this recent post: http://android-developers.blogspot.com/2011/06/deep-dive-into-location.html
25:51: how to get fresher data
32:00: make your services intelligent like do everything async, let it die asap
33:13: make your app psychic: leverage what the phone already knows, remembers what the user has told it. Like the Account Manager. Preferences should follow the user across devices and resets (which means storing them offline). Give users the ability to delete their preferences, using the Backup Manager to backup shared preferences.
38:21: intercept links using an Intent Receiver
39:25: make your apps adaptive. E.g: behave as expected, optimize for different user experiences. You can specify the IME keyboard behavior by setting the appropriate action (Go, Search, Send, Done etc). Use android:imeOptions for that
43:20: handle volume and playback buttons icw requesting audio focus for playback
47:10: provide translations for your app and title description to have it better found in people's native language
48:00: make your app smooth: make everything async. Loader and CursorLoader are available since HoneyComb.

Wednesday, June 29, 2011

Android Bluetooth bugs on Galaxy S2

Just a quick post regarding Bluetooth on the Samsung Galaxy S II.

Recently I've been creating an app that uses Bluetooth for sending data between devices (not FTP just strings). A pretty good introduction can be found here, though some things are missing from it. Like an example how you can register a BroadcastReceiver for the ACTION_SCAN_MODE_CHANGE. Just in case you are looking for it, here it is:



// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy

...

// Creates a BroadcastReceiver for ACTION_SCAN_MODE_CHANGED for BT discovery
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {

public void onReceive(Context context, Intent intent) {
if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) {
int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,
BluetoothAdapter.ERROR);
String strMode = "";
switch(mode){
case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
strMode = "mode changed: SCAN_MODE_CONNECTABLE_DISCOVERABLE";
break;
case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
break;
case BluetoothAdapter.SCAN_MODE_NONE:
strMode = "mode changed: SCAN_MODE_NONE";
break;
default:
strMode = "mode changed: unknown mode " + mode;
break;
}
Log.d(TAG, strMode);

}


Basically a device can be made discoverable by others, or a device can be set to find other devices (discover). Being discoverable is configurable up to 300 seconds max, finding other devices is not configurable and about 12 seconds.

Making a device discoverable entails the following code:


Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);
startActivityForResult(discoverableIntent, REQUEST_MAKE_BT_DISCOVERABLE);


That should enable Bluetooth if it isn't already and make the device discoverable for 120 seconds, and the result of the intent comes in at onActivityResult(). For receiving Bluetooth state changes you can register a BroadcastReceiver. This all works fine on a Nexus One.
But not on all devices, their Bluetooth stack contains bugs and behaves differently:

Samsung Galaxy S II
  • When you make the device discoverable, its state should become SCAN_MODE_CONNECTABLE_DISCOVERABLE, i.e value 23, meaning this device is both discoverable and connectable from remote Bluetooth devices.
    On the Nexus One I see the device.getScanMode() always return 23, as it should be. But on the Galaxy S2 it sometimes just changes to scanmode SCAN_MODE_CONNECTABLE (21), making it not discoverable anymore! And indeed, other devices don't see it anymore when discovering. In the S2 phone's settings I also see in that case that the discoverable checkbox is not checked.
    Workaround: the only workaround that always works seems to be to always first turn on the Bluetooth, and after that send the above discoverableIntent. Thus first do a


    Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableIntent, REQUEST_ENABLE_BT);


    then the


    Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
    discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);
    startActivityForResult(discoverableIntent, REQUEST_MAKE_BT_DISCOVERABLE);



  • When you start the discoverable intent with any number different from 120, say 300, the popup that asks for user confirmation always shows 120, instead of the different number, say 300. The discoverability actually stays on for the specified amount (300 seconds), so functionally it works (phew), but the display is just incorrect and you can't change it. Confusing for the user.



Other bugs
I also found a few other more generic bugs reported by others and found by myself. Maybe they were SDK or device related (my app runs on Android 2.0+, tested on N1, Wildfire and Galaxy S2):

Sunday, April 17, 2011

Horizontally sliding grid of images in Android

For my Android app I wanted a swipeable and horizontally slideable 3x3 grid of images. If there were more than 9 images, a swipe should show the next 3x3 grid. Another requirement was that all should be in a ScrollView because there would be more on the screen above the 3x3 grid.
The first solutions that directly came to my mind were of course the GalleryView or a GridView or a horizontal ListView.
But the final solution was a ViewFlipper with TableLayouts as its children!
Below you can find my "journey" figuring out, experimenting and getting to the solution.

List View

Luckily for the horizontal ListView there was already a good example here. The result:


It really does work as a horizontal ListView, but it doesn't support the paging I was looking for; it is just a continuous list.
Sadly the code wasn't downloadable in one block (update: seems I just missed it at the top!), and it also contained typos (the layout tags were all lowercase), so it took me a while to get it all fixed and useable in an Eclipse project.
For people looking for the complete source code, you can find it here.
Note that the getView() has not been optimized yet, it creates a new view each time!
Also weird in the original code was the TextView within the ImageView, I changed the code to have them below each eachother.
An advantage of this implementation is that there is no "center locking" of the image anymore.

Gallery View

So the horizontal ListView didn't cut it, let's try a GalleryView (g1 in the code) with per item a standard GridView. That worked reasonably, except that the swiping didn't work anymore! Keypad scrolling still worked, kind of:


Luckily I found a fix for that, implementing a custom GridView that passes the fling on to the parent gallery (g2 in the code).



But then I still had to put it in a ScrollView... And then the trouble started, look at the vertical scrollbars that suddenly appear:


Bummer!

I ended up with quite some trial code which you can find here. Disclaimer: this is quite messy trial code so you are warned and I'm not supporting this code in any way; you are on your own here :)

Grid View

Making a GridView scroll horizontally in sets of 3x3 images just gave me headaches from the beginning. For example you can't set the number of rows, so it might sometimes show more rows than 3. And a GridView inside a ScrollView is also strongly warned against. Which btw also applies to ListView within a ScrollView. In general it even seems: a scrolling View inside another scrolling View is not a good idea and even strongly discouraged!
Furthermore, on a xlarge screen (WXGA) the gridview had almost its own will for laying out, making it look very different than on a HVGA screen.

The solution

After some more thinking I came across the solution: put Table Layouts of 3x3 images inside a ViewFlipper! Per table you can control how many images should be shown and it's quite easy to add swiping! Paging is then done per table. You can swipe left and right.
Page 1:


Page 2 after swiping to the left:


The source code of the final solution you can find here.
Note the top left icon in the grid changes when you swipe to the left the first time (so you can tell that the 2nd page is showing).
Note also the use of the FixedViewFlipper to handle rotation correctly.
A future improvement: keep track of the "page" you're on so with the example of 2 tables, don't allow the swipe/fling to "go round", i.e swiping to the left on the 2nd table shows the first table again...

PS: of course you could also create your custom component, but I want to avoid that as much as possible to minimize (custom) maintenance.

Misc

These links I also stumbled upon but didn't do anything with:

Hope this helps somebody some time :)

Saturday, February 12, 2011

Number of downloaded apps/installs of an android app

Online android market website gives more details on the number of downloads/installs of an app than the market on a device (phone)!
Check for example 3D Light Racer Basic. On my Nexus One it says it has > 250000 downloads. But on the website it says 500000 - 1000000 installs! See the red arrow.


Can be pretty useful for competitor analysis for example :)