Sunday, May 19, 2013

Managing Audio Focus - Android

Was just googling around how to manage the media player in an android application when a call or sms arrives, and stumbled upon this very interesting and important concept of managing audio focus...........

I have taken this directly from the documentation available at developer.android.com, as the explanation is very lucid out here.........thus saving me from the trouble of retyping it...:)

With multiple apps potentially playing audio it's important to think about how they should interact. To avoid every music app playing at the same time, Android uses audio focus to moderate audio playback—only apps that hold the audio focus should play audio.
Before your app starts playing audio it should request—and receive—the audio focus. Likewise, it should know how to listen for a loss of audio focus and respond appropriately when that happens.

Request the Audio Focus


Before your app starts playing any audio, it should hold the audio focus for the stream it will be using. This is done with a call to requestAudioFocus() which returns AUDIOFOCUS_REQUEST_GRANTED if your request is successful.
You must specify which stream you're using and whether you expect to require transient or permanent audio focus. Request transient focus when you expect to play audio for only a short time (for example when playing navigation instructions). Request permanent audio focus when you plan to play audio for the foreseeable future (for example, when playing music).
The following snippet requests permanent audio focus on the music audio stream. You should request the audio focus immediately before you begin playback, such as when the user presses play or the background music for the next game level begins.
AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
...
// Request audio focus for playback
int result = am.requestAudioFocus(afChangeListener,
                                 // Use the music stream.
                                 AudioManager.STREAM_MUSIC,
                                 // Request permanent focus.
                                 AudioManager.AUDIOFOCUS_GAIN);
   if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
    // Start playback.
}
 

Once you've finished playback be sure to call abandonAudioFocus(). This notifies the system that you no longer require focus and unregisters the associated AudioManager.OnAudioFocusChangeListener. In the case of abandoning transient focus, this allows any interupted app to continue playback.

// Abandon audio focus when playback complete am.abandonAudioFocus(afChangeListener);

When requesting transient audio focus you have an additional option: whether or not you want to enable "ducking." Normally, when a well-behaved audio app loses audio focus it immediately silences its playback. By requesting a transient audio focus that allows ducking you tell other audio apps that it’s acceptable for them to keep playing, provided they lower their volume until the focus returns to them.
// Request audio focus for playback
int result = am.requestAudioFocus(afChangeListener,
                     // Use the music stream.
                      AudioManager.STREAM_MUSIC,
                     // Request permanent focus.
                     AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
   if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    // Start playback.
}
Ducking is particularly suitable for apps that use the audio stream intermittently, such as for audible driving directions.
Whenever another app requests audio focus as described above, its choice between permanent and transient (with or without support for ducking) audio focus is received by the listener you registered when requesting focus.

Handle the Loss of Audio Focus


If your app can request audio focus, it follows that it will in turn lose that focus when another app requests it. How your app responds to a loss of audio focus depends on the manner of that loss.
The onAudioFocusChange() callback method of they audio focus change listener you registered when requesting audio focus receives a parameter that describes the focus change event. Specifically, the possible focus loss events mirror the focus request types from the previous section—permanent loss, transient loss, and transient with ducking permitted.
Generally speaking, a transient (temporary) loss of audio focus should result in your app silencing it’s audio stream, but otherwise maintaining the same state. You should continue to monitor changes in audio focus and be prepared to resume playback where it was paused once you’ve regained the focus.
If the audio focus loss is permanent, it’s assumed that another application is now being used to listen to audio and your app should effectively end itself. In practical terms, that means stopping playback, removing media button listeners—allowing the new audio player to exclusively handle those events—and abandoning your audio focus. At that point, you would expect a user action (pressing play in your app) to be required before you resume playing audio.
In the following code snippet, we pause the playback or our media player object if the audio loss is transient and resume it when we have regained the focus. If the loss is permanent, it unregisters our media button event receiver and stops monitoring audio focus changes.

OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
    public void onAudioFocusChange(int focusChange) {
        if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT
            // Pause playback
        } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
            // Resume playback 
        } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
            am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
            am.abandonAudioFocus(afChangeListener);
            // Stop playback
        }
    }
};
In the case of a transient loss of audio focus where ducking is permitted, rather than pausing playback, you can "duck" instead.

Duck!


