1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.deskclock.settings 18 19 import android.annotation.TargetApi 20 import android.app.NotificationManager 21 import android.content.Context 22 import android.content.Context.AUDIO_SERVICE 23 import android.content.Context.NOTIFICATION_SERVICE 24 import android.database.ContentObserver 25 import android.media.AudioManager 26 import android.media.AudioManager.STREAM_ALARM 27 import android.os.Build 28 import android.provider.Settings 29 import android.util.AttributeSet 30 import android.view.View 31 import android.widget.ImageView 32 import android.widget.SeekBar 33 import androidx.preference.Preference 34 import androidx.preference.PreferenceViewHolder 35 36 import com.android.deskclock.R 37 import com.android.deskclock.RingtonePreviewKlaxon 38 import com.android.deskclock.Utils 39 import com.android.deskclock.data.DataModel 40 41 class AlarmVolumePreference(context: Context?, attrs: AttributeSet?) : Preference(context!!, attrs) { 42 private lateinit var mSeekbar: SeekBar 43 44 private var mPreviewPlaying = false 45 onBindViewHoldernull46 override fun onBindViewHolder(holder: PreferenceViewHolder) { 47 super.onBindViewHolder(holder) 48 val context: Context = getContext() 49 val audioManager: AudioManager = context.getSystemService(AUDIO_SERVICE) as AudioManager 50 51 // Disable click feedback for this preference. 52 holder.itemView.setClickable(false) 53 // Minimum volume for alarm is not 0, calculate it. 54 val maxVolume = audioManager.getStreamMaxVolume(STREAM_ALARM) - getMinVolume(audioManager) 55 mSeekbar = holder.findViewById(R.id.alarm_volume_slider) as SeekBar 56 mSeekbar.setMax(maxVolume) 57 mSeekbar.setProgress(audioManager.getStreamVolume(STREAM_ALARM) - 58 getMinVolume(audioManager)) 59 (holder.findViewById(R.id.alarm_icon) as ImageView) 60 .setImageResource(R.drawable.ic_alarm_small) 61 onSeekbarChanged() 62 63 val volumeObserver: ContentObserver = object : ContentObserver(mSeekbar.getHandler()) { 64 override fun onChange(selfChange: Boolean) { 65 // Volume was changed elsewhere, update our slider. 66 mSeekbar.setProgress(audioManager.getStreamVolume(STREAM_ALARM) - 67 getMinVolume(audioManager)) 68 } 69 } 70 71 mSeekbar.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener { 72 override fun onViewAttachedToWindow(v: View) { 73 context.getContentResolver().registerContentObserver(Settings.System.CONTENT_URI, 74 true, volumeObserver) 75 } 76 77 override fun onViewDetachedFromWindow(v: View) { 78 context.getContentResolver().unregisterContentObserver(volumeObserver) 79 } 80 }) 81 82 mSeekbar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { 83 override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { 84 if (fromUser) { 85 val newVolume = progress + getMinVolume(audioManager) 86 audioManager.setStreamVolume(STREAM_ALARM, newVolume, 0) 87 } 88 onSeekbarChanged() 89 } 90 91 override fun onStartTrackingTouch(seekBar: SeekBar?) { 92 } 93 94 override fun onStopTrackingTouch(seekBar: SeekBar) { 95 if (!mPreviewPlaying) { 96 // If we are not currently playing, start. 97 RingtonePreviewKlaxon 98 .start(context, DataModel.dataModel.defaultAlarmRingtoneUri) 99 mPreviewPlaying = true 100 seekBar.postDelayed(Runnable { 101 RingtonePreviewKlaxon.stop(context) 102 mPreviewPlaying = false 103 }, ALARM_PREVIEW_DURATION_MS) 104 } 105 } 106 }) 107 } 108 onSeekbarChangednull109 private fun onSeekbarChanged() { 110 mSeekbar.setEnabled(doesDoNotDisturbAllowAlarmPlayback()) 111 } 112 doesDoNotDisturbAllowAlarmPlaybacknull113 private fun doesDoNotDisturbAllowAlarmPlayback(): Boolean { 114 return !Utils.isNOrLater || doesDoNotDisturbAllowAlarmPlaybackNPlus() 115 } 116 117 @TargetApi(Build.VERSION_CODES.N) doesDoNotDisturbAllowAlarmPlaybackNPlusnull118 private fun doesDoNotDisturbAllowAlarmPlaybackNPlus(): Boolean { 119 val notificationManager = 120 getContext().getSystemService(NOTIFICATION_SERVICE) as NotificationManager 121 return notificationManager.getCurrentInterruptionFilter() != 122 NotificationManager.INTERRUPTION_FILTER_NONE 123 } 124 getMinVolumenull125 private fun getMinVolume(audioManager: AudioManager): Int { 126 return if (Utils.isPOrLater) audioManager.getStreamMinVolume(STREAM_ALARM) else 0 127 } 128 129 companion object { 130 private const val ALARM_PREVIEW_DURATION_MS: Long = 2000 131 } 132 } 133