The company where I'm working has started using 2-step authentication. So every time any employee wants to connect to f.e. VPN, he/she has to:
- sign-in using username and password
- if credentials are valid, system calls to the employee's cellphone and asks to press pound ("#") sign to complete authentication.
This second step is a bit annoying - you have to search your phone every time you want to login. Especially it annoys at home, where my phone usually hides from me in different places. Also if you forgot your phone somewhere (in the car, at home etc.) it is hard to connect to corporate network.
"It's time for Android development"- I have decided to create android app, that should:
- Pick up the phone
- Press pound key
- Hang up
- Bring the beer (joking :) )
Sounds not very scary (huh, if I knew!), simple enough for the first app. Let's rock!
Before start
Development Tools
I tried to use pre-released version of AndroidStudio, but for some reasons it didn't work on my OS X 10.8.5. So I fall back to "standard" ADT Bundle. Unfortunately its built-in Eclipse-based IDE has disappointed me - it is slow thing with "bugged" debugger, that often disconnects from emulator without any obvious reasons. I'm not saying it is ugly, but that is definitely worst dev tool for mobile platforms I've ever tried (I'm comparing with dev tools I used for development for iPhone, BlackBerry and Nokia). Looking forward to trying release version of AndroidStudio - I hope guys from JetBrains won't break good tradition and will deliver high-quality product soon.
Documentation and tutorials
Opposite to the toolkit, documentation is very good. Here some good resources for beginners:
- Android Development Tutorial - good overview of android platform, provides basic understanding of android's app and its components.
- UI Overview - must read - provides overview of the main UI components and recommendations how to use them to improve apps' usability.
- Devices and Displays, Metrics and Grids, Iconography - basics about different UI interface aspects.
- Best practices for Performance, Best UX - highly recommended to read.
- Firs app - if you're building your first app
- Android Performance Case Study - very good post about performance of android's apps - even if you do not try to profile your app, article provides basic understanding of the apps' performance and ways for optimization.
- SDK Samples, Google I/O App, photup - learn best practices by reading source code.
Huh, finally - development!!!
Preferences Screen
I didn't want that my app intercept every call, only from some certain number. I also would like to have ability to turn application off. So I started building my preferences view from scratch, but then I realized that there is mechanism building standard preferences screens. I won't pay a lot of attention of how to work with preferences, it is well described here. All that added to it is displaying preference value in the preferences main screen:
@Override protected void onPause() { super.onPause(); // Unregister the listener whenever a key changes getPreferenceScreen().getSharedPreferences() .unregisterOnSharedPreferenceChangeListener(this); } @Override protected void onResume() { super.onResume(); //update phone preference summary with actual value SharedPreferences preferences = getPreferenceScreen().getSharedPreferences(); phoneNumberPreference.setSummary(preferences.getString(PHONE_NUMBER_PREFERENCE, getResources().getString(R.string.phone_number_desc))); preferences.registerOnSharedPreferenceChangeListener(this); } @Override public void onSharedPreferenceChanged(SharedPreferences preferences, String key) { //update phone preference summary with actual value if(key.equals(PHONE_NUMBER_PREFERENCE)){ phoneNumberPreference.setSummary(preferences.getString(key, getResources().getString(R.string.phone_number_desc))); } }
Pick up the phone
Surprise - there is no easy way to do it in Android. Traditional approach - use hidden ITelephony API via reflection does not work in v. 2.3+ (details here and here). Finally (after spending many hours trying to find solution) I applied approach used in this great application - I just send bluetooth keypressed event, and it works even if there is no bluetooth headset installed! Magic!
// Simulate a press of the headset button to pick up the call Log.d("AuthenticatorAutoAnswerIntentService.pickUp", "Intent.ACTION_MEDIA_BUTTON Down"); Intent buttonDown = new Intent(Intent.ACTION_MEDIA_BUTTON); buttonDown.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK)); context.sendOrderedBroadcast(buttonDown, "android.permission.CALL_PRIVILEGED"); // trigger on buttonUp instead of buttonDown Log.d("AuthenticatorAutoAnswerIntentService.pickUp", "Intent.ACTION_MEDIA_BUTTON Up"); Intent buttonUp = new Intent(Intent.ACTION_MEDIA_BUTTON); buttonUp.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK)); context.sendOrderedBroadcast(buttonUp, "android.permission.CALL_PRIVILEGED");
Press pound key
I was not able to do it programmatically. There is no API to do it. There is open issue to provide it, but it is still open. Here what I tried:
- Send "#" pressed event - no luck (good overview of different ways how to do it is here)
- Use hidden android.telephony.Phone class (as suggested here)
- Call the "#" number using intent (recipe from here)
- During experiments I've found out, that when I put my android device close enough to the powerful sound speaker that plays"#" tone (300ms long), it was recognized by caller as "#" keypress. But when I tried to play "#" DTMF tone (using ToneGenerator) by android audio system (after my application picks up the phone), it didn't work. I think that my phone's sound is just not powerful enough. However when my app makes android audio to play long DTMF beep (5 seconds instead 300ms), caller hangs up - so it "hears" long beeps, but ignores short ones (again - in a case of external sound source, short DTMF beeps worked well - magic!).
Hang up
The most easy case - that was done by using ITelephony hack, which (in this particular case) for some unbelievable reasons does not need android.permission.MODIFY_PHONE_STATE permission.
Dessert - notification
In addition, application shows notification in the status bar when auto-answer is activated. I found that very useful.
Project Structure
Project lives here
- SettingsActivity - main settings screen
- AuthenticatorAutoAnswerNotifier - main guy who takes care about apps' notifications in the status bar
- AuthenticatorAutoAnswerBootReceiver - starts on device boot complete, all that it does - just calls AuthenticatorAutoAnswerNotifier to update notification
- AuthenticatorAutoAnswerReceiver - receiver that activates on incomming call, checks settings, and in case if app is activated and it is "authenticator" phone number, initiates async call to the auto-answer service.
- AuthenticatorAutoAnswerIntentService - service that does all the "auto-answer and authenticate" work. All process takes several seconds, so we cannot perform it in AuthenticatorAutoAnswerReceiver, which is running in main application thread.
No comments:
Post a Comment