MediaCompat - Hook Button - android

I have the following code to capture the Hook button press from a headset. This code works in Android 4.1, Android 5.0 and also on 7.0
I have two headphones,
First one is a simple Samsung handsfree/headphones which came with an old samsung phone. It has only one button.
Second one is a Sony headphone with handsfree mic, it also has only one button.
Both these headsets when plugged in to Android 4.1 or Android 5 - the button press is recognised in the OnPlay method (see code below).
However in Android 7.1.2 when I use the Samsung Headset the onPlay method is NOT called when the Hook button is pressed.
The Sony headset button press results in onPlay method being called.
I added the commented out code to see whether a MediaButton event is being received by the application. If I use the samsung headset and press the button it does result in the MediaButton event, I verified it using the onMediaButtonEvent.
Why is this mediabutton event not translating in to onPlay - only in case of Android 7.1.2 and that too only using that particular headset.
What should I be looking in the event.
private void initMediaSessions()
{
mSession = new MediaSessionCompat(getApplicationContext(), VoiceTicketService.class.getSimpleName());
mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS);
mSession.setMediaButtonReceiver(null);
mStateBuilder = new PlaybackStateCompat.Builder()
.setActions(PlaybackStateCompat.ACTION_PLAY);
mSession.setPlaybackState(mStateBuilder.build());
mSession.setCallback(new MediaSessionCompat.Callback()
{
//callback code is here.
#Override
public void onPlay()
{
Log.d("onPlay", "Hook key pressed UI is active");
toggleRecogniserState();
}
#Override
public void onStop()
{
Log.d("onStop", "Hook key pressed UI is active");
toggleRecogniserState();
}
#Override
public void onPause()
{
Log.d("onPause", "Hook key pressed UI is active");
toggleRecogniserState();
}
/* #Override
public boolean onMediaButtonEvent(Intent mediaButtonEvent)
{
KeyEvent event = (KeyEvent)mediaButtonEvent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
Log.d("onMediaButtonEvent ", "Hook key pressed UI is active "+event.getAction());
if(event.getAction()==0)
toggleRecogniserState();
return true;
}*/
}
);
mSession.setActive(true);
}

I have figured out the issue using getKeycode() on the event.
The KeyCode expected for the hook button press is 79. Both the headsets send this keycode 79 when it is tested on the Android 4.1,5.0.
However Android 7.1 is running on a Xiaomi phone which has its own Android Mod. I think this is the culprit which is recognising the button press from Samsung headphone as keycode 88 instead of 79. So its a phone specific issue and not an Android problem.

Related

Detect changes in power status of a TV connected to an Android device via HDMI

I'm building an Android media player application that I intend to use to play media (videos, pictures, etc.) on a TV while connected via an HDMI cable.
I want to have the media player app pause when the TV's power status is OFF and want it to play when the TV is turned ON.
How do I detect the TV's power status within my Android application when my Android device is connected to the TV via HDMI?
Both the TV and the Android device have support for HDMI-CEC. The device in question is an ODROID C2. I've seen this functionality on the KODI Android application which has a feature to pause the video when the HDMI-CEC status is OFF, I'm looking to implement this within my app as well.
Any help is appreciated. Thanks in advance!
EDIT: Progress below
I tried reading the status of the HDMI connection from within this file /sys/devices/virtual/switch/hdmi/state. However, this file holds int 1 no matter whether the power status of the connected screen / TV is ON or OFF.
2nd Progress update
I'm still working on this. Will not give up, and once I'm done I will surely post the answer here.
You can listen for changes in HDMI status (0 for unplugged and 1 for plugged) by registering for ACTION_HDMI_AUDIO_PLUG. It reports with status 0 when tv is switched off, switches to any other display medium or HDMI is removed. To read into its technicality, you can check out how hot plug detection works in HDMI. Overall, your app can at all times monitor whether the display can currently play your content or not. I have myself implemented this in a solution (on X96 mini android box & amazon fire-stick) where I needed to ensure that the content was actually being played because it included paid content. Also, I have attached the sample code file.
Note: This solution will only work when android device is HDMI source not sink!
Here's the documentation link too- https://developer.android.com/reference/android/media/AudioManager#ACTION_HDMI_AUDIO_PLUG
private BroadcastReceiver eventReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// pause video
String action = intent.getAction();
switch (action) {
case ACTION_HDMI_AUDIO_PLUG :
// EXTRA_AUDIO_PLUG_STATE: 0 - UNPLUG, 1 - PLUG
Toast.makeText(getApplicationContext(),"HDMI PLUGGED OR UNPLUGGED",Toast.LENGTH_LONG).show();
Log.d("MainActivity", "ACTION_HDMI_AUDIO_PLUG " + intent.getIntExtra(EXTRA_AUDIO_PLUG_STATE, -1));
((TextView)(findViewById(R.id.textView))).setText(((TextView)(findViewById(R.id.textView))).getText().toString().concat("At "+System.nanoTime()+": "+intent.getIntExtra(EXTRA_AUDIO_PLUG_STATE, -1) +"\n"));
break;
}
}
};
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(eventReceiver);
}
#Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_HDMI_AUDIO_PLUG);
registerReceiver(eventReceiver, filter);
}
In Some TV's, You need to monitor that (sys/class/amhdmitx/amhdmitx0/hpd_state) folder for changes by 500 ms Interval. because it'll change from 1 to 0 and again from 0 to 1 within 1 seconds.

Android "O" (Oreo, 8) and higher media buttons issue

The code for handling media buttons from headsets that I use in my Text-to-Speech app works great under Android API 22 through 25 (in older versions of Android they are handled by other, now depreciated means). However under Android 8 "Oreo", both public beta and final release, it does not work. Here is the relevant code:
When the service starts, I create MediaSessionCompact object:
mSession = new MediaSessionCompat(getApplicationContext(), "my.package.name._player_session");
mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mSession.setActive(true);
mSession.setCallback(myMediaSessionCallback);
PlaybackStateCompat state = new PlaybackStateCompat.Builder()
.setActions(ACTION_PLAY_PAUSE | ACTION_PLAY | ACTION_PAUSE |
ACTION_SKIP_TO_NEXT | ACTION_SKIP_TO_PREVIOUS |
ACTION_FAST_FORWARD | ACTION_REWIND
)
.setState(PlaybackStateCompat.STATE_PAUSED, 0 /*PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN*/, 1f)
.build();
mSession.setPlaybackState(state);
There is of course session media callback defined:
private MediaSessionCompat.Callback myMediaSessionCallback = new MediaSessionCompat.Callback() {
#Override
public boolean onMediaButtonEvent(Intent mediaButtonIntent) {
// The log output below never appears on "Oreo", nothing comes here.
Log.d(TAG, "callback onMediaButtonEvent() Compat");
MediaButtonIntentReceiver.handleIntent(mediaButtonIntent.getAction(), (KeyEvent) mediaButtonIntent.getParcelableExtra(Intent.EXTRA_KEY_EVENT));
return true;
}
#Override
public void onSkipToNext() {
//...
}
// etc. other overrides
};
I also experimented with PendingIntent, using MediaButtonReceiver.buildMediaButtonPendingIntent() and set mSession.setMediaButtonReceiver(pendingIntent) for all the actions I'm interested in, then in my service onStartCommand() I call MediaButtonReceiver.handleIntent(mSession, intent):
// still in the same service:
mSession.setMediaButtonReceiver(
MediaButtonReceiver.buildMediaButtonPendingIntent(
this,
mMediaButtonReceiverComponentName,
ACTION_PLAY));
mSession.setMediaButtonReceiver(
MediaButtonReceiver.buildMediaButtonPendingIntent(
this,
mMediaButtonReceiverComponentName,
ACTION_PAUSE));
mSession.setMediaButtonReceiver(
MediaButtonReceiver.buildMediaButtonPendingIntent(
this,
mMediaButtonReceiverComponentName,
ACTION_PLAY_PAUSE));
and in the service onStartCommand():
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// ...
if (intent != null) {
MediaButtonReceiver.handleIntent(mSession, intent);
// ...
}
return START_NOT_STICKY;
}
Nothing, it's completely dumb to media buttons press events. What's wrong with "O" or my code there??? I'm completely baffled.
Update 8/32/2017
I also created a trivial but working app project that demonstrates the problem, please see: https://github.com/gregko/PlayerServiceSample. This project displays LogCat output when a media button is pressed on a headset under Android 5.x to 7.x, but fails completely under Android 8 "Oreo".
Update 9/1/2017
There is now an open issue on Android Issue Tracker about this, which I submitted, at https://issuetracker.google.com/issues/65175978. Still the media buttons work in several music player apps I tested on Oreo, I just can't figure out what do they do differently to make them work... The context of my app is not playing music, but reading aloud text with Text to Speech service, so a lot of code from Music Player examples does not apply.
Solved. On "Android 8.0 Behavior Changes" Google page we find this text:
In Android 8.0 (API level 26) the handling of media button events is different:
The handling of media buttons in a UI activity has not changed: foreground activities still get priority in handling media button events.
If the foreground activity does not handle the media button event, the system routes the event to the app that most recently played audio locally. The active status, flags, and playback state of a media session are not considered when determining which app receives media button events.
If the app's media session has been released, the system sends the media button event to the app's MediaButtonReceiver if it has one.
For every other case, the system discards the media button event.
All I had to do to make my trivial sample work was to play some sound with MediaPlayer. Apparently playing sound with Text-to-Speech API does not qualify, which in my opinion is a bug.
Here is the code I added to my trivial sample to make it work, playing a very brief and silent WAV file from Raw resources directory:
final MediaPlayer mMediaPlayer;
mMediaPlayer = MediaPlayer.create(this, R.raw.silent_sound);
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
mMediaPlayer.release();
}
});
mMediaPlayer.start();
Update
Submitted the bug report to Android issue tracker at https://issuetracker.google.com/issues/65344811
Update 2, Oct. 10, 2017
Google now says that Oreo behavior in this respect is "by design" and won't fix it. Read the reply near the end of the issue tracker post above. I must say I'm disappointed.

Barcode Scan With Both Hard and Soft Trigger EMDK

We have a custom scanner to scan barcode using, which works with SOFT trigger (Using App Button) by using Motorola's emdk library.
barcodeManager = (BarcodeManager) this.emdkManager.getInstance(EMDKManager.FEATURE_TYPE.BARCODE);
scanner = barcodeManager.getDevice(BarcodeManager.DeviceIdentifier.DEFAULT);
scanner.addStatusListener(articleListener);
scanner.addDataListener(new Scanner.DataListener() {
#Override
public void onData(ScanDataCollection scanDataCollection) {
processData(scanDataCollection);
}
});
scanner.addDataListener(dataListener);
scanner.triggerType = Scanner.TriggerType.SOFT_ALWAYS;
scanner.enable();
How can i have both soft and Hard trigger to scan the data?
and with datalistener process the data received from both?
Zebra Technologies acquire Motorola Solution enterprise business in Oct. 2014, most of the updated documentation is now available under the Zebra Launchpad.
Scanner.TriggerType controls how you want to activate the barcode scanner on Zebra Android devices, usually you can set it up or Hard (scan is activated pressing the hardware trigger button) or Soft (scan is activated as soon as you call the Scanner.read() method).
To have an application that can use the Hardware trigger and having an on screen button to activate the scanner you can leave set the triggerType to Scanner.TriggerType.HARD and implement a login in the click event handler for the soft scan button so that you set the TriggerType to Scanner.TriggerType.SOFT_ONCE and then you call the Scanner.read() method. You can eventually check if there's another read active.
This is a sample implementation that you can test adding a button in the Barcode API sample included in the EMDK for Android (latest is v4.0):
private void softScan() {
if (scanner != null) {
try {
// Reset continuous flag
bContinuousMode = false;
if (scanner.isReadPending()) {
// Cancel the pending read.
scanner.cancelRead();
}
scanner.triggerType = TriggerType.SOFT_ONCE;
scanner.read();
new AsyncUiControlUpdate().execute(true);
} catch (ScannerException e) {
textViewStatus.setText("Status: " + e.getMessage());
}
}
}
So, usually you work with a TriggerType.HARD, but when you press the SCAN button you disable any pending read and you switch to TriggerType.SCAN_ONCE.
The implementation of the status listener needs to switch back the scanner to TriggerType.HARD and call the read() method.
You can find a complete sample at this github repository where I've added a Soft Scan button to the standard Zebra's EMDK Barcode API sample.
All the data are received by the same Data Listener.

Actionscript 3 Android Device 'Back' Key

This is the code I have for a Android device BACK button:
function handleKeyOut(event:KeyboardEvent):void
{
if (event.keyCode == Keyboard.BACK)
{
event.preventDefault();
gotoAndStop(2);
}
}
When I test the code on my Samsung Galaxy Mega 6.3 phone (Kit Kat), hitting the BACK key button causes the app to not show on the screen yet still run in the background. My app does have a frame 2...Flash debugger gives no errors so I'm thinking maybe it's Kit Kat (since I've had sound issues with Kit Kat already).
I would like for the BACK key to take the user to frame 2. If I code a 'Native Application Exit' for the BACK key it works fine...the BACK key just won't accept the gotoAndStop command.
I'm researching this now so thanks if anyone can put some insight into this.
THIS WORKS...
stage.addEventListener(KeyboardEvent.KEY_DOWN, buttonPressed);
function buttonPressed(event:KeyboardEvent):void
{
if (event.keyCode == Keyboard.BACK)
{
event.preventDefault();
stage.removeEventListener(KeyboardEvent.KEY_DOWN, buttonPressed);
gotoAndStop("home");
}
}

Smartwatch 2 app crashing watch on menu open

I have a smartwatch 2 app on the market which has been working fine for months, but recently it has started crashing a second after the context menu is opened.
The onKey code looks like this:
#Override
public void onKey(final int action, final int keyCode, final long timeStamp) {
// Menu button click
if (action == Control.Intents.KEY_ACTION_RELEASE
&& keyCode == Control.KeyCodes.KEYCODE_OPTIONS) {
showMenu(mMenuItemsText);
}
}
(mMenuItemsText is defined at the class level and instantiated in the constructor:
mMenuItemsText[0] = new Bundle();
mMenuItemsText[0].putInt(Control.Intents.EXTRA_MENU_ITEM_ID, MENU_ITEM_REVERSE_RATE);
mMenuItemsText[0].putString(Control.Intents.EXTRA_MENU_ITEM_TEXT, context.getResources().getString(R.string.converter_menu_reverse_rate));
)
When I click the watch menu button in my app, the menu opens up, and then a second later the watch crashes and disconnects from the phone before starting back up and reconnecting to the phone. Nothing in logcat and the phone doesn't show a crash prompt, it seems completely unaware that the watch has crashed.
If I put Log.d statements on each line above then they all show up in logcat, it seems to be happening after the menu has finished its "swipe in" animation.
Thinking the problem was in the utils app, I tried replacing the showMenu call with the same code to send the menu intent directly:
#Override
public void onKey(final int action, final int keyCode, final long timeStamp) {
// Menu button click
if (action == Control.Intents.KEY_ACTION_RELEASE
&& keyCode == Control.KeyCodes.KEYCODE_OPTIONS) {
Intent intent = new Intent(Control.Intents.CONTROL_MENU_SHOW);
intent.putExtra(Control.Intents.EXTRA_MENU_ITEMS, mMenuItemsText);
sendToHostApp(intent);
}
}
But I get the same problem. I have another SW2 app on the market with the same code and it works fine. I'm completely stumped as to how to find the problem, as I'm unable to step into the code in Eclipse.
This issue will be fixed in the upcoming 1.4.54 host app SW to be released in the next few days. The issue has to do with the number of touch regions supported, which has been increased from 25 to 30 in the update.
I have about the same problem. I have several menus in my app but only one of them will crash the Smartwatch 2. It happens every time now, also reported by many users. The problem came after the recent firmware update. So hopefully it will be fixed in a new firmware or updated SDK release.
My problem is now fixed in latest firmware from Sony

Categories

Resources