Есть ли трансляция для изменения объема?

Я программирую небольшой виджет, который нужно обновлять, когда пользователь меняет громкость звонка или вибрирует настройки.

Захват android.media.VIBRATE_SETTING_CHANGED отлично подходит для настроек вибрации, но я не нашел способа получить уведомление, когда громкость звонка изменится, и хотя я мог бы попытаться захватить, когда пользователь нажимает физические клавиши увеличения/уменьшения громкости, есть много других вариантов изменения громкости без использования этих клавиш.

Знаете ли вы, существует ли какое-либо действие трансляции для этого или каким-либо образом создать его или решить проблему без него?

Ответ 1

Нет трансляции, но я обнаружил, что вы можете подключить наблюдателя контента, чтобы получать уведомление при изменении настроек, причем объем потоков является некоторыми из этих параметров. Зарегистрируйтесь для android.provider.Settings.System.CONTENT_URI, чтобы получать уведомления обо всех изменениях настроек:

mSettingsContentObserver = new SettingsContentObserver( new Handler() ); 
this.getApplicationContext().getContentResolver().registerContentObserver( 
    android.provider.Settings.System.CONTENT_URI, true, 
    mSettingsContentObserver );

Наблюдатель содержимого может выглядеть примерно так:

public class SettingsContentObserver extends ContentObserver {

   public SettingsContentObserver(Handler handler) {
      super(handler);
   } 

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

   @Override
   public void onChange(boolean selfChange) {
      super.onChange(selfChange);
      Log.v(LOG_TAG, "Settings change detected");
      updateStuff();
   }
}

И обязательно отмените регистрацию содержимого в какой-то момент.

Ответ 2

код Натана работает, но дает два уведомления для каждой настройки системы изменений. Чтобы этого избежать, используйте следующие

public class SettingsContentObserver extends ContentObserver {
    int previousVolume;
    Context context;

    public SettingsContentObserver(Context c, Handler handler) {
        super(handler);
        context=c;

        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)
        {
            Logger.d("Decreased");
            previousVolume=currentVolume;
        }
        else if(delta<0)
        {
            Logger.d("Increased");
            previousVolume=currentVolume;
        }
    }
}

Затем в вашем сервисе onCreate зарегистрируйте его с помощью:

mSettingsContentObserver = new SettingsContentObserver(this,new Handler());
getApplicationContext().getContentResolver().registerContentObserver(android.provider.Settings.System.CONTENT_URI, true, mSettingsContentObserver );

Затем отмените регистрацию в onDestroy:

getApplicationContext().getContentResolver().unregisterContentObserver(mSettingsContentObserver);

Ответ 3

Да, вы можете зарегистрировать ресивер для изменения тома (это своего рода хак, но работает), мне удалось сделать это таким образом (не включает ContentObserver): В манифестном файле xml:

<receiver android:name="com.example.myproject.receivers.MyReceiver" >
     <intent-filter>
          <action android:name="android.media.VOLUME_CHANGED_ACTION" />
     </intent-filter>
</receiver>

BroadcastReceiver:

public class MyReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals("android.media.VOLUME_CHANGED_ACTION")) {
            Log.d("Music Stream", "has changed");       
        }
    }
}

надеюсь, что это поможет!

Ответ 4

На основе Nathan's, adi и swooby code Я создал полный рабочий пример с некоторыми незначительными улучшениями.

Глядя на класс AudioFragment, мы можем видеть, насколько легко слушать изменения объема с помощью нашего пользовательского ContentObserver.

public class AudioFragment extends Fragment implements OnAudioVolumeChangedListener {

    private AudioVolumeObserver mAudioVolumeObserver;

    @Override
    public void onResume() {
        super.onResume();
        // initialize audio observer
        if (mAudioVolumeObserver == null) {
            mAudioVolumeObserver = new AudioVolumeObserver(getActivity());
        }
        /*
         * register audio observer to identify the volume changes
         * of audio streams for music playback.
         *
         * It is also possible to listen for changes in other audio stream types:
         * STREAM_RING: phone ring, STREAM_ALARM: alarms, STREAM_SYSTEM: system sounds, etc.
         */
        mAudioVolumeObserver.register(AudioManager.STREAM_MUSIC, this);
    }

    @Override
    public void onPause() {
        super.onPause();
        // release audio observer
        if (mAudioVolumeObserver != null) {
            mAudioVolumeObserver.unregister();
        }
    }

    @Override
    public void onAudioVolumeChanged(int currentVolume, int maxVolume) {
        Log.d("Audio", "Volume: " + currentVolume + "/" + maxVolume);
        Log.d("Audio", "Volume: " + (int) ((float) currentVolume / maxVolume) * 100 + "%");
    }
}


public class AudioVolumeContentObserver extends ContentObserver {

    private final OnAudioVolumeChangedListener mListener;
    private final AudioManager mAudioManager;
    private final int mAudioStreamType;
    private int mLastVolume;

    public AudioVolumeContentObserver(
            @NonNull Handler handler,
            @NonNull AudioManager audioManager,
            int audioStreamType,
            @NonNull OnAudioVolumeChangedListener listener) {

        super(handler);
        mAudioManager = audioManager;
        mAudioStreamType = audioStreamType;
        mListener = listener;
        mLastVolume = audioManager.getStreamVolume(mAudioStreamType);
    }

