/* * Copyright (C) 2014 CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.notification; import android.content.ContentResolver; import android.content.Context; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.Ringtone; import android.media.RingtoneManager; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.provider.Settings; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceManager; import android.support.v7.preference.PreferenceViewHolder; import android.text.format.Formatter; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.widget.SeekBar; import android.widget.TextView; import com.android.settings.R; import cyanogenmod.providers.CMSettings; public class IncreasingRingVolumePreference extends Preference implements Handler.Callback, SeekBar.OnSeekBarChangeListener { private static final String TAG = "IncreasingRingMinVolumePreference"; public interface Callback { void onStartingSample(); } private SeekBar mStartVolumeSeekBar; private SeekBar mRampUpTimeSeekBar; private TextView mRampUpTimeValue; private Ringtone mRingtone; private Callback mCallback; private Handler mHandler; private final Handler mMainHandler = new Handler(this); private static final int MSG_START_SAMPLE = 1; private static final int MSG_STOP_SAMPLE = 2; private static final int MSG_INIT_SAMPLE = 3; private static final int MSG_SET_VOLUME = 4; private static final int CHECK_RINGTONE_PLAYBACK_DELAY_MS = 1000; public IncreasingRingVolumePreference(Context context) { this(context, null); } public IncreasingRingVolumePreference(Context context, AttributeSet attrs) { this(context, attrs, 0); } public IncreasingRingVolumePreference(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public IncreasingRingVolumePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); setLayoutResource(R.layout.preference_increasing_ring); initHandler(); } public void setCallback(Callback callback) { mCallback = callback; } public void onActivityResume() { initHandler(); } public void onActivityStop() { if (mHandler != null) { postStopSample(); mHandler.getLooper().quitSafely(); mHandler = null; } } @Override public boolean handleMessage(Message msg) { switch (msg.what) { case MSG_START_SAMPLE: onStartSample((float) msg.arg1 / 1000F); break; case MSG_STOP_SAMPLE: onStopSample(); break; case MSG_INIT_SAMPLE: onInitSample(); break; case MSG_SET_VOLUME: onSetVolume((float) msg.arg1 / 1000F); break; } return true; } @Override public void onBindViewHolder(PreferenceViewHolder holder) { super.onBindViewHolder(holder); initHandler(); final SeekBar seekBar = (SeekBar) holder.findViewById(R.id.start_volume); if (seekBar == mStartVolumeSeekBar) return; mStartVolumeSeekBar = seekBar; mRampUpTimeSeekBar = (SeekBar) holder.findViewById(R.id.ramp_up_time); mRampUpTimeValue = (TextView) holder.findViewById(R.id.ramp_up_time_value); final ContentResolver cr = getContext().getContentResolver(); float startVolume = CMSettings.System.getFloat(cr, CMSettings.System.INCREASING_RING_START_VOLUME, 0.1f); int rampUpTime = CMSettings.System.getInt(cr, CMSettings.System.INCREASING_RING_RAMP_UP_TIME, 10); mStartVolumeSeekBar.setProgress(Math.round(startVolume * 1000F)); mStartVolumeSeekBar.setOnSeekBarChangeListener(this); mRampUpTimeSeekBar.setOnSeekBarChangeListener(this); mRampUpTimeSeekBar.setProgress((rampUpTime / 5) - 1); mRampUpTimeValue.setText( Formatter.formatShortElapsedTime(getContext(), rampUpTime * 1000)); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { if (seekBar == mStartVolumeSeekBar) { postStartSample(seekBar.getProgress()); } } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) { ContentResolver cr = getContext().getContentResolver(); if (fromTouch && seekBar == mStartVolumeSeekBar) { CMSettings.System.putFloat(cr, CMSettings.System.INCREASING_RING_START_VOLUME, (float) progress / 1000F); } else if (seekBar == mRampUpTimeSeekBar) { int seconds = (progress + 1) * 5; mRampUpTimeValue.setText( Formatter.formatShortElapsedTime(getContext(), seconds * 1000)); if (fromTouch) { CMSettings.System.putInt(cr, CMSettings.System.INCREASING_RING_RAMP_UP_TIME, seconds); } } } private void initHandler() { if (mHandler != null) return; HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler"); thread.start(); mHandler = new Handler(thread.getLooper(), this); mHandler.sendEmptyMessage(MSG_INIT_SAMPLE); } private void onInitSample() { mRingtone = RingtoneManager.getRingtone(getContext(), Settings.System.DEFAULT_RINGTONE_URI); if (mRingtone != null) { mRingtone.setStreamType(AudioManager.STREAM_RING); mRingtone.setAudioAttributes( new AudioAttributes.Builder(mRingtone.getAudioAttributes()) .setFlags(AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY | AudioAttributes.FLAG_BYPASS_MUTE) .build()); } } private void postStartSample(int progress) { boolean playing = isSamplePlaying(); mHandler.removeMessages(MSG_START_SAMPLE); mHandler.removeMessages(MSG_SET_VOLUME); mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_SAMPLE, progress, 0), playing ? CHECK_RINGTONE_PLAYBACK_DELAY_MS : 0); if (playing) { mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_VOLUME, progress, 0)); } } private void onStartSample(float volume) { if (mRingtone == null) { return; } if (!isSamplePlaying()) { if (mCallback != null) { mCallback.onStartingSample(); } try { mRingtone.play(); } catch (Throwable e) { Log.w(TAG, "Error playing ringtone", e); } } mRingtone.setVolume(volume); } private void onSetVolume(float volume) { if (mRingtone != null) { mRingtone.setVolume(volume); } } private boolean isSamplePlaying() { return mRingtone != null && mRingtone.isPlaying(); } public void stopSample() { if (mHandler != null) { postStopSample(); } } private void postStopSample() { // remove pending delayed start messages mHandler.removeMessages(MSG_START_SAMPLE); mHandler.removeMessages(MSG_STOP_SAMPLE); mHandler.sendEmptyMessage(MSG_STOP_SAMPLE); } private void onStopSample() { if (mRingtone != null) { mRingtone.stop(); } } }