Volume up and down detective

I am trying to create extension to detect if user click on volume up or down and set it to event.
I found this code

@Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
            volumeButtonListener.onVolumeUp();
            return true;
        } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
            volumeButtonListener.onVolumeDown();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

But I think app inventor doesn't have onKeyDown inside it...
do you have any idea?

Two years ago I had made an extension to listen to key events, not sure where it is.
It is not directly possible at all, you need to do some special reflection operations. The code's somewhere lying around in my old hard drive... that i dont currently have access to it until three days.

Edit: However if you are primarily concerned about listening to volume changes, there are other Android APIs available

1 Like

I'm using the below code into my projects and it's working fine for me.

  1. Create the custom listener class
    class VolumeButtonListener extends ContentObserver {

        int previousVolume;

        public VolumeButtonListener(Handler handler) {
            super(handler);
            AudioManager audio = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
            previousVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC);
        }

        @Override
        public boolean deliverSelfNotifications() {
            return super.deliverSelfNotifications();
        }

        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);

            AudioManager audio = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
            int currentVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC);

            int delta = previousVolume - currentVolume;

            if (delta > 0) {
                previousVolume = currentVolume;
                // Volume down button is clicked
            } else if (delta < 0) {
                previousVolume = currentVolume;
                // Volume up button is clicked
            }
        }
    }
  1. Declare and Initialize the variable
// Declare the variable for global use
private VolumeButtonListener volumeListener;
// Initialize the variable
volumeListener = new VolumeButtonListener(new Handler(Looper.getMainLooper()));
  1. Register the listener class as ContentObserver
// Register the VolumeButtonListener class as ContentObserver
context.getContentResolver().registerContentObserver(android.provider.Settings.System.CONTENT_URI, true, volumeListener);
  1. Remove the VolumeButtonListener class from ContentObserver on the Activity destroy
container.$form().registerForOnDestroy(new OnDestroyListener() {
            @Override
            public void onDestroy() {
                context.getContentResolver().unregisterContentObserver(volumeListener);
            }
        });

Hope it helps :+1:

2 Likes

Hope you can share code ASAP.

like what?

i tested it, it is not work for me.
AIX:
me.aemo.volumedetector.aix (7.2 KB)
AIA:
VolumeTrackerEx.aia (9.5 KB)

VolumeButtonListener use this class as inner class.

My Code:

VolumeTracker.java

package me.aemo.volumedetector.lib;

import android.content.Context;
import android.database.ContentObserver;
import android.media.AudioManager;
import android.os.Handler;

public class VolumeTracker extends ContentObserver {

    private final Context context;
    private final VolumeListener listener;
    private int previousVolume;

    public VolumeTracker(Handler handler, Context context, VolumeListener listener) {
        super(handler);
        this.context = context;
        this.listener = listener;
        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        previousVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
    }

    @Override
    public boolean deliverSelfNotifications() {
        return super.deliverSelfNotifications();
    }

    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);

        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);

        int delta = previousVolume - currentVolume;
        if (delta > 0) {
            previousVolume = currentVolume;
            if (listener != null) listener.onVolumeUp();
        } else if (delta < 0) {
            previousVolume = currentVolume;
            if (listener != null) listener.onVolumeDown();
        }
    }
}

VolumeListener.java

package me.aemo.volumedetector.lib;

public interface VolumeListener {
    void onVolumeUp();
    void onVolumeDown();
}

Extension Code

VolumeDetector.java

public class VolumeDetector extends AndroidNonvisibleComponent
        implements VolumeListener, OnDestroyListener {
    private static final String TAG = "VolumeDetector";

    private VolumeTracker volumeTracker;
    private final Context context;
    public VolumeDetector(ComponentContainer container){
        super(container.$form());
        this.context = container.$context();
    }
    @SimpleFunction
    public void Initialize(){
        if (volumeTracker == null){
            this.volumeTracker = new VolumeTracker(new Handler(Looper.getMainLooper()), context, this);
        }
    }

    @SimpleFunction
    public void StartListener(){
        if (volumeTracker != null){
            context.getContentResolver().registerContentObserver(Settings.System.CONTENT_URI, true, volumeTracker);
        }
    }
    @SimpleFunction
    public void StopListener(){
        if (volumeTracker != null){
            context.getContentResolver().unregisterContentObserver(volumeTracker);
        }
    }


    @Override
    public void onVolumeUp() {
        form.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                OnVolumeUp();
            }
        });
    }

    @SimpleEvent
    public void OnVolumeUp() {
        EventDispatcher.dispatchEvent(this,"OnVolumeUp");
    }

    @Override
    public void onVolumeDown() {
        form.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                OnVolumeDown();
            }
        });
    }

    @SimpleEvent
    public void OnVolumeDown() {
        EventDispatcher.dispatchEvent(this,"OnVolumeDown");
    }

    @Override
    public void onDestroy() {
        context.getContentResolver().unregisterContentObserver(volumeTracker);
    }


  }

