Android : How to set MediaPlayer volume programmatically? - java

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

Related

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

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"

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
}

Adjusting Volume in Android Audiomanager is inaccurate

in my Android Application i have a method, which allows me to adjust the volume. This works like it should.
public void adjustVolume(int adjustType){
if(myAudioManager == null) {
myAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
}
if (adjustType == 0){
myAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
Toast.makeText(getApplicationContext(), "mute audio", Toast.LENGTH_SHORT).show();
}
else if(adjustType == 1) {
myAudioManager.adjustVolume(AudioManager.ADJUST_LOWER, AudioManager.FLAG_SHOW_UI);
Toast.makeText(getApplicationContext(), "decrease volume", Toast.LENGTH_SHORT).show();
}
else if (adjustType == 2){
myAudioManager.adjustVolume(AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI);
Toast.makeText(getApplicationContext(), "increase volume", Toast.LENGTH_SHORT).show();
}
else if (adjustType == 3){
int maxVolume = myAudioManager.getStreamMaxVolume(AudioManager.STREAM_SYSTEM);
myAudioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, maxVolume, AudioManager.FLAG_SHOW_UI);
Toast.makeText(getApplicationContext(), "Max volume", Toast.LENGTH_SHORT).show();
}
Log.d(LOG_TAG, "Volume is " + myAudioManager.getStreamVolume(AudioManager.STREAM_SYSTEM));
}
e.g. I can call adjustVolume(1) to decrease the volume, and adjustVolume(2) to increase the volume. From Volume zero, to max volume it takes 15 steps
So far, so good.
Now I have another method, where I want to set the volume directly:
public void adjustVolumeDirect(int volumeValue) {
if(myAudioManager == null) {
myAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
}
//maxVolume is 7
int maxVolume = myAudioManager.getStreamMaxVolume(AudioManager.STREAM_SYSTEM);
float factor = (float)volumeValue / 100;
int targetVolume = (int) (maxVolume * factor);
Log.d(LOG_TAG,"MAX_VOLUME = "+maxVolume+" VOLUMEVALUE = " +volumeValue+ " TARGET VOLUME = " + targetVolume + "factor = " + factor);
myAudioManager.setStreamVolume(AudioManager.STREAM_SYSTEM,targetVolume , AudioManager.FLAG_SHOW_UI);
}
e.g.
adjustVolumeDirect(100) should set the Volume to max. adjustVolumeDirect(50) should set in to 50% and so on.
And this part does not work like I expect it. I can only set the volume in 7 steps! The method above gives me 15 steps!
Can anyone give me a hint how to solve this?
I want to set the volume in 10-steps:
adjustVolumeDirect(10) = 10%
adjustVolumeDirect(20) = 20%
...
adjustVolumeDirect(100) = 100%
How can I achieve this?
The reason you have a different number of steps is probably because two different audio streams are affected - for example the system audio stream, which may have 7 steps and the media audio stream, which might have 15.
The number of steps are different for various android devices and are defined in the system. See also this issue in the android bug tracker.
There are different ways to allow a finer tuning depending on your device. You can probably find some more information here on the android stackexchange site.
I've resolved the issue with this:
mAudioManager.getStreamMaxVolume(audio.STREAM_MUSIC)**/3**
mAudioManager.getStreamMaxVolume(audio.STREAM_MUSIC)**/2**
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, audio.getStreamMaxVolume(mAudioManager.STREAM_MUSIC)/3, AudioManager.FLAG_SHOW_UI);

Accessing ArrayList<Mat> across Activities, OpenCV, Android

I am trying to use an ArrayList() across two different Activities. It is declared:
public static ArrayList<Mat> Video = new ArrayList<Mat>();
I read in frames from the camera and when I have 50 I go to my next activity.
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
Mat frame = inputFrame.rgba();
if(Video.size() < 50)
{
Log.i(TAG, "Added frame");
Video.add(frame);
}
else
{
String e = Integer.toString(Video.get(1).cols());
Log.v(TAG1, e);
e = Integer.toString(Video.get(1).rows());
Log.v(TAG1, e);
Intent intent = new Intent(this, Analysis.class);
startActivity(intent);
}
return inputFrame.rgba();
}
The log output for this method is:
11-15 22:53:30.225: V/Values(32362): 800
11-15 22:53:30.225: V/Values(32362): 480
This is the correct height and width for the device this is running on(Galaxy S2).
Then in my next activities onCreate() I directly access "Video":
String e = Integer.toString(HomeScreen.Video.get(1).cols());
Log.v(TAG, e);
String h = Integer.toString(HomeScreen.Video.get(1).rows());
Log.v(TAG, h);
But this time the log reads:
11-15 22:53:30.840: V/Values2:(32362): 800
11-15 22:53:30.840: V/Values2:(32362): 0
So my question is, why is the row() value not 480 in both Logs? I need a List of frames because I am recording all the frames and then in another Activity I am going to operate on them and output the display(which I need to number of rows for).
I replaced the line:
Video.add(frame);
With
Video.add(frame.clone());
and it works perfectly!
I think in the top line i was only copying the header part of the frame and not the entire frame's contents.

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.

Categories