diff --git a/buildSrc/src/main/groovy/Config.groovy b/buildSrc/src/main/groovy/Config.groovy index a69b8907c7..9a8d69d659 100644 --- a/buildSrc/src/main/groovy/Config.groovy +++ b/buildSrc/src/main/groovy/Config.groovy @@ -34,7 +34,7 @@ class Config { lib_base : new ModuleConfig(isApply: true , useLocal: true , localPath: "./lib/base"), lib_common : new ModuleConfig(isApply: true , useLocal: true , localPath: "./lib/common"), lib_subutil : new ModuleConfig(isApply: true , useLocal: true , localPath: "./lib/subutil"), - lib_utilcode : new ModuleConfig(isApply: true , useLocal: false, localPath: "./lib/utilcode", remotePath: "com.blankj:utilcodex:$Config.versionName"), + lib_utilcode : new ModuleConfig(isApply: true , useLocal: true , localPath: "./lib/utilcode", remotePath: "com.blankj:utilcodex:$Config.versionName"), lib_utildebug : new ModuleConfig(isApply: true , useLocal: true , localPath: "./lib/utildebug"), lib_utildebug_no_op : new ModuleConfig(isApply: true , useLocal: true , localPath: "./lib/utildebug-no-op"), /*Don't delete this line*/ diff --git a/config/publish.gradle b/config/publish.gradle index 7608215123..17238d23ea 100644 --- a/config/publish.gradle +++ b/config/publish.gradle @@ -21,206 +21,206 @@ ./gradlew :xxmodule:publish2Remote -> upload to mavenCentral */ -apply plugin: 'maven-publish' -apply plugin: 'signing' - -ext.multiPublishMode = true - -File localPropertiesFile = project.rootProject.file("local.properties"); -if (!localPropertiesFile.exists()) { - return -} - -Properties properties = new Properties() -properties.load(new FileInputStream(localPropertiesFile)) -properties.each { name, value -> ext[name] = value } - -afterEvaluate { - def ext = project.ext - publishing { - publications { - release(MavenPublication) { - groupId ext.groupId - artifactId ext.artifactId - version ext.version - - if (isAndroidEnv(project)) { - if (project.ext.multiPublishMode) { - artifact("$buildDir/outputs/aar/${project.getName()}-release.aar") - artifact sourcesJar - } else { - from project.components.release - } - } else { - from project.components.java - } - - pom { - name = ext.artifactId - description = ext.artifactId - url = ext.website - - licenses { - license { - name = 'The Apache Software License, Version 2.0' - url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' - } - } - developers { - developer { - id = ext.ossrhUsername - name = ext.ossrhUsername - } - } - scm { - url = ext.website - connection = ext.website - developerConnection = ext.website + ".git" - } - - if (project.ext.multiPublishMode) { - withXml { - def dependenciesNode = asNode().getAt('dependencies')[0] ?: - asNode().appendNode('dependencies') - - configurations.api.getDependencies().each { - dep -> addDependency(project, dependenciesNode, dep, "compile") - } - configurations.implementation.getDependencies().each { - dep -> addDependency(project, dependenciesNode, dep, "runtime") - } - } - } - } - } - } - - repositories { - maven { - // s01 is newest - def releasesUrl = "https://s01.oss.sonatype.org/content/repositories/releases/" - def snapshotUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/" - url = version.toUpperCase().endsWith('SNAPSHOT') ? snapshotUrl : releasesUrl - - credentials { - username ossrhUsername - password ossrhPassword - } - } - } - } - - signing { - sign publishing.publications - } -} - -private void addDependency(Project project, def dependenciesNode, Dependency dep, String scope) { - if (dep.group == null || dep.version == null || dep.name == null || dep.name == "unspecified") { - return - } - - final dependencyNode = dependenciesNode.appendNode('dependency') - dependencyNode.appendNode('scope', scope) - - if (dep.version == 'unspecified') { - // 检测 module 中的 dependencies 是否有源码依赖 - // 如果是源码依赖,而且没有在 config 中配置 remotePath, - // 那么发布到仓库,其他地方依赖该库时会找不到源码的那个库 - println "publish -> module(unspecified) <${dep.group}:${dep.name}:${dep.version}>" - if (project.ext.groupId || project.ext.version) { - throw new GradleException("The module of <" + dep.name + "> should set groupId & version.") - } - // 源码依赖,但配置了 remotePath,让 pom 中写入 remotePath - println("publish -> module(wrapped) <${project.ext.groupId}:${name}:${project.ext.version}>") - - dependencyNode.appendNode('groupId', project.ext.pomGroupID) - dependencyNode.appendNode('artifactId', dep.name) - dependencyNode.appendNode('version', project.ext.pomVersion) - } else { - dependencyNode.appendNode('groupId', dep.group) - dependencyNode.appendNode('artifactId', dep.name) - dependencyNode.appendNode('version', dep.version) - println("publish -> library <${dep.group}:${dep.name}:${dep.version}>") - } - - if (!dep.transitive) { - // In case of non transitive dependency, - // all its dependencies should be force excluded from them POM file - final exclusionNode = dependencyNode.appendNode('exclusions').appendNode('exclusion') - exclusionNode.appendNode('groupId', '*') - exclusionNode.appendNode('artifactId', '*') - } else if (!dep.properties.excludeRules.empty) { - // For transitive with exclusions, all exclude rules should be added to the POM file - final exclusions = dependencyNode.appendNode('exclusions') - dep.properties.excludeRules.each { ExcludeRule rule -> - final exclusionNode = exclusions.appendNode('exclusion') - exclusionNode.appendNode('groupId', rule.group ?: '*') - exclusionNode.appendNode('artifactId', rule.module ?: '*') - } - } -} - -if (isAndroidEnv(project)) { - // This generates sources.jar - task sourcesJar(type: Jar) { - classifier = 'sources' - from android.sourceSets.main.java.source - } - - task javadoc(type: Javadoc) { - source = android.sourceSets.main.java.source - classpath += configurations.compile - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) - } - - task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir - } -} else { - task sourcesJar(type: Jar, dependsOn: classes) { - classifier = 'sources' - from sourceSets.main.allSource - } - - task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir - } -} - -if (project.hasProperty("kotlin")) { - // Disable creating javadocs - project.tasks.withType(Javadoc) { - enabled = false - } -} - -javadoc { - options { - encoding "UTF-8" - charSet 'UTF-8' - author true - version project.ext.version - links "http://docs.oracle.com/javase/7/docs/api" - title "${project.ext.artifactId} ${project.ext.version}" - } -} - -artifacts { - archives javadocJar - archives sourcesJar -} - -static def isAndroidEnv(Project project) { - return project.getPlugins().hasPlugin('com.android.application') || project.getPlugins().hasPlugin('com.android.library') -} - -task publish2Local(type: GradleBuild) { - tasks = ['assemble', 'publishReleasePublicationToMavenLocal'] -} - -task publish2Remote(type: GradleBuild) { - tasks = ['assemble', 'publishReleasePublicationToMavenRepository'] -} \ No newline at end of file +//apply plugin: 'maven-publish' +//apply plugin: 'signing' +// +//ext.multiPublishMode = true +// +//File localPropertiesFile = project.rootProject.file("local.properties"); +//if (!localPropertiesFile.exists()) { +// return +//} +// +//Properties properties = new Properties() +//properties.load(new FileInputStream(localPropertiesFile)) +//properties.each { name, value -> ext[name] = value } +// +//afterEvaluate { +// def ext = project.ext +// publishing { +// publications { +// release(MavenPublication) { +// groupId ext.groupId +// artifactId ext.artifactId +// version ext.version +// +// if (isAndroidEnv(project)) { +// if (project.ext.multiPublishMode) { +// artifact("$buildDir/outputs/aar/${project.getName()}-release.aar") +// artifact sourcesJar +// } else { +// from project.components.release +// } +// } else { +// from project.components.java +// } +// +// pom { +// name = ext.artifactId +// description = ext.artifactId +// url = ext.website +// +// licenses { +// license { +// name = 'The Apache Software License, Version 2.0' +// url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' +// } +// } +// developers { +// developer { +// id = ext.ossrhUsername +// name = ext.ossrhUsername +// } +// } +// scm { +// url = ext.website +// connection = ext.website +// developerConnection = ext.website + ".git" +// } +// +// if (project.ext.multiPublishMode) { +// withXml { +// def dependenciesNode = asNode().getAt('dependencies')[0] ?: +// asNode().appendNode('dependencies') +// +// configurations.api.getDependencies().each { +// dep -> addDependency(project, dependenciesNode, dep, "compile") +// } +// configurations.implementation.getDependencies().each { +// dep -> addDependency(project, dependenciesNode, dep, "runtime") +// } +// } +// } +// } +// } +// } +// +// repositories { +// maven { +// // s01 is newest +// def releasesUrl = "https://s01.oss.sonatype.org/content/repositories/releases/" +// def snapshotUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/" +// url = version.toUpperCase().endsWith('SNAPSHOT') ? snapshotUrl : releasesUrl +// +// credentials { +// username ossrhUsername +// password ossrhPassword +// } +// } +// } +// } +// +// signing { +// sign publishing.publications +// } +//} +// +//private void addDependency(Project project, def dependenciesNode, Dependency dep, String scope) { +// if (dep.group == null || dep.version == null || dep.name == null || dep.name == "unspecified") { +// return +// } +// +// final dependencyNode = dependenciesNode.appendNode('dependency') +// dependencyNode.appendNode('scope', scope) +// +// if (dep.version == 'unspecified') { +// // 检测 module 中的 dependencies 是否有源码依赖 +// // 如果是源码依赖,而且没有在 config 中配置 remotePath, +// // 那么发布到仓库,其他地方依赖该库时会找不到源码的那个库 +// println "publish -> module(unspecified) <${dep.group}:${dep.name}:${dep.version}>" +// if (project.ext.groupId || project.ext.version) { +// throw new GradleException("The module of <" + dep.name + "> should set groupId & version.") +// } +// // 源码依赖,但配置了 remotePath,让 pom 中写入 remotePath +// println("publish -> module(wrapped) <${project.ext.groupId}:${name}:${project.ext.version}>") +// +// dependencyNode.appendNode('groupId', project.ext.pomGroupID) +// dependencyNode.appendNode('artifactId', dep.name) +// dependencyNode.appendNode('version', project.ext.pomVersion) +// } else { +// dependencyNode.appendNode('groupId', dep.group) +// dependencyNode.appendNode('artifactId', dep.name) +// dependencyNode.appendNode('version', dep.version) +// println("publish -> library <${dep.group}:${dep.name}:${dep.version}>") +// } +// +// if (!dep.transitive) { +// // In case of non transitive dependency, +// // all its dependencies should be force excluded from them POM file +// final exclusionNode = dependencyNode.appendNode('exclusions').appendNode('exclusion') +// exclusionNode.appendNode('groupId', '*') +// exclusionNode.appendNode('artifactId', '*') +// } else if (!dep.properties.excludeRules.empty) { +// // For transitive with exclusions, all exclude rules should be added to the POM file +// final exclusions = dependencyNode.appendNode('exclusions') +// dep.properties.excludeRules.each { ExcludeRule rule -> +// final exclusionNode = exclusions.appendNode('exclusion') +// exclusionNode.appendNode('groupId', rule.group ?: '*') +// exclusionNode.appendNode('artifactId', rule.module ?: '*') +// } +// } +//} +// +//if (isAndroidEnv(project)) { +// // This generates sources.jar +// task sourcesJar(type: Jar) { +// classifier = 'sources' +// from android.sourceSets.main.java.source +// } +// +// task javadoc(type: Javadoc) { +// source = android.sourceSets.main.java.source +// classpath += configurations.compile +// classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) +// } +// +// task javadocJar(type: Jar, dependsOn: javadoc) { +// classifier = 'javadoc' +// from javadoc.destinationDir +// } +//} else { +// task sourcesJar(type: Jar, dependsOn: classes) { +// classifier = 'sources' +// from sourceSets.main.allSource +// } +// +// task javadocJar(type: Jar, dependsOn: javadoc) { +// classifier = 'javadoc' +// from javadoc.destinationDir +// } +//} +// +//if (project.hasProperty("kotlin")) { +// // Disable creating javadocs +// project.tasks.withType(Javadoc) { +// enabled = false +// } +//} +// +//javadoc { +// options { +// encoding "UTF-8" +// charSet 'UTF-8' +// author true +// version project.ext.version +// links "http://docs.oracle.com/javase/7/docs/api" +// title "${project.ext.artifactId} ${project.ext.version}" +// } +//} +// +//artifacts { +// archives javadocJar +// archives sourcesJar +//} +// +//static def isAndroidEnv(Project project) { +// return project.getPlugins().hasPlugin('com.android.application') || project.getPlugins().hasPlugin('com.android.library') +//} +// +//task publish2Local(type: GradleBuild) { +// tasks = ['assemble', 'publishReleasePublicationToMavenLocal'] +//} +// +//task publish2Remote(type: GradleBuild) { +// tasks = ['assemble', 'publishReleasePublicationToMavenRepository'] +//} \ No newline at end of file diff --git a/feature/utilcode/pkg/src/main/AndroidManifest.xml b/feature/utilcode/pkg/src/main/AndroidManifest.xml index 8da0667a23..94ce260cb3 100644 --- a/feature/utilcode/pkg/src/main/AndroidManifest.xml +++ b/feature/utilcode/pkg/src/main/AndroidManifest.xml @@ -275,6 +275,9 @@ android:name=".feature.volume.VolumeActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:launchMode="singleTop" /> + > { + return CollectionUtils.newArrayList( + startRingItemCLick( + "Start Ring 1", + "ring" + ), + volumeItemSeekBar("Adjust Ring 1 Volume", "ring"), + rateItemSeekBar("Adjust Ring 1 Rate", "ring"), + getLoopSwitch("Set Ring 1 Loop", "ring"), + pauseRingItemCLick("Pause Ring 1", "ring"), + resumeRingItemCLick("Resume Ring 1", "ring"), + stopRingItemCLick("Stop Ring 1", "ring"), + startRingItemCLick( + "Start Ring 2", + "ring2" + ), + volumeItemSeekBar("Adjust Ring 2 Volume", "ring2"), + rateItemSeekBar("Adjust Ring 2 Rate", "ring2"), + getLoopSwitch("Set Ring 2 Loop", "ring2"), + pauseRingItemCLick("Pause Ring 2", "ring2"), + resumeRingItemCLick("Resume Ring 2", "ring2"), + stopRingItemCLick("Stop Ring 2", "ring2") + ) + } + + private fun startRingItemCLick( + title: CharSequence, + resKey: String + ): CommonItemClick { + return CommonItemClick(title) { + SoundUtils.getInstance().stop(resKey) + SoundUtils.getInstance().play(resKey) + } + } + + private fun volumeItemSeekBar(title: CharSequence, resKey: String): CommonItemSeekBar { + var curPos = 100 + return CommonItemSeekBar( + title, + 100, + object : CommonItemSeekBar.ProgressListener() { + override fun getCurValue(): Int { + return curPos + } + + override fun onProgressChanged( + seekBar: SeekBar?, + progress: Int, + fromUser: Boolean + ) { + curPos = progress + SoundUtils.getInstance() + .setVolume(resKey, progress / 100f, progress / 100f) + } + }) + } + + private fun rateItemSeekBar(title: CharSequence, resKey: String): CommonItemSeekBar { + var curPos = 100 + + val itemSeekBar = CommonItemSeekBar( + title, + 50, + 200, + object : CommonItemSeekBar.ProgressListener() { + override fun getCurValue(): Int { + return curPos + } + + override fun onProgressChanged( + seekBar: SeekBar?, + progress: Int, + fromUser: Boolean + ) { + curPos = progress + SoundUtils.getInstance() + .setRate(resKey, progress / 100f) + } + }) + itemSeekBar + return itemSeekBar + } + + + private fun getLoopSwitch(title: CharSequence, resKey: String): CommonItemSwitch { + var isChecked = false + return CommonItemSwitch( + title, + { + isChecked + }, + { + isChecked = it + SoundUtils.getInstance().setLoop(resKey, isChecked) + } + ) + } + + + private fun pauseRingItemCLick(title: CharSequence, resKey: String): CommonItemClick { + return CommonItemClick(title) { + SoundUtils.getInstance().pause(resKey) + } + } + + private fun resumeRingItemCLick(title: CharSequence, resKey: String): CommonItemClick { + return CommonItemClick(title) { + SoundUtils.getInstance().resume(resKey) + } + } + + private fun stopRingItemCLick(title: CharSequence, resKey: String): CommonItemClick { + return CommonItemClick(title) { + SoundUtils.getInstance().stop(resKey) + } + } + + override fun onDestroy() { + super.onDestroy() + SoundUtils.getInstance().release() + } +} diff --git a/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/volume/VolumeActivity.kt b/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/volume/VolumeActivity.kt index 26d67dda07..bc1ff3de0a 100644 --- a/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/volume/VolumeActivity.kt +++ b/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/volume/VolumeActivity.kt @@ -3,12 +3,16 @@ package com.blankj.utilcode.pkg.feature.volume import android.content.Context import android.content.Intent import android.media.AudioManager +import android.os.Bundle +import android.view.View import android.widget.SeekBar import com.blankj.common.activity.CommonActivity import com.blankj.common.item.CommonItem +import com.blankj.common.item.CommonItemClick import com.blankj.common.item.CommonItemSeekBar import com.blankj.utilcode.pkg.R import com.blankj.utilcode.util.CollectionUtils +import com.blankj.utilcode.util.SoundUtils import com.blankj.utilcode.util.VolumeUtils /** @@ -32,30 +36,43 @@ class VolumeActivity : CommonActivity() { return R.string.demo_volume } + override fun bindItems(): MutableList> { return CollectionUtils.newArrayList( - getItemSeekBar("Voice Call", AudioManager.STREAM_VOICE_CALL), - getItemSeekBar("System", AudioManager.STREAM_SYSTEM), - getItemSeekBar("Music", AudioManager.STREAM_MUSIC), - getItemSeekBar("Ring", AudioManager.STREAM_RING), - getItemSeekBar("Alarm", AudioManager.STREAM_ALARM), - getItemSeekBar("Notification", AudioManager.STREAM_NOTIFICATION), - getItemSeekBar("Dtmf", AudioManager.STREAM_DTMF) + getItemSeekBar("Voice Call", AudioManager.STREAM_VOICE_CALL), + getItemSeekBar("System", AudioManager.STREAM_SYSTEM), + getItemSeekBar("Music", AudioManager.STREAM_MUSIC), + getItemSeekBar("Ring", AudioManager.STREAM_RING), + getItemSeekBar("Alarm", AudioManager.STREAM_ALARM), + getItemSeekBar("Notification", AudioManager.STREAM_NOTIFICATION), + getItemSeekBar("Dtmf", AudioManager.STREAM_DTMF) ) } + override fun initView(savedInstanceState: Bundle?, contentView: View?) { + super.initView(savedInstanceState, contentView) + } + private fun getItemSeekBar(title: CharSequence, streamType: Int): CommonItemSeekBar { - return CommonItemSeekBar(title, VolumeUtils.getMaxVolume(streamType), object : CommonItemSeekBar.ProgressListener() { - override fun getCurValue(): Int { - return VolumeUtils.getVolume(streamType) - } - - override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { - VolumeUtils.setVolume(streamType, progress, AudioManager.FLAG_SHOW_UI) - } - }) + return CommonItemSeekBar( + title, + VolumeUtils.getMaxVolume(streamType), + object : CommonItemSeekBar.ProgressListener() { + override fun getCurValue(): Int { + return VolumeUtils.getVolume(streamType) + } + + override fun onProgressChanged( + seekBar: SeekBar?, + progress: Int, + fromUser: Boolean + ) { + VolumeUtils.setVolume(streamType, progress, AudioManager.FLAG_SHOW_UI) + } + }) } + override fun onResume() { super.onResume() itemsView.updateItems(bindItems()) diff --git a/feature/utilcode/pkg/src/main/res/raw/ring.mp3 b/feature/utilcode/pkg/src/main/res/raw/ring.mp3 new file mode 100644 index 0000000000..6e113b4062 Binary files /dev/null and b/feature/utilcode/pkg/src/main/res/raw/ring.mp3 differ diff --git a/feature/utilcode/pkg/src/main/res/raw/ring2.mp3 b/feature/utilcode/pkg/src/main/res/raw/ring2.mp3 new file mode 100644 index 0000000000..a7d090fac7 Binary files /dev/null and b/feature/utilcode/pkg/src/main/res/raw/ring2.mp3 differ diff --git a/feature/utilcode/pkg/src/main/res/values/strings.xml b/feature/utilcode/pkg/src/main/res/values/strings.xml index ccec698647..a33469ac53 100644 --- a/feature/utilcode/pkg/src/main/res/values/strings.xml +++ b/feature/utilcode/pkg/src/main/res/values/strings.xml @@ -43,6 +43,7 @@ TransActivity Demo VibrateUtils Demo VolumeUtils Demo + SoundUtils Demo Shared Element diff --git a/lib/common/src/main/java/com/blankj/common/item/CommonItemSeekBar.java b/lib/common/src/main/java/com/blankj/common/item/CommonItemSeekBar.java index 14ff005851..1fdb0062cd 100644 --- a/lib/common/src/main/java/com/blankj/common/item/CommonItemSeekBar.java +++ b/lib/common/src/main/java/com/blankj/common/item/CommonItemSeekBar.java @@ -1,5 +1,6 @@ package com.blankj.common.item; +import android.os.Build; import android.view.MotionEvent; import android.view.View; import android.widget.SeekBar; @@ -22,10 +23,11 @@ */ public class CommonItemSeekBar extends CommonItem { - private CharSequence mTitle; - private CharSequence mContent; - private int mMaxProgress; - private int mCurProgress; + private CharSequence mTitle; + private CharSequence mContent; + private int mMinProgress; + private int mMaxProgress; + private int mCurProgress; private ProgressListener mProgressListener; public CommonItemSeekBar(@StringRes int title, int maxProgress, @NonNull ProgressListener listener) { @@ -41,6 +43,16 @@ public CommonItemSeekBar(@NonNull CharSequence title, int maxProgress, @NonNull mContent = String.valueOf(mCurProgress); } + public CommonItemSeekBar(@NonNull CharSequence title, int minProgress, int maxProgress, @NonNull ProgressListener listener) { + super(R.layout.common_item_title_seekbar); + mTitle = title; + mMaxProgress = maxProgress; + mMinProgress = minProgress; + mCurProgress = listener.getCurValue(); + mProgressListener = listener; + mContent = String.valueOf(mCurProgress); + } + @Override public void bind(@NonNull ItemViewHolder holder, int position) { @@ -53,6 +65,9 @@ public void bind(@NonNull ItemViewHolder holder, int position) { final SeekBar seekBar = holder.findViewById(R.id.commonItemSb); seekBar.setMax(mMaxProgress); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + seekBar.setMin(mMinProgress); + } seekBar.setProgress(mCurProgress); holder.itemView.setOnTouchListener(new View.OnTouchListener() { @Override diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/SoundUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/SoundUtils.java new file mode 100644 index 0000000000..2be99cce0d --- /dev/null +++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/SoundUtils.java @@ -0,0 +1,410 @@ +package com.blankj.utilcode.util; + +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.media.AudioAttributes; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.media.SoundPool; +import android.os.Build; +import android.os.Bundle; +import android.text.TextUtils; + +import androidx.annotation.RawRes; +import androidx.annotation.RequiresApi; + +import com.blankj.utilcode.R; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Objects; + +/** + * @author leo + * @version 1.0 + * @className SoundUtils + * @description TODO + * @date 2022/11/24 15:21 + **/ +public class SoundUtils { + + private int maxStreams = 5; + private int streamType = AudioManager.STREAM_MUSIC; + private int contentType = AudioAttributes.CONTENT_TYPE_UNKNOWN; + private int flags; + private boolean muted; + private int capturePolicy = AudioAttributes.ALLOW_CAPTURE_BY_NONE; + private int usage; + + private HashMap soundIdMap; + private HashMap streamIdMap; + + private static SoundUtils soundUtils; + + private SoundPool soundPool; + + public static SoundUtils getInstance() { + synchronized (SoundUtils.class) { + if (soundUtils == null) { + soundUtils = new Builder() + .setStreamType(AudioManager.STREAM_MUSIC) + .setMaxStreams(5) + .build(); + } + } + return soundUtils; + } + + + private SoundUtils() { + soundIdMap = new HashMap<>(); + streamIdMap = new HashMap<>(); + create(); + } + + + private void setMaxStreams(int maxStreams) { + this.maxStreams = maxStreams; + } + + private void setStreamType(int streamType) { + this.streamType = streamType; + } + + private void setContentType(int contentType) { + this.contentType = contentType; + } + + private void setFlags(int flags) { + this.flags = flags; + } + + private void setHapticChannelsMuted(boolean muted) { + this.muted = muted; + } + + private void setAllowedCapturePolicy(int capturePolicy) { + this.capturePolicy = capturePolicy; + } + + private void setUsage(int usage) { + this.usage = usage; + } + + public static class Builder { + + private int maxStreams = 5; + private int streamType = AudioManager.STREAM_MUSIC; + private int contentType = AudioAttributes.CONTENT_TYPE_UNKNOWN; + private int flags; + private boolean muted; + private int capturePolicy = AudioAttributes.ALLOW_CAPTURE_BY_NONE; + private int usage; + + + public Builder setMaxStreams(int maxStreams) { + this.maxStreams = maxStreams; + return this; + } + + public Builder setStreamType(int streamType) { + this.streamType = streamType; + return this; + } + + public Builder setContentType(int contentType) { + this.contentType = contentType; + return this; + } + + public Builder setFlags(int flags) { + this.flags = flags; + return this; + } + + public Builder setHapticChannelsMuted(boolean muted) { + this.muted = muted; + return this; + } + + public Builder setAllowedCapturePolicy(int capturePolicy) { + this.capturePolicy = capturePolicy; + return this; + } + + public Builder setUsage(int usage) { + this.usage = usage; + return this; + } + + public SoundUtils build() { + SoundUtils soundUtils = new SoundUtils(); + soundUtils.setFlags(flags); + soundUtils.setContentType(contentType); + soundUtils.setMaxStreams(maxStreams); + soundUtils.setHapticChannelsMuted(muted); + soundUtils.setStreamType(streamType); + soundUtils.setAllowedCapturePolicy(capturePolicy); + soundUtils.setUsage(usage); + return soundUtils; + } + } + + private void create() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + AudioAttributes attributes; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + attributes = new AudioAttributes.Builder() + .setLegacyStreamType(streamType) + .setContentType(contentType) + .setFlags(flags) + .setHapticChannelsMuted(muted) + .setUsage(usage) + .setAllowedCapturePolicy(capturePolicy) + .build(); + } else { + attributes = new AudioAttributes.Builder() + .setLegacyStreamType(streamType) + .setContentType(contentType) + .setFlags(flags) + .setUsage(usage) + .build(); + } + soundPool = new SoundPool.Builder() + .setMaxStreams(maxStreams) + .setAudioAttributes(attributes) + .build(); + } else { + soundPool = new SoundPool(maxStreams, streamType, 0); + } + } + + public void preload(String key, @RawRes int resId) { + preload(key, resId, null); + } + + public void preload(String key, @RawRes int resId, SoundPool.OnLoadCompleteListener listener) { + + if (soundIdMap.size() > maxStreams) { + throw new IllegalArgumentException("u cannot preload raw count > maxStreams..."); + } + + int soundID = soundPool.load(Utils.getApp(), resId, 1); + if (listener != null) { + soundPool.setOnLoadCompleteListener(listener); + } + soundIdMap.put(key, soundID); + } + + public void preload(String key, String path) { + preload(key, path, null); + } + + public void preload(String key, String path, SoundPool.OnLoadCompleteListener listener) { + + if (soundIdMap.size() > maxStreams) { + throw new IllegalArgumentException("u cannot preload raw count > maxStreams..."); + } + + int soundID = soundPool.load(path, 1); + if (listener != null) { + soundPool.setOnLoadCompleteListener(listener); + } + soundIdMap.put(key, soundID); + } + + public void preload(String key, AssetFileDescriptor afd) { + preload(key, afd, null); + } + + public void preload(String key, AssetFileDescriptor afd, SoundPool.OnLoadCompleteListener listener) { + + if (soundIdMap.size() > maxStreams) { + throw new IllegalArgumentException("u cannot preload raw count > maxStreams..."); + } + + int soundID = soundPool.load(afd, 1); + if (listener != null) { + soundPool.setOnLoadCompleteListener(listener); + } + soundIdMap.put(key, soundID); + } + + public void preload(String key, FileDescriptor fd, long offset, long length) { + preload(key, fd, offset, length, null); + } + + public void preload(String key, FileDescriptor fd, long offset, long length, SoundPool.OnLoadCompleteListener listener) { + + if (soundIdMap.size() > maxStreams) { + throw new IllegalArgumentException("u cannot preload raw count > maxStreams..."); + } + + int soundID = soundPool.load(fd, offset, length, 1); + if (listener != null) { + soundPool.setOnLoadCompleteListener(listener); + } + soundIdMap.put(key, soundID); + } + + + public void unload(String key) { + Integer soundId = soundIdMap.get(key); + if (soundId != null) { + soundPool.unload(soundId); + } + soundIdMap.remove(key); + } + + + /** + * @param: key + * @param: leftVolume left volume value (range = 0.0 to 1.0) + * @param: rightVolume right volume value (range = 0.0 to 1.0) + * @param: priority tream priority (0 = lowest priority) + * @param: loop loop mode (0 = no loop, -1 = loop forever) + * @param: rate playback rate (1.0 = normal playback, range 0.5 to 2.0) + * @description: TODO + * @return: void + * @author: leo + * @date: 2022/12/1 9:52 + */ + public void play(String key, float leftVolume, float rightVolume, int priority, int loop, float rate) { + if (soundPool != null) { + Integer resId = soundIdMap.get(key); + if (resId != null) { + int streamID = soundPool.play(resId, leftVolume, rightVolume, priority, loop, rate); + streamIdMap.put(key, streamID); + } else { + throw new IllegalStateException("i have not find this " + key + " raw!"); + } + } + } + + public void play(String key, int priority, int loop, float rate) { + play(key, 1, 1, priority, loop, rate); + } + + public void play(String key, int loop, float rate) { + play(key, 1, 1, 1, loop, rate); + } + + public void play(String key) { + play(key, 1, 1, 1, 0, 1); + } + + public void play(String key, boolean loop) { + play(key, loop ? -1 : 0, 1); + } + + public void play(String key, float rate) { + play(key, 0, rate); + } + + + public void stop(String key) { + Integer streamID = streamIdMap.get(key); + if (streamID != null) { + soundPool.stop(streamID); + } + } + + public void resume(String key) { + Integer streamId = streamIdMap.get(key); + if (streamId != null) { + soundPool.resume(streamId); + } + } + + public void pause(String key) { + Integer streamId = streamIdMap.get(key); + if (streamId != null) { + soundPool.pause(streamId); + } + } + + /** + * @param: key + * @param: loop loop mode (0 = no loop, -1 = loop forever) + * @description: TODO + * @return: void + * @author: leo + * @date: 2022/12/1 10:24 + */ + public void setLoop(String key, int loop) { + if (soundPool != null) { + soundIdMap.get(key); + Integer streamId = streamIdMap.get(key); + if (streamId != null) { + soundPool.pause(streamId); + soundPool.setLoop(streamId, loop); + soundPool.resume(streamId); + } + } + } + + public void setLoop(String key, boolean loop) { + if (loop) { + setLoop(key, -1); + } else { + setLoop(key, 0); + } + } + + /** + * @param: key + * @param: leftVolume left volume value (range = 0.0 to 1.0) + * @param: rightVolume right volume value (range = 0.0 to 1.0) + * @description: TODO + * @return: void + * @author: leo + * @date: 2022/12/1 10:19 + */ + public void setVolume(String key, float leftVolume, float rightVolume) { + if (soundPool != null) { + Integer streamID = streamIdMap.get(key); + if (streamID != null) { + soundPool.setVolume(streamID, leftVolume, rightVolume); + } + } + } + + /** + * @param: key + * @param: rate rate – playback rate (1.0 = normal playback, range 0.5 to 2.0) + * @description: TODO + * @return: void + * @author: leo + * @date: 2022/12/1 10:26 + */ + public void setRate(String key, float rate) { + if (soundPool != null) { + Integer streamID = streamIdMap.get(key); + if (streamID != null) { + soundPool.setRate(streamID, rate); + } + } + } + + + public void release() { + Iterator iterator = soundIdMap.keySet().iterator(); + while (iterator.hasNext()) { + String key = iterator.next(); + stop(key); + Integer soundId = soundIdMap.get(key); + if (soundId != null) { + soundPool.unload(soundId); + } + iterator.remove(); + } + streamIdMap.clear(); + if (soundPool != null) { + soundPool.release(); + soundUtils = null; + } + } +} diff --git a/lib/utilcode/src/test/java/com/blankj/utilcode/util/SoundUtilsTest.java b/lib/utilcode/src/test/java/com/blankj/utilcode/util/SoundUtilsTest.java new file mode 100644 index 0000000000..d79e4f8c4a --- /dev/null +++ b/lib/utilcode/src/test/java/com/blankj/utilcode/util/SoundUtilsTest.java @@ -0,0 +1,46 @@ +package com.blankj.utilcode.util; + +import android.media.AudioManager; + +import com.blankj.utilcode.R; + +import org.junit.Test; + +/** + * @author leo + * @version 1.0 + * @className SoundUtilsTest + * @description TODO + * @date 2022/11/30 11:15 + **/ +public class SoundUtilsTest extends BaseTest { + + @Test + public void play() { + //前奏 + SoundUtils soundUtils = new SoundUtils.Builder() + .setMaxStreams(8) + .setStreamType(AudioManager.STREAM_RING) + .build(); + + + +// //1.用法1 +// SoundUtils.getInstance().preload("ring", R.raw.ring); +// SoundUtils.getInstance().play("ring"); +// +// //2.用法2 +// soundUtils.preload("ring",R.raw.ring); +// soundUtils.preload("ringback",R.raw.ring); +// +// soundUtils.play("ring"); +// +// +// SoundUtils.getInstance().destroy(); +// soundUtils.destroy(); +// +// +// SoundUtils.play(); + + } +}