my code is okay??

No, you're not following my instructions.

What is my mistake ): ?

Please try this code:

package me.aemo.volumedetector;

import com.google.appinventor.components.annotations.DesignerComponent;
import com.google.appinventor.components.annotations.SimpleEvent;
import com.google.appinventor.components.annotations.SimpleFunction;
import com.google.appinventor.components.runtime.ComponentContainer;
import com.google.appinventor.components.runtime.EventDispatcher;
import com.google.appinventor.components.runtime.OnDestroyListener;
import com.google.appinventor.components.runtime.AndroidNonvisibleComponent;

import android.content.Context;
import android.database.ContentObserver;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings.System;

@DesignerComponent(version = 2, versionName = "1.0", description = "Developed by Mahmoud Hooda by Fast.", iconName = "icon.png")
public class VolumeDetector extends AndroidNonvisibleComponent {
  private final Context context;
  private VolumeButtonListener volumeListener;

  public VolumeDetector(ComponentContainer container) {
    super(container.$form());
    context = container.$context().getApplicationContext();
    volumeListener = new VolumeButtonListener(new Handler(Looper.getMainLooper()));
    container.$form().registerForOnDestroy(new OnDestroyListener() {
      @Override
      public void onDestroy() {
        StopListening();
      }
    });
  }

  @SimpleFunction(description = "Start listening the volumn button press.")
  public void StartListening() {
    context.getContentResolver().registerContentObserver(System.CONTENT_URI, true, volumeListener);
  }

  @SimpleFunction(description = "Stop listening the volumn button press.")
  public void StopListening() {
    if (volumeListener != null) {
      context.getContentResolver().unregisterContentObserver(volumeListener);
    }
  }

  @SimpleEvent(description = "The volume up button is pressed.")
  public void VolumeUpPressed() {
    EventDispatcher.dispatchEvent(this, "VolumeUpPressed");
  }

  @SimpleEvent(description = "The volume down button is pressed.")
  public void VolumeDownPressed() {
    EventDispatcher.dispatchEvent(this, "VolumeDownPressed");
  }

  // Inner class to listen the volume button press
  class VolumeButtonListener extends ContentObserver {
    int previousVolume;

    public VolumeButtonListener(Handler handler) {
      super(handler);
      AudioManager audio = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
      previousVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC);
    }

    @Override
    public boolean deliverSelfNotifications() {
      return super.deliverSelfNotifications();
    }

    @Override
    public void onChange(boolean selfChange) {
      super.onChange(selfChange);
      AudioManager audio = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
      int currentVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC);
      int delta = previousVolume - currentVolume;
      if (delta > 0) {
        previousVolume = currentVolume;
        VolumeDownPressed();
      } else if (delta < 0) {
        previousVolume = currentVolume;
        VolumeUpPressed();
      }
    }
  }
}

me.aemo.volumedetector.aix (6.5 KB)

1 Like

still not work for me

I've just tested it on Kodular and it's working there on Companion and APK. You might need to export the apk from AI2 for testing. I'm not sure. You might give a try.

1 Like

can you share aia

VolumeListener.aia (3.6 MB) (Rename it as .aia to .apk)

And this apk is also working. It's exported from AI2.

1 Like

Here is the aia files.

VolumeListener.aia (8.9 KB) (AI2)

AdmobBlocks.aia (26.6 KB) (Kodular)

hhhhhhhhhh :sweat_smile:
i know now why it is not work,
It only works when you raise or lower the Media volume.
and my device automatically raise or lower volume of Ringtone

Edit: i mean those

Can you edit the code to work on all volume types @JEWEL

You mean to listen the specific type of volume like: Ringtone, Media, Notification, System?

1 Like

i don't need to create listener for specific type just on volume up or down.
i don't care if it is Ringtone, Media, Notification, System