    /**
     * Depending on the handler this method may be executed on the UI thread
     */
    @Override
    public void onChange(boolean selfChange, Uri uri) {
        if (mAudioManager != null && mListener != null) {
            int maxVolume = mAudioManager.getStreamMaxVolume(mAudioStreamType);
            int currentVolume = mAudioManager.getStreamVolume(mAudioStreamType);
            if (currentVolume != mLastVolume) {
                mLastVolume = currentVolume;
                mListener.onAudioVolumeChanged(currentVolume, maxVolume);
            }
        }
    }

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


public class AudioVolumeObserver {

    private final Context mContext;
    private final AudioManager mAudioManager;
    private AudioVolumeContentObserver mAudioVolumeContentObserver;

    public AudioVolumeObserver(@NonNull Context context) {
        mContext = context;
        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    }

    public void register(int audioStreamType, 
                         @NonNull OnAudioVolumeChangedListener listener) {

        Handler handler = new Handler();
        // with this handler AudioVolumeContentObserver#onChange() 
        //   will be executed in the main thread
        // To execute in another thread you can use a Looper
        // +info: https://stackoverflow.com/a/35261443/904907

        mAudioVolumeContentObserver = new AudioVolumeContentObserver(
                handler,
                mAudioManager,
                audioStreamType,
                listener);

        mContext.getContentResolver().registerContentObserver(
                android.provider.Settings.System.CONTENT_URI,
                true,
                mAudioVolumeContentObserver);
    }

    public void unregister() {
        if (mAudioVolumeContentObserver != null) {
            mContext.getContentResolver().unregisterContentObserver(mAudioVolumeContentObserver);
            mAudioVolumeContentObserver = null;
        }
    }
}


public interface OnAudioVolumeChangedListener {

    void onAudioVolumeChanged(int currentVolume, int maxVolume);
}

Надеюсь, что это еще полезно для кого-то!:)

Ответ 5

Натан и код adi работают, но могут быть очищены и автономны:

public class AudioStreamVolumeObserver
{
    public interface OnAudioStreamVolumeChangedListener
    {
        void onAudioStreamVolumeChanged(int audioStreamType, int volume);
    }

    private static class AudioStreamVolumeContentObserver
            extends ContentObserver
    {
        private final AudioManager                       mAudioManager;
        private final int                                mAudioStreamType;
        private final OnAudioStreamVolumeChangedListener mListener;

        private int mLastVolume;

        public AudioStreamVolumeContentObserver(
                @NonNull
                Handler handler,
                @NonNull
                AudioManager audioManager, int audioStreamType,
                @NonNull
                OnAudioStreamVolumeChangedListener listener)
        {
            super(handler);

            mAudioManager = audioManager;
            mAudioStreamType = audioStreamType;
            mListener = listener;

            mLastVolume = mAudioManager.getStreamVolume(mAudioStreamType);
        }

        @Override
        public void onChange(boolean selfChange)
        {
            int currentVolume = mAudioManager.getStreamVolume(mAudioStreamType);

            if (currentVolume != mLastVolume)
            {
                mLastVolume = currentVolume;

                mListener.onAudioStreamVolumeChanged(mAudioStreamType, currentVolume);
            }
        }
    }

    private final Context mContext;

    private AudioStreamVolumeContentObserver mAudioStreamVolumeContentObserver;

    public AudioStreamVolumeObserver(
            @NonNull
            Context context)
    {
        mContext = context;
    }

    public void start(int audioStreamType,
                      @NonNull
                      OnAudioStreamVolumeChangedListener listener)
    {
        stop();

        Handler handler = new Handler();
        AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);

        mAudioStreamVolumeContentObserver = new AudioStreamVolumeContentObserver(handler, audioManager, audioStreamType, listener);

        mContext.getContentResolver()
                .registerContentObserver(System.CONTENT_URI, true, mAudioStreamVolumeContentObserver);
    }

    public void stop()
    {
        if (mAudioStreamVolumeContentObserver == null)
        {
            return;
        }

        mContext.getContentResolver()
                .unregisterContentObserver(mAudioStreamVolumeContentObserver);
        mAudioStreamVolumeContentObserver = null;
    }
}

Ответ 6

Если изменяется его единственный режим звонка, вы можете использовать приемник Brodcast с "android.media.RINGER_MODE_CHANGED" в качестве действия. Легко реализовать

Ответ 7

Привет, я пробовал код выше, и это не сработало для меня. Но когда я попытался добавить эту строку

getActivity().setVolumeControlStream(AudioManager.STREAM_MUSIC);

и поместите

mSettingsContentObserver = new SettingsContentObserver(this,new Handler());
getApplicationContext().getContentResolver().registerContentObserver(android.provider.Settings.System.CONTENT_URI, true, mSettingsContentObserver );

Теперь он работает. Моя забота заключается в том, как скрыть диалог томов. Смотрите image.

Ответ 8

  private const val EXTRA_VOLUME_STREAM_TYPE = "android.media.EXTRA_VOLUME_STREAM_TYPE"
  private const val VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION"

    val filter = IntentFilter(VOLUME_CHANGED_ACTION)
    filter.addAction(RINGER_MODE_CHANGED_ACTION)

      val receiver = object : BroadcastReceiver() {
            override fun onReceive(context1: Context, intent: Intent) {

                val stream = intent.getIntExtra(EXTRA_VOLUME_STREAM_TYPE, UNKNOWN)
                val mode = intent.getIntExtra(EXTRA_RINGER_MODE, UNKNOWN)
                val volumeLevel = audioManager.getStreamVolume(stream)
            }
        }