Ducking is the process of lowering your audio stream output volume to make transient audio from another app easier to hear without totally disrupting the audio from your own application.
In the following code snippet lowers the volume on our media player object when we temporarily lose focus, then returns it to its previous level when we regain focus.
OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
    public void onAudioFocusChange(int focusChange) {
        if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
            // Lower the volume
        } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
            // Raise it back to normal
        }
    }
};
A loss of audio focus is the most important broadcast to react to, but not the only one. The system broadcasts a number of intents to alert you to changes in user’s audio experience. The next lesson demonstrates how to monitor them to improve the user’s overall experience.


For more detailed explanation around Managing Audio Playback you can visit http://developer.android.com/training/managing-audio/audio-focus.html  

Android Studio

Getting Started with Android Studio ........ straight from developer.android.com

EARLY ACCESS PREVIEW

Android Studio is a new Android development environment based on IntelliJ IDEA. Similar to Eclipse with the ADT Plugin, Android Studio provides integrated Android developer tools for development and debugging. On top of the capabilities you expect from IntelliJ, Android Studio offers:
  • Gradle-based build support.
  • Android-specific refactoring and quick fixes.
  • Lint tools to catch performance, usability, version compatibility and other problems.
  • ProGuard and app-signing capabilities.
  • Template-based wizards to create common Android designs and components.
  • A rich layout editor that allows you to drag-and-drop UI components, preview layouts on multiple screen configurations, and much more.
Caution: Android Studio is currently available as anearly access preview. Several features are either incomplete or not yet implemented and you may encounter bugs. If you are not comfortable using an unfinished product, you may want to instead download (or continue to use) the ADT Bundle(Eclipse with the ADT Plugin).

For more information about Android Studio or to download the same visit http://developer.android.com/sdk/installing/studio.html 

Thursday, April 25, 2013

Install apk and uninstall an app via Intents

        Currently am working on a project that requires to build an Android application that will download another apps within itself and install the same. I call the main app as parent app and the apps that will be downloaded as child apps (just for simplicity...... :)). Also it is required that the child apps should not be visible to the user and should be used only via the parent app. Finally when the parent app is uninstalled; it is required that the child apps also be uninstalled.

        Initially I thought that the requirements mentioned above were not possible, but after a little bit of reading I came up with a solution.

              1) The first requirement of downloading and installing an app within another app can be achieved by simply downloading an apk (using http) and installing the apk when download finishes. Android does not authorize to install anything without users consent (unless device is rooted), only thing we can do is prompt the user to install the apk. The app will be installed successfully if user clicks 'Install'.

Intent intent = new Intent(Intent.ACTION_VIEW);
Uri apkUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory() + "/Download/" + "app.apk")); 
intent.setDataAndType(apkUri, "application/vnd.android.package-archive"); startActivity(intent);

              2) For the child apps not be visible to user, I simply had to remove intent-filter of the launcher activity from the manifest file. When no launcher activity is present in an application, it does not have a launcher icon.



But in absence of a launcher activity how to launch an application?
Hence, to launch the actiivty we need to provide it a custom action and use it to launch the activity from the parent app.


To launch the above activity the intent call will be as below.

Intent intent = new Intent(); intent.setAction("com.temporary.nolauncher.action.NO_LAUNCHER"); startActivity(intent);

                 3) Finally for uninstalling the child apps when parent app is uninstalled, I had to register a receiver that would catch the event when any app is uninstalled. After the event is caught, I checked which app was uninstalled using the URI. If the parent app is uninstalled then the child app would prompt the user to uninstall itself. Again uninstall can't be achieved without user's consent (unless device is rooted).

The receiver has to be registered in manifest as follows. The action PACKAGE_REMOVED serves the purpose of catching the event when any anpp is uninstalled.


After the event of uinstalling any app is caught in the receiver, we can detect which app was uninstalled (using the intent). The intent returns the package name via the uri. 'intent' reference is avaible from the onReceive method in the receiver.

private String getPackageName(Intent intent) { 
      Uri uri = intent.getData(); 
      String pkg = uri != null ? uri.getSchemeSpecificPart() : null; 
      return pkg; 
}

Finally once the it is detected that the app uninstalled was the parent app then we can fire an intent that will prompt the user to uninstall the child app.

String packToUninstall = "com.temporary.nolauncher"; 
Uri packageURI = Uri.parse("package:" + packToUninstall); 
Intent uninstallIntent = new Intent(Intent.ACTION_DELETE,packageURI); uninstallIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ctx.startActivity(uninstallIntent);

 'ctx' reference is avaible from the onReceive method in the receiver.