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 private lateinit var mAlarmIcon: ImageView 44 45 private var mPreviewPlaying = false 46 47 override fun onBindViewHolder(holder: PreferenceViewHolder) { 48 super.onBindViewHolder(holder) 49 val context: Context = getContext() 50 val audioManager: AudioManager = context.getSystemService(AUDIO_SERVICE) as AudioManager 51 52 // Disable click feedback for this preference. 53 holder.itemView.setClickable(false) 54 mSeekbar = holder.findViewById(R.id.alarm_volume_slider) as SeekBar 55 mSeekbar.setMax(audioManager.getStreamMaxVolume(STREAM_ALARM)) 56 mSeekbar.setProgress(audioManager.getStreamVolume(STREAM_ALARM)) 57 mAlarmIcon = holder.findViewById(R.id.alarm_icon) as ImageView 58 onSeekbarChanged() 59 60 val volumeObserver: ContentObserver = object : ContentObserver(mSeekbar.getHandler()) { 61 override fun onChange(selfChange: Boolean) { 62 // Volume was changed elsewhere, update our slider. 63 mSeekbar.setProgress(audioManager.getStreamVolume(STREAM_ALARM)) 64 } 65 } 66 67 mSeekbar.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener { 68 override fun onViewAttachedToWindow(v: View?) { 69 context.getContentResolver().registerContentObserver(Settings.System.CONTENT_URI, 70 true, volumeObserver) 71 } 72 73 override fun onViewDetachedFromWindow(v: View?) { 74 context.getContentResolver().unregisterContentObserver(volumeObserver) 75 } 76 }) 77 78 mSeekbar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { 79 override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { 80 if (fromUser) { 81 audioManager.setStreamVolume(STREAM_ALARM, progress, 0) 82 } 83 onSeekbarChanged() 84 } 85 86 override fun onStartTrackingTouch(seekBar: SeekBar?) { 87 } 88 89 override fun onStopTrackingTouch(seekBar: SeekBar) { 90 if (!mPreviewPlaying && seekBar.getProgress() != 0) { 91 // If we are not currently playing and progress is set to non-zero, start. 92 RingtonePreviewKlaxon 93 .start(context, DataModel.dataModel.defaultAlarmRingtoneUri) 94 mPreviewPlaying = true 95 seekBar.postDelayed(Runnable { 96 RingtonePreviewKlaxon.stop(context) 97 mPreviewPlaying = false 98 }, ALARM_PREVIEW_DURATION_MS) 99 } 100 } 101 }) 102 } 103 104 private fun onSeekbarChanged() { 105 mSeekbar.setEnabled(doesDoNotDisturbAllowAlarmPlayback()) 106 val imageRes = if (mSeekbar.getProgress() == 0) { 107 R.drawable.ic_alarm_off_24dp 108 } else { 109 R.drawable.ic_alarm_small 110 } 111 mAlarmIcon.setImageResource(imageRes) 112 } 113 114 private fun doesDoNotDisturbAllowAlarmPlayback(): Boolean { 115 return !Utils.isNOrLater || doesDoNotDisturbAllowAlarmPlaybackNPlus() 116 } 117 118 @TargetApi(Build.VERSION_CODES.N) 119 private fun doesDoNotDisturbAllowAlarmPlaybackNPlus(): Boolean { 120 val notificationManager = 121 getContext().getSystemService(NOTIFICATION_SERVICE) as NotificationManager 122 return notificationManager.getCurrentInterruptionFilter() != 123 NotificationManager.INTERRUPTION_FILTER_NONE 124 } 125 126 companion object { 127 private const val ALARM_PREVIEW_DURATION_MS: Long = 2000 128 } 129 }