How can I specify internal speakers to play an audio file? - java

In my Android app, I want to play sounds on the phone internal speakers even when the user is connected to the device through a Bluetooth device or other external speakers. Can this be done?

I came up with way of playing audio on the phone speakers even when I have a connected Bluetooth audio device:
fun createPhoneSpeakerPreferredPlayer(context: Context, resource: Int): MediaPlayer{
val mediaPlayer = MediaPlayer.create(context, resource)
val audioDeviceType = if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) AudioDeviceInfo.TYPE_BUILTIN_SPEAKER_SAFE else AudioDeviceInfo.TYPE_BUILTIN_SPEAKER
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
//create mapped devices variable to allow better logging. The devices collection is not being serialized well (or at all)
val mappedDevices = (devices.map {
#Suppress("unused")
object {
val id = it.id
val type = it.type
val productName = it.productName
}})
val jsonRepresentation = Gson().toJson(mappedDevices)
Log.d(Tag, "devices: $jsonRepresentation")
val playbackDevice = devices.firstOrNull { it.type == audioDeviceType }
Log.d(Tag, "selectedDevice: {id=${playbackDevice?.id}}, type=\"${playbackDevice?.type}\", productName=\"${playbackDevice?.productName}\"")
mediaPlayer.preferredDevice = playbackDevice
return mediaPlayer
}
to use that method and play audio:
val mediaPlayer = createPhoneSpeakerPreferredPlayer(this.context, R.raw.myAudioId)
mediaPlayer.looping = true
mediaPlayer.start()
This seems to work if I have a Bluetooth speaker connected, but once I start playing audio in the background, like watching Netflix, or listening to a podcast, the audio no longer plays on the phone speaker.
Here is a sample app I made with two buttons. One to play music through the phone speaker and one to play through whatever speaker is connected:
https://github.com/danwize/play-sound-android/blob/main/SoundPlayer/app/src/main/java/com/example/soundplayer/MainActivity.kt
I'm seeing that the first time I create a media player and play sound this way, the sound does play on the phone speakers. The second audio plays on the Bluetooth speaker. When I'm not playing any video or music, but still connected to a Bluetooth device, both my sounds play on the phone speakers. Here are the logs to show that I am correctly setting the preferred device:
2022-09-07 11:21:33.969 6101-6101/com.myApp D/MediaPlayerExtensions: devices: [{"id":2,"productName":"Pixel 3","type":1},{"id":3,"productName":"Pixel 3","type":2},{"id":11,"productName":"Pixel 3","type":18},{"id":369,"productName":"Jabra Elite 85h","type":8},{"id":363,"productName":"Jabra Elite 85h","type":7}]
2022-09-07 11:21:33.969 6101-6101/com.myApp D/MediaPlayerExtensions: selectedDevice: {id=3}, type="2", productName="Pixel 3"
2022-09-07 11:22:14.290 6101-6101/com.myApp D/MediaPlayerExtensions: devices: [{"id":2,"productName":"Pixel 3","type":1},{"id":3,"productName":"Pixel 3","type":2},{"id":11,"productName":"Pixel 3","type":18},{"id":369,"productName":"Jabra Elite 85h","type":8},{"id":363,"productName":"Jabra Elite 85h","type":7}]
2022-09-07 11:22:14.290 6101-6101/com.myApp D/MediaPlayerExtensions: selectedDevice: {id=3}, type="2", productName="Pixel 3"

Related

Changing the image on the smartwatch notification

I'm developing a simple app for Android. I would like to change the image of my notifications on Samsung Galaxy Watch smartwatch. I tried to do it like this, unfortunately it doesn't work. In addition, the setBackround method is deprecated. Please help.
NotificationCompat.WearableExtender extender = new NotificationCompat.WearableExtender();
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.logo);
extender.setBackground(bitmap);
notificationn = new NotificationCompat.Builder(getApplicationContext(),WateringChannel.ID_OF_CHANNEL_2)
.setSmallIcon(R.drawable.icon_drop_of_water)
.setContentTitle("Tura " + sharedPreferences.getInt(SharedPreferencesNames.WateringData.ACTUAL_ROUND,1))
.setContentText("PozostaƂy czas: " + ToolClass.generateCountDownTimerTime(millisUntilFinished/1000))
.setPriority(NotificationCompat.PRIORITY_LOW)
.setOngoing(true)
.setOnlyAlertOnce(true)
.extend(extender);
notificationManager = NotificationManagerCompat.from(getApplicationContext());
notificationManager.notify(2, notificationn.build());

How correctly detect device is tablet or phone

I can detect device type from attrs resources:
<bool name="isTablet">false</bool>
// xlarge and sw600dp = tablet
// normal = phone
if(getResources().getBoolean(R.bool.isTablet)) {
// is tablet
}
But some smaller tablets are detected as phones.
I think you can use TelephonyManager class.
TelephonyManager manager = (TelephonyManager)getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE);
if (Objects.requireNonNull(manager).getPhoneType() == TelephonyManager.PHONE_TYPE_NONE){
//Tablet
}
else{
//Mobile Device
}

Can an Android Expert Explain Strange USB Host Behavior

I am writing an Android application to read input from a HID USB foot pedal (press the pedal, get a message, do something).
The UsbManager is not recognizing the device. The foot pedal may be throwing an error in Android kernel when it plugs in, because I see this error message in the logcat:
"EventHub could not get driver version for /dev/input/mouse0, not a typewriter"
However, I know the foot pedal works, because when I plug it in and press it, it changes the focus to the next button on the activity... So I know it is communicating with my Nexus tablet and apparently its default action is to move the focus to the next button/object. I don't think there are any problems with my code, since it will recognize other USB devices, just not this foot pedal. I can actually tell when it's pressed by checking for when the focus changes, but that won't work for what I want since this app will run in the background as a service. I've tried setting an intent filter for this specific USB device (I know its product id and vendor id). However, it still shows no connected devices and the pop-up message that is supposed to ask the user to confirm launching the application never shows up. I've tried just listing all the connected USB devices as well, but I always get an empty list.
Is there any way to intercept input from this device so I can tell when the foot pedal gets pressed, even though Android's USB Manager will not recognize it?
For completeness, here is my code. I am testing on a Galaxy Nexus 10 tablet:
public int list_usb_devices()
{
int device_count = 0;
UsbManager mUsbManager;
mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
String LOG_TAG = "USB";
for (UsbDevice device : mUsbManager.getDeviceList().values()) {
//This code is never reached...
Log.d(LOG_TAG, "Detected device: " + device.toString());
Log.d(LOG_TAG, "Model: " + device.getDeviceName());
Log.d(LOG_TAG, "Id: " + device.getDeviceId());
Log.d(LOG_TAG, "Class: " + device.getDeviceClass());
Log.d(LOG_TAG, "Protocol: " + device.getDeviceProtocol());
Log.d(LOG_TAG, "VendorId: " + device.getVendorId());
Log.d(LOG_TAG, "ProductId: " + device.getProductId());
CharSequence text = device.toString();
show_toast(text);
device_count++;
}
return device_count;
}
I did some research in the Android source and it seems that all HID boot devices (mouse, keyboard etc.) are blacklisted and can therefore not be accessed using the USBManager API.
Here is the relevant part from the UsbHostManager.java , see here: http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/4.4.2_r1/com/android/server/usb/UsbHostManager.java/?v=source
/* returns true if the USB device should not be accessible by applications */
private boolean isBlackListed(int clazz, int subClass, int protocol) {
// blacklist hubs
if (clazz == UsbConstants.USB_CLASS_HUB) return true;
// blacklist HID boot devices (mouse and keyboard)
if (clazz == UsbConstants.USB_CLASS_HID &&
subClass == UsbConstants.USB_INTERFACE_SUBCLASS_BOOT) {
return true;
}
return false;
}

Change the Android bluetooth device name

I know it's possible to get the local device name as described in the solution to this question Display Android Bluetooth Device Name
What I'm interested in knowing is, can I change the local buetooth name (the one other devices see when I'm in discovery mode) programaticlly. I know you can change it by hand, but I'm writing and app and I want to be able to change the name (add a simple flag) so other devices with the same application can scan and instantly know if the phone is also running the app.
tl;dr: How can I change the bluetooth device name on android?
Yes you can change your device name using setName(String name) of BluetoothAdapter type.Following is the sample code:
private BluetoothAdapter bluetoothAdapter = null;
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
void ChangeDeviceName(){
Log.i(LOG, "localdevicename : "+bluetoothAdapter.getName()+" localdeviceAddress : "+bluetoothAdapter.getAddress());
bluetoothAdapter.setName("NewDeviceName");
Log.i(LOG, "localdevicename : "+bluetoothAdapter.getName()+" localdeviceAddress : "+bluetoothAdapter.getAddress());
}
Thanks for the original answer, here are a few things I found when implementing that might help someone else out.
1) BT has to be enabled for setName() to work.
2) It takes time for BT to Enable. ie. you Can't just call enable() then setName()
3) It takes time for the name to "sink in". ie. you can't call getName() right after setName() and expect the new name.
So, here is a snippet of code I came up with to use a runnable to get the job done in the background. It is also time bound to 10seconds, so it won't run forever if there is a problem.
Finally, this is part of our power on check, and we normally leave BT disabled (due to battery). So, I turn BT back off after, you may not want to do that.
// BT Rename
//
final String sNewName = "Syntactics";
final BluetoothAdapter myBTAdapter = BluetoothAdapter.getDefaultAdapter();
final long lTimeToGiveUp_ms = System.currentTimeMillis() + 10000;
if (myBTAdapter != null)
{
String sOldName = myBTAdapter.getName();
if (sOldName.equalsIgnoreCase(sNewName) == false)
{
final Handler myTimerHandler = new Handler();
myBTAdapter.enable();
myTimerHandler.postDelayed(
new Runnable()
{
#Override
public void run()
{
if (myBTAdapter.isEnabled())
{
myBTAdapter.setName(sNewName);
if (sNewName.equalsIgnoreCase(myBTAdapter.getName()))
{
Log.i(TAG_MODULE, "Updated BT Name to " + myBTAdapter.getName());
myBTAdapter.disable();
}
}
if ((sNewName.equalsIgnoreCase(myBTAdapter.getName()) == false) && (System.currentTimeMillis() < lTimeToGiveUp_ms))
{
myTimerHandler.postDelayed(this, 500);
if (myBTAdapter.isEnabled())
Log.i(TAG_MODULE, "Update BT Name: waiting on BT Enable");
else
Log.i(TAG_MODULE, "Update BT Name: waiting for Name (" + sNewName + ") to set in");
}
}
} , 500);
}
}
To change the bluetooth name properly you need to take care of following things:
1) You need following permissions:
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
2) Check the bluetooth state from adapter as you can only change the name of bluetooth is turned on.
val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(bluetoothAdapter.state == BluetoothAdapter.STATE_ON){
bluetoothAdapter.setName("NewDeviceName");
}
3) If the bluetooth is not turned on then you can turn it on with the following command:
bluetoothAdapter.enable()
4) Last thing, please don't use static timers to wait for bluetooth state changes instead the proper way is that you can register for android.bluetooth.adapter.action.STATE_CHANGED broadcast and useBluetoothAdapter.EXTRA_STATE to get the new state of bluetooth whenever it is changed.
Note: Not all devices behave the same when it comes to bluetooth and changing the name due to caching and hw address, so never expect same outcome from all devices.

Android : How to set MediaPlayer volume programmatically?

How to set the mediaplayer volume programmatically. I use it for alarm notification. Any help is highly appreciated and thanks in advance.
Using AudioManager, you can simply control the volume of media players.
AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 20, 0);
also from MediaPlayer (But I didn't try that)
setVolume(float leftVolume, float rightVolume)
Since: API Level 1
Sets the volume on this player. This API is recommended for balancing
the output of audio streams within an application. Unless you are
writing an application to control user settings, this API should be
used in preference to setStreamVolume(int, int, int) which sets the
volume of ALL streams of a particular type. Note that the passed
volume values are raw scalars. UI controls should be scaled
logarithmically.
Parameters
leftVolume left volume scalar
rightVolume right volume scalar
Hope this help
audio = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
For Volume UP
audio.adjustStreamVolume(AudioManager.STREAM_MUSIC,
AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI);
for Volume Down
audio.adjustStreamVolume(AudioManager.STREAM_MUSIC,
AudioManager.ADJUST_LOWER, AudioManager.FLAG_SHOW_UI);
You do have the setVolume method in the MediaPlayer class. See here
To Hide Volume Controll UI:
audio = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
For Volume UP
audio.adjustStreamVolume(AudioManager.STREAM_MUSIC,
AudioManager.ADJUST_RAISE, 0);
for Volume Down
audio.adjustStreamVolume(AudioManager.STREAM_MUSIC,
AudioManager.ADJUST_LOWER, 0);
Read this page right here. It explains very well.
Basically, unless your app is a replacement alarm clock, you need to make the following call in the "onCreate()" function:
setVolumeControlStream(AudioManager.STREAM_MUSIC);
In this way you can create the volume of your app using the hardware buttons.
You can do the below using Kotlin, this code will check if the media volume is more than 20% of the maximum volume of the device, and will reduce it to be 20% only.
val audio = this.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val level = audio.getStreamVolume(AudioManager.STREAM_MUSIC)
val maxVolume = audio.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
val percent = 0.2f
val twintyVolume = (maxVolume * percent).toInt()
if ( level > twintyVolume) {
Toast.makeText(this,"audio level is $level", Toast.LENGTH_LONG).show()
audio.setStreamVolume(AudioManager.STREAM_MUSIC,twintyVolume,0)
}
Remember to set left and right speaker volumes.
if (System.nanoTime() == alarm){
yourMediaPlayer.setVolume(volume, volume)}
}
Try This
protected static void setVolume(int volume) {
currentVolume = volume;
{
if (volume == 1) {
volume = 2;
}
try {
float vol = ((float) volume / CONSTANT.SYSTEM_MAX_VOLUME);
mediaPlayer.setVolume(vol, vol);
} catch (Exception e) {
e.printStackTrace();
}
}
}
The below code sets the volume to the maximum level (getStreamMaxVolume()).
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
am.setStreamVolume(AudioManager.STREAM_MUSIC,am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),0);
Code:
AudioManager mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
try
{
float count = 100 * 0.2f;
Log.d("--count_float", count + "");
Log.d("--count_final", Math.round(count) + "");
Log.d("--count_volume", new
PreferenceMotionSensor(mContext).getStreamVolume());
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, Math.round(count), 0);
}
catch (Exception e)
{
Log.d("--Error", e.getMessage());
}
Output
D/--count_float: 20.0
D/--count_final: 20
D/--count_volume: 100

Categories