Merge branch 'devel'

master v1.2.0
yattoz 2022-02-19 15:08:38 +09:00
commit 7ea4d6ed67
19 changed files with 227 additions and 44 deletions

View File

@ -1,8 +1,5 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<AndroidXmlCodeStyleSettings>
<option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" />
</AndroidXmlCodeStyleSettings>
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>

View File

@ -16,8 +16,8 @@ configurations.all {
} }
android { android {
compileSdkVersion 29 compileSdkVersion 30
buildToolsVersion "29.0.2" buildToolsVersion "30.0.1"
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
@ -28,9 +28,9 @@ android {
defaultConfig { defaultConfig {
applicationId "fr.forum_thalie.tsumugi" applicationId "fr.forum_thalie.tsumugi"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 29 targetSdkVersion 30
versionCode 115 versionCode 120
versionName "1.1.5" versionName "1.2.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
} }

View File

@ -13,6 +13,7 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.SET_ALARM"/> <uses-permission android:name="android.permission.SET_ALARM"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application <application
android:allowBackup="true" android:allowBackup="true"
@ -26,7 +27,7 @@
<service <service
android:name=".RadioService" android:name=".RadioService"
android:enabled="true" android:enabled="true"
android:exported="true"> android:exported="false">
<intent-filter> <intent-filter>
<action android:name="android.media.browse.MediaBrowserService" /> <action android:name="android.media.browse.MediaBrowserService" />
</intent-filter> </intent-filter>
@ -60,7 +61,7 @@
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:launchMode="singleTask" android:launchMode="singleTask"
android:parentActivityName=".MainActivity" android:parentActivityName=".MainActivity"
android:screenOrientation="fullSensor" /> android:screenOrientation="fullUser" />
<receiver android:name="androidx.media.session.MediaButtonReceiver"> <receiver android:name="androidx.media.session.MediaButtonReceiver">
<intent-filter> <intent-filter>

View File

@ -1,7 +1,7 @@
{ {
"planning": [ "planning": [
{ {
"title": "Musique Classique", "title": "Le Classique du Matin",
"periodicity": "1111111", "periodicity": "1111111",
"hour_begin": "6:00", "hour_begin": "6:00",
"hour_end": "7:00" "hour_end": "7:00"

View File

@ -117,7 +117,7 @@ class MainActivity : BaseActivity() {
} }
} }
override fun onRestoreInstanceState(savedInstanceState: Bundle?) { override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState ?: Bundle()) super.onRestoreInstanceState(savedInstanceState ?: Bundle())
// Now that BottomNavigationBar has restored its instance state // Now that BottomNavigationBar has restored its instance state
// and its selectedItemId, we can proceed with setting up the // and its selectedItemId, we can proceed with setting up the

View File

@ -454,12 +454,13 @@ class RadioService : MediaBrowserServiceCompat() {
// this function is playing the stream if available, or a default sound if there's a problem. // this function is playing the stream if available, or a default sound if there's a problem.
private fun beginPlayingOrFallback() private fun beginPlayingOrFallback()
{ {
PlayerStore.instance.volume.value = 100 // we set the max volume for exoPlayer to be sure it rings correctly. PlayerStore.instance.volume.value = PreferenceManager.getDefaultSharedPreferences(this).getInt("alarmVolume", 100)
// we set the max volume for exoPlayer to be sure it rings correctly.
beginPlaying(isRinging = true, isFallback = false) beginPlaying(isRinging = true, isFallback = false)
val wait: (Any?) -> Any = { val wait: (Any?) -> Any = {
/* /*
Here we lower the isAlarmStopped flag and we wait for 17s. (seems like 12 could be a bit too short since I increased the buffer!!) Here we lower the isAlarmStopped flag and we wait for 17s. (seems like 12 could be a bit too short since I increased the buffer!!)
If the player stops the alarm (by calling an intent), the isAlarmStopped flag will be raised. If the user stops the alarm (by calling an intent), the isAlarmStopped flag will be raised.
*/ */
isAlarmStopped = false // reset the flag isAlarmStopped = false // reset the flag
var i = 0 var i = 0
@ -498,6 +499,7 @@ class RadioService : MediaBrowserServiceCompat() {
.setUsage(C.USAGE_ALARM) .setUsage(C.USAGE_ALARM)
.build() .build()
} else { } else {
isAlarmStopped = true // if we're not ringing and it tries playing, it means the user opened the app somehow
audioAttributes.setUsage(AudioAttributesCompat.USAGE_MEDIA) audioAttributes.setUsage(AudioAttributesCompat.USAGE_MEDIA)
audioFocusRequestBuilder.setAudioAttributes(audioAttributes.build()) audioFocusRequestBuilder.setAudioAttributes(audioAttributes.build())
audioFocusRequest = audioFocusRequestBuilder.build() audioFocusRequest = audioFocusRequestBuilder.build()
@ -513,8 +515,6 @@ class RadioService : MediaBrowserServiceCompat() {
return return
} }
if (mediaSession.controller.playbackState.state == PlaybackStateCompat.STATE_PLAYING && !isRinging)
return // nothing to do here
PlayerStore.instance.playbackState.value = PlaybackStateCompat.STATE_PLAYING PlayerStore.instance.playbackState.value = PlaybackStateCompat.STATE_PLAYING
// Reinitialize media player. Otherwise the playback doesn't resume when beginPlaying. Dunno why. // Reinitialize media player. Otherwise the playback doesn't resume when beginPlaying. Dunno why.
@ -530,7 +530,6 @@ class RadioService : MediaBrowserServiceCompat() {
} }
// START PLAYBACK, LET'S ROCK // START PLAYBACK, LET'S ROCK
player.playWhenReady = true
nowPlayingNotification.update(this, isUpdatingNotificationButton = true, isRinging = isRinging) nowPlayingNotification.update(this, isUpdatingNotificationButton = true, isRinging = isRinging)
playbackStateBuilder.setState( playbackStateBuilder.setState(
@ -540,6 +539,8 @@ class RadioService : MediaBrowserServiceCompat() {
SystemClock.elapsedRealtime() SystemClock.elapsedRealtime()
) )
mediaSession.setPlaybackState(playbackStateBuilder.build()) mediaSession.setPlaybackState(playbackStateBuilder.build())
player.playWhenReady = true
//[REMOVE LOG CALLS]Log.d(tag, radioTag + "begin playing") //[REMOVE LOG CALLS]Log.d(tag, radioTag + "begin playing")
} }
@ -553,7 +554,6 @@ class RadioService : MediaBrowserServiceCompat() {
{ {
if (mediaSession.controller.playbackState.state == PlaybackStateCompat.STATE_STOPPED) if (mediaSession.controller.playbackState.state == PlaybackStateCompat.STATE_STOPPED)
return // nothing to do here return // nothing to do here
if (PlayerStore.instance.playbackState.value == PlaybackStateCompat.STATE_PLAYING) if (PlayerStore.instance.playbackState.value == PlaybackStateCompat.STATE_PLAYING)
isAlarmStopped = true isAlarmStopped = true

View File

@ -0,0 +1,129 @@
package fr.forum_thalie.tsumugi.preferences
import android.content.Context
import android.content.Intent
import android.media.AudioManager
import android.os.Bundle
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import androidx.preference.SeekBarPreference
import androidx.preference.SwitchPreferenceCompat
import fr.forum_thalie.tsumugi.Actions
import fr.forum_thalie.tsumugi.R
import fr.forum_thalie.tsumugi.RadioService
import fr.forum_thalie.tsumugi.playerstore.PlayerStore
import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext
import kotlin.math.max
import kotlin.math.min
class AlarmAdjustVolumeFragment : PreferenceFragmentCompat() {
override fun onAttach(context: Context) {
(activity as AppCompatActivity).supportActionBar?.title = context.getString(R.string.test_alarm_volume)
super.onAttach(context)
}
// get previous state: if it's playing, we'll resume playing as multimedia; if it was stopped, we'll stop
private var isPlayingMultimedia: Boolean = PlayerStore.instance.isPlaying.value ?: false
override fun onStop() {
if (isPlayingMultimedia)
{
actionOnService(Actions.PLAY)
} else {
actionOnService(Actions.PAUSE)
}
PlayerStore.instance.volume.value = PreferenceManager.getDefaultSharedPreferences(requireContext()).getInt("volume", 100)
super.onStop()
}
override fun onResume() {
isPlayingMultimedia = PlayerStore.instance.isPlaying.value ?: false
// start as alarm
actionOnService(Actions.PLAY_OR_FALLBACK)
super.onResume()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
fun <T> debounce(delayMs: Long = 500L,
coroutineContext: CoroutineContext,
f: (T) -> Unit): (T) -> Unit {
var debounceJob: Job? = null
return { param: T ->
if (debounceJob?.isCompleted != false) {
debounceJob = CoroutineScope(coroutineContext).launch {
delay(delayMs)
f(param)
}
}
}
}
val adjustAlarmVolume: (Int) -> Unit = debounce<Int>(50, GlobalScope.coroutineContext) {
android.util.Log.d(tag, "button $it pushed")
val keyCode = it
val audioManager = requireContext().getSystemService(Context.AUDIO_SERVICE) as AudioManager
audioManager.apply {
val currentVolume = this.getStreamVolume(AudioManager.STREAM_ALARM)
val minVolume = 0 // audioManager.getStreamMinVolume(AudioManager.STREAM_ALARM) <- require API28+
val maxVolume = this.getStreamMaxVolume(AudioManager.STREAM_ALARM)
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
this.setStreamVolume(AudioManager.STREAM_ALARM, max(currentVolume - 1, minVolume), AudioManager.FLAG_SHOW_UI)
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP){
this.setStreamVolume(AudioManager.STREAM_ALARM, min(currentVolume + 1, maxVolume), AudioManager.FLAG_SHOW_UI)
} else {
}
}
}
view.isFocusableInTouchMode = true;
view.requestFocus();
view.setOnKeyListener { _, i, event ->
if (i == KeyEvent.KEYCODE_VOLUME_DOWN || i == KeyEvent.KEYCODE_VOLUME_UP) {
adjustAlarmVolume(i)
true
} else {
false
}
}
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.alarm_adjust_volume_preferences, rootKey)
val alarmVolumeSeekBar = findPreference<SeekBarPreference>("alarmVolume")
alarmVolumeSeekBar?.apply {
min = 0
max = 100
updatesContinuously = true
setOnPreferenceChangeListener { _, newValue ->
actionOnService(Actions.VOLUME, newValue as Int)
true
}
}
}
private fun actionOnService(a: Actions, v: Int = 0)
{
val i = Intent(requireContext(), RadioService::class.java)
i.putExtra("action", a.name)
i.putExtra("value", v)
//[REMOVE LOG CALLS]Log.d(tag, "Sending intent ${a.name}")
requireContext().startService(i)
}
}

View File

@ -2,15 +2,29 @@ package fr.forum_thalie.tsumugi.preferences
import android.app.TimePickerDialog import android.app.TimePickerDialog
import android.content.Context import android.content.Context
import android.content.Intent
import android.media.AudioManager
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.KeyEvent
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit import androidx.core.content.edit
import androidx.preference.* import androidx.preference.*
import fr.forum_thalie.tsumugi.* import fr.forum_thalie.tsumugi.*
import fr.forum_thalie.tsumugi.R import fr.forum_thalie.tsumugi.R
import fr.forum_thalie.tsumugi.alarm.RadioAlarm import fr.forum_thalie.tsumugi.alarm.RadioAlarm
import fr.forum_thalie.tsumugi.playerstore.PlayerStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope.coroutineContext
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.util.* import java.util.*
import kotlin.coroutines.CoroutineContext
import kotlin.math.max
import kotlin.math.min
class AlarmFragment : PreferenceFragmentCompat() { class AlarmFragment : PreferenceFragmentCompat() {
@ -129,12 +143,10 @@ class AlarmFragment : PreferenceFragmentCompat() {
true true
} }
alarmDays?.isEnabled = isWakingUp?.isChecked ?: false alarmDays?.isEnabled = isWakingUp?.isChecked ?: false
timeSet?.isEnabled = isWakingUp?.isChecked ?: false timeSet?.isEnabled = isWakingUp?.isChecked ?: false
snoozeDuration?.isEnabled = isWakingUp?.isChecked ?: false snoozeDuration?.isEnabled = isWakingUp?.isChecked ?: false
} }
} }

View File

@ -166,10 +166,10 @@ class NowPlayingFragment : Fragment() {
} }
}) })
seekBarVolume.setOnSeekBarChangeListener(nowPlayingViewModel.seekBarChangeListener)
seekBarVolume.progress = PlayerStore.instance.volume.value!! seekBarVolume.progress = PlayerStore.instance.volume.value!!
progressBar.max = 1000 seekBarVolume.setOnSeekBarChangeListener(nowPlayingViewModel.seekBarChangeListener)
progressBar.max = 100
progressBar.progress = 0 progressBar.progress = 0
syncPlayPauseButtonImage(root) syncPlayPauseButtonImage(root)

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
</selector>

View File

@ -56,5 +56,13 @@
<string name="action_refresh">Raffraîchir les données</string> <string name="action_refresh">Raffraîchir les données</string>
<string name="autoPlay">\"Play\" au branchement de la prise casque</string> <string name="autoPlay">\"Play\" au branchement de la prise casque</string>
<string name="settings">Paramètres</string> <string name="settings">Paramètres</string>
<string name="test_alarm_volume">Tester et ajuster le volume de l\'alarme</string>
<string name="test_alarm_volume_summary">Touchez pour écouter et régler le volume</string>
<string name="popupTestAlarmVolume">La radio est à présent lancée en mode alarme. Ajustez le volume avec les boutons de volume de votre appareil. Vous pouvez faire un réglage plus fin du volume avec la réglette ci-dessous.</string>
<string name="finished">Terminé</string>
<string name="alarmVolumeSeekBar">Ajuster le volume de l\'alarme</string>
<string name="setupAlarmClockVolume">Régler le volume de l\'alarme</string>
<string name="alarm_volume">Volume de l\'alarme</string>
</resources> </resources>

View File

@ -76,5 +76,12 @@
<string name="action_refresh">Refresh data</string> <string name="action_refresh">Refresh data</string>
<string name="autoPlay">Auto play when plugging headphones</string> <string name="autoPlay">Auto play when plugging headphones</string>
<string name="settings">Settings</string> <string name="settings">Settings</string>
<string name="test_alarm_volume">Test and change alarm volume</string>
<string name="test_alarm_volume_summary">Touch this to listen to the alarm volume and set it up</string>
<string name="popupTestAlarmVolume">The radio stream is now started in alarm mode. Adjust the volume with the volume buttons on your device. You can make a finer adjustment of the volume with the slider below.</string>
<string name="finished">Done</string>
<string name="alarmVolumeSeekBar">Adjust alarm volume</string>
<string name="setupAlarmClockVolume">Set up alarm volume</string>
<string name="alarm_volume">Alarm volume</string>
</resources> </resources>

View File

@ -11,8 +11,14 @@
<item name="android:colorBackground">@color/colorPrimaryDark</item> <item name="android:colorBackground">@color/colorPrimaryDark</item>
</style> </style>
<style name="AppTheme.Parameters" parent="AppTheme"> <style name="AppTheme.Parameters" parent="Theme.AppCompat.NoActionBar">
<item name="icon"/> <!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:textColorHighlight">@color/rblue</item>
<item name="android:colorBackground">@color/colorPrimaryDark</item>
<item name="icon">@drawable/none</item>
</style> </style>
<style name="AppTheme.BottomBar" parent="AppTheme"> <style name="AppTheme.BottomBar" parent="AppTheme">

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Preference
app:key="alarmAdjustText"
app:iconSpaceReserved="false"
app:title=""
android:selectable="false"
android:persistent="false"
app:summary="@string/popupTestAlarmVolume"
app:singleLineTitle="false"
/>
<SeekBarPreference
app:key="alarmVolume"
app:iconSpaceReserved="false"
app:title="@string/alarm_volume"
app:defaultValue="100"
app:showSeekBarValue="true"
/>
</PreferenceScreen>

View File

@ -37,4 +37,13 @@
android:summary="%s" android:summary="%s"
/> />
<Preference
app:iconSpaceReserved="true"
android:key="testAlarmVolume"
android:title="@string/test_alarm_volume"
android:summary="@string/test_alarm_volume_summary"
app:icon="@drawable/ic_volume_high"
app:fragment="fr.forum_thalie.tsumugi.preferences.AlarmAdjustVolumeFragment"
/>
</PreferenceScreen> </PreferenceScreen>

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen <PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android">
>
<Preference <Preference
app:icon="@drawable/ic_alarm" app:icon="@drawable/ic_alarm"

View File

@ -1,13 +1,13 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = '1.3.61' ext.kotlin_version = '1.3.72'
repositories { repositories {
google() google()
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.5.3' classpath 'com.android.tools.build:gradle:4.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files

View File

@ -1,6 +1,6 @@
#Sun Oct 13 11:20:35 CEST 2019 #Sat Aug 08 18:44:30 CEST 2020
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip