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