├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── masoudss │ │ ├── activity │ │ ├── MainActivity.kt │ │ └── SelectAudioActivity.kt │ │ ├── adapter │ │ └── AudioAdapter.kt │ │ └── model │ │ └── AudioModel.kt │ └── res │ ├── drawable │ ├── ic_audio.xml │ ├── ic_github.xml │ └── ic_import.xml │ ├── layout │ ├── activity_main.xml │ ├── activity_select_audio.xml │ └── item_audio.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── files ├── dancer.gif ├── music.gif ├── preview.gif └── preview.png ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── lib ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── masoudss │ │ └── lib │ │ ├── DanceView.kt │ │ ├── SeekBarOnProgressChanged.kt │ │ ├── WaveformSeekBar.kt │ │ ├── audiovisulizer │ │ ├── base │ │ │ └── BaseVisualizer.java │ │ ├── model │ │ │ ├── AnimSpeed.java │ │ │ ├── PaintStyle.java │ │ │ └── PositionGravity.java │ │ └── utils │ │ │ ├── AVConstants.java │ │ │ └── BezierSpline.java │ │ ├── exception │ │ ├── InvalidInputException.kt │ │ └── SampleDataException.kt │ │ └── utils │ │ ├── AudioPlayer.java │ │ ├── ThreadBlocking.kt │ │ ├── Utils.kt │ │ ├── WaveGravity.kt │ │ └── WaveformOptions.kt │ └── res │ ├── raw │ ├── sample2.mp3 │ ├── sample3.mp3 │ └── sample5.mp3 │ └── values │ ├── attrs.xml │ └── strings.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | .idea 10 | /.idea/assetWizardSettings.xml 11 | .DS_Store 12 | /build 13 | /captures 14 | .externalNativeBuild 15 | *.log 16 | bin/ 17 | gen/ 18 | out/ 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## danceView使用方法 3 | ```xml 4 | 18 | 19 | ``` 20 | 21 | 22 | ```xml 23 | shader_num 顶部加渐变的个数 24 | 25 | color_end 渐变尾部颜色 26 | 27 | color_start 渐变开头颜色 28 | 29 | color_center 渐变中间颜色 30 | 31 | min_dance_num 每一列中最少显示的个数 32 | 33 | max_dance_num 每一列中最大显示的个数 34 | 35 | dance_gap 每一个音频格之间的间距 36 | 37 | ``` 38 | 39 | ## Visualizer使用 40 | 41 | **1、获取实例** 42 | 43 | ```java 44 | visualizer = new Visualizer(mediaPlayer.getAudioSessionId()); 45 | ``` 46 | 47 | **2、设置采样值** 48 | 49 | ```java 50 | visualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]); 51 | ``` 52 | 53 | 通过Visualizer.getCaptureSizeRange()这一底层实现的方法来返回一个采样值的范围数组,0为最小值128,1为最大值1024!采样值都为2的n次幂! 54 | 55 | **3、设置监听器** 56 | 57 | ```java 58 | setDataCaptureListener(OnDataCaptureListener listener, rate,iswave,isfft ) 59 | ``` 60 | 61 | 先说后面三个参数:rate采样的频率,下边通过方法Visualizer.getMaxCaptureRate()返回最大的采样频率,单位为milliHertz毫赫兹,iswave是波形信号,isfft是频域信号。 62 | 第一个参数OnDataCaptureListener接口,这里可以一个它的匿名内部类,然后它有两个回调方法: 63 | 64 | ```java 65 | onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) 66 | ``` 67 | 和 68 | ```java 69 | onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) 70 | ``` 71 | 72 | 这两个回调对应着上边的两个参数iswave和isfft!如果iswave为true,isfft为false则会回调onWaveFormDataCapture方法,如果iswave为false,isfft为true则会回调onFftDataCapture方法。 73 | 74 | 75 | 76 | 77 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 78 | 79 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | compileSdkVersion 30 9 | ndkVersion "16.1.4479499" 10 | 11 | defaultConfig { 12 | applicationId "com.masoudss" 13 | minSdkVersion 21 14 | targetSdkVersion 30 15 | versionCode 1 16 | versionName "1.0" 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | } 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(dir: 'libs', include: ['*.jar']) 29 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 30 | implementation 'androidx.appcompat:appcompat:1.1.0-alpha04' 31 | implementation 'androidx.core:core-ktx:1.1.0-alpha05' 32 | implementation 'com.google.android.material:material:1.1.0-alpha05' 33 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 34 | implementation "org.jetbrains.anko:anko-commons:0.10.0" 35 | implementation project(':lib') 36 | } 37 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/masoudss/activity/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.masoudss.activity 2 | 3 | import android.Manifest 4 | import android.app.Activity 5 | import android.app.ProgressDialog 6 | import android.content.Intent 7 | import android.content.pm.PackageManager 8 | import android.os.Build 9 | import android.os.Bundle 10 | import android.util.Log 11 | import android.widget.RadioButton 12 | import android.widget.SeekBar 13 | import android.widget.Toast 14 | import androidx.appcompat.app.AppCompatActivity 15 | import androidx.core.content.ContextCompat 16 | import com.masoudss.R 17 | import com.masoudss.lib.SeekBarOnProgressChanged 18 | import com.masoudss.lib.WaveformSeekBar 19 | import com.masoudss.lib.utils.AudioPlayer 20 | import com.masoudss.lib.utils.Utils 21 | import com.masoudss.lib.utils.WaveGravity 22 | import kotlinx.android.synthetic.main.activity_main.* 23 | import org.jetbrains.anko.doAsync 24 | import org.jetbrains.anko.uiThread 25 | import java.util.* 26 | 27 | 28 | class MainActivity : AppCompatActivity() { 29 | companion object { 30 | private const val REQ_CODE_PICK_SOUND_FILE = 1 31 | private const val REQ_CODE_STORAGE_PERMMISION = 2 32 | private const val REQ_CODE_AUDIO_PERMMISION = 3 33 | 34 | } 35 | 36 | 37 | private var mAudioPlayer: AudioPlayer = AudioPlayer() 38 | override fun onCreate(savedInstanceState: Bundle?) { 39 | super.onCreate(savedInstanceState) 40 | setContentView(R.layout.activity_main) 41 | /** 42 | * 先检查录音权限,然后再打开录音 43 | */ 44 | checkAudioPermission() 45 | waveformSeekBar.apply { 46 | progress = 33 47 | waveWidth = Utils.dp(this@MainActivity, 5) 48 | waveGap = Utils.dp(this@MainActivity, 2) 49 | waveMinHeight = Utils.dp(this@MainActivity, 5) 50 | waveCornerRadius = Utils.dp(this@MainActivity, 2) 51 | waveGravity = WaveGravity.CENTER 52 | waveBackgroundColor = ContextCompat.getColor(this@MainActivity, R.color.white) 53 | waveProgressColor = ContextCompat.getColor(this@MainActivity, R.color.blue) 54 | sample = getDummyWaveSample() 55 | onProgressChanged = object : SeekBarOnProgressChanged { 56 | override fun onProgressChanged( 57 | waveformSeekBar: WaveformSeekBar, 58 | progress: Int, 59 | fromUser: Boolean 60 | ) { 61 | if (fromUser) 62 | waveProgress.progress = progress 63 | } 64 | } 65 | } 66 | 67 | waveWidth.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { 68 | override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { 69 | waveformSeekBar.waveWidth = progress / 100F * Utils.dp(this@MainActivity, 20) 70 | } 71 | 72 | override fun onStartTrackingTouch(seekBar: SeekBar?) {} 73 | 74 | override fun onStopTrackingTouch(seekBar: SeekBar?) {} 75 | }) 76 | 77 | waveCornerRadius.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { 78 | override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { 79 | waveformSeekBar.waveCornerRadius = progress / 100F * Utils.dp(this@MainActivity, 10) 80 | } 81 | 82 | override fun onStartTrackingTouch(seekBar: SeekBar?) {} 83 | 84 | override fun onStopTrackingTouch(seekBar: SeekBar?) {} 85 | }) 86 | 87 | waveGap.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { 88 | override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { 89 | waveformSeekBar.waveGap = progress / 100F * Utils.dp(this@MainActivity, 10) 90 | } 91 | 92 | override fun onStartTrackingTouch(seekBar: SeekBar?) {} 93 | 94 | override fun onStopTrackingTouch(seekBar: SeekBar?) {} 95 | }) 96 | 97 | waveProgress.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { 98 | override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { 99 | waveformSeekBar.progress = progress 100 | } 101 | 102 | override fun onStartTrackingTouch(seekBar: SeekBar?) {} 103 | 104 | override fun onStopTrackingTouch(seekBar: SeekBar?) {} 105 | }) 106 | 107 | waveMaxProgress.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { 108 | override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { 109 | waveProgress.max = progress 110 | waveformSeekBar.maxProgress = progress.toFloat() 111 | } 112 | 113 | override fun onStartTrackingTouch(seekBar: SeekBar?) {} 114 | 115 | override fun onStopTrackingTouch(seekBar: SeekBar?) {} 116 | }) 117 | 118 | gravityRadioGroup.setOnCheckedChangeListener { _, checkedId -> 119 | 120 | val radioButton = gravityRadioGroup.findViewById(checkedId) as RadioButton 121 | val index = gravityRadioGroup.indexOfChild(radioButton) 122 | waveformSeekBar.waveGravity = when (index) { 123 | 0 -> WaveGravity.TOP 124 | 1 -> WaveGravity.CENTER 125 | else -> WaveGravity.BOTTOM 126 | } 127 | } 128 | 129 | waveColorRadioGroup.setOnCheckedChangeListener { _, checkedId -> 130 | 131 | val radioButton = waveColorRadioGroup.findViewById(checkedId) as RadioButton 132 | val index = waveColorRadioGroup.indexOfChild(radioButton) 133 | waveformSeekBar.waveBackgroundColor = when (index) { 134 | 0 -> ContextCompat.getColor(this, R.color.pink) 135 | 1 -> ContextCompat.getColor(this, R.color.yellow) 136 | else -> ContextCompat.getColor(this, R.color.white) 137 | } 138 | } 139 | 140 | progressColorRadioGroup.setOnCheckedChangeListener { _, checkedId -> 141 | 142 | val radioButton = progressColorRadioGroup.findViewById(checkedId) as RadioButton 143 | val index = progressColorRadioGroup.indexOfChild(radioButton) 144 | waveformSeekBar.waveProgressColor = when (index) { 145 | 0 -> ContextCompat.getColor(this, R.color.red) 146 | 1 -> ContextCompat.getColor(this, R.color.blue) 147 | else -> ContextCompat.getColor(this, R.color.green) 148 | } 149 | } 150 | 151 | icImport.setOnClickListener { 152 | checkStoragePermission() 153 | } 154 | } 155 | 156 | public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { 157 | super.onActivityResult(requestCode, resultCode, data) 158 | if (data != null && requestCode == REQ_CODE_PICK_SOUND_FILE && resultCode == Activity.RESULT_OK) { 159 | val path = data.getStringExtra("path") 160 | 161 | val progressDialog = ProgressDialog(this@MainActivity) 162 | progressDialog.setMessage(getString(R.string.message_waiting)) 163 | progressDialog.show() 164 | 165 | 166 | doAsync { 167 | // waveformSeekBar.setSampleFrom(path!!) 168 | 169 | uiThread { 170 | progressDialog.dismiss() 171 | } 172 | } 173 | } 174 | // if (data != null && requestCode == REQ_CODE_AUDIO_PERMMISION && resultCode == Activity.RESULT_OK) { 175 | // startPlayingAudio(R.raw.sample3) 176 | // } 177 | } 178 | 179 | private fun checkStoragePermission() { 180 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 181 | val hasReadPermission = checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) 182 | val hasWritePermission = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) 183 | 184 | val permissions = ArrayList() 185 | 186 | if (hasReadPermission != PackageManager.PERMISSION_GRANTED) 187 | permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE) 188 | 189 | if (hasWritePermission != PackageManager.PERMISSION_GRANTED) 190 | permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE) 191 | 192 | if (permissions.isNotEmpty()) 193 | requestPermissions(permissions.toTypedArray(), REQ_CODE_STORAGE_PERMMISION) 194 | else 195 | launchSelectAudioActivity() 196 | 197 | } else 198 | launchSelectAudioActivity() 199 | } 200 | 201 | /** 202 | *检查录音权限 203 | */ 204 | private fun checkAudioPermission() { 205 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 206 | val hasRecordPermission = checkSelfPermission(Manifest.permission.RECORD_AUDIO) 207 | val permissions = ArrayList() 208 | if (hasRecordPermission != PackageManager.PERMISSION_GRANTED) 209 | permissions.add(Manifest.permission.RECORD_AUDIO) 210 | if (permissions.isNotEmpty()) { 211 | requestPermissions(permissions.toTypedArray(), REQ_CODE_AUDIO_PERMMISION) 212 | } else { 213 | startPlayingAudio(R.raw.sample3) 214 | } 215 | 216 | } 217 | } 218 | 219 | override fun onRequestPermissionsResult( 220 | requestCode: Int, 221 | permissions: Array, 222 | grantResults: IntArray 223 | ) { 224 | Log.d( 225 | "MainActivity", 226 | "onRequestPermissionsResult requestCode $requestCode permissions ${permissions.contentToString()} grantResults ${grantResults.contentToString()}" 227 | ) 228 | if (requestCode == REQ_CODE_STORAGE_PERMMISION) { 229 | var denied = false 230 | for (i in permissions.indices) 231 | if (grantResults[i] == PackageManager.PERMISSION_DENIED) { 232 | denied = true 233 | break 234 | } 235 | 236 | if (denied) 237 | Toast.makeText( 238 | this@MainActivity, 239 | getString(R.string.permission_error), 240 | Toast.LENGTH_SHORT 241 | ).show() 242 | launchSelectAudioActivity() 243 | 244 | } else if (requestCode == REQ_CODE_AUDIO_PERMMISION) { 245 | var denied = false 246 | for (i in permissions.indices) 247 | if (grantResults[i] == PackageManager.PERMISSION_DENIED) { 248 | denied = true 249 | break 250 | } 251 | 252 | if (denied) 253 | Toast.makeText( 254 | this@MainActivity, 255 | getString(R.string.permission_error), 256 | Toast.LENGTH_SHORT 257 | ).show() 258 | startPlayingAudio(R.raw.sample3) 259 | } else { 260 | super.onRequestPermissionsResult(requestCode, permissions, grantResults) 261 | } 262 | 263 | } 264 | 265 | private fun launchSelectAudioActivity() { 266 | val intent = Intent(this@MainActivity, SelectAudioActivity::class.java) 267 | startActivityForResult(intent, REQ_CODE_PICK_SOUND_FILE) 268 | } 269 | 270 | private fun getDummyWaveSample(): IntArray { 271 | val data = IntArray(50) 272 | for (i in data.indices) 273 | data[i] = Random().nextInt(data.size) 274 | return data 275 | } 276 | 277 | private fun startPlayingAudio(resId: Int) { 278 | mAudioPlayer.play(this, resId) { 279 | danceView.release() 280 | } 281 | val audioSessionId: Int = mAudioPlayer.audioSessionId 282 | if (audioSessionId != -1) { 283 | danceView.setAudioSessionId(audioSessionId) 284 | } 285 | } 286 | 287 | } 288 | -------------------------------------------------------------------------------- /app/src/main/java/com/masoudss/activity/SelectAudioActivity.kt: -------------------------------------------------------------------------------- 1 | package com.masoudss.activity 2 | 3 | import android.app.Activity 4 | import android.content.Intent 5 | import android.database.Cursor 6 | import android.os.Bundle 7 | import androidx.appcompat.app.AppCompatActivity 8 | 9 | import kotlinx.android.synthetic.main.activity_select_audio.* 10 | import android.provider.MediaStore 11 | import androidx.recyclerview.widget.LinearLayoutManager 12 | import com.masoudss.model.AudioModel 13 | import com.masoudss.R 14 | import com.masoudss.adapter.AudioAdapter 15 | import org.jetbrains.anko.doAsync 16 | import org.jetbrains.anko.uiThread 17 | 18 | 19 | class SelectAudioActivity : AppCompatActivity() { 20 | 21 | private val audioList = ArrayList() 22 | private val projection = arrayOf( 23 | MediaStore.Audio.Media._ID, 24 | MediaStore.Audio.Media.ARTIST, 25 | MediaStore.Audio.Media.TITLE, 26 | MediaStore.Audio.Media.DATA) 27 | 28 | override fun onCreate(savedInstanceState: Bundle?) { 29 | super.onCreate(savedInstanceState) 30 | setContentView(R.layout.activity_select_audio) 31 | initViews() 32 | loadAudioFiles() 33 | } 34 | 35 | private fun initViews(){ 36 | audioRecyclerView.layoutManager = LinearLayoutManager(this) 37 | audioRecyclerView.adapter = AudioAdapter(this@SelectAudioActivity, audioList) 38 | } 39 | 40 | private fun loadAudioFiles() { 41 | 42 | doAsync { 43 | 44 | var cursor: Cursor? = null 45 | try { 46 | cursor = contentResolver.query( 47 | MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, 48 | projection, 49 | null, 50 | null, 51 | MediaStore.Audio.Media.DATE_ADDED + " DESC") 52 | 53 | while (cursor!!.moveToNext()) { 54 | val audioModel = AudioModel() 55 | audioModel.artist = cursor.getString(1) 56 | audioModel.title = cursor.getString(2) 57 | audioModel.path = cursor.getString(3) 58 | audioList.add(audioModel) 59 | } 60 | } catch (e: Exception) { 61 | e.printStackTrace() 62 | } finally { 63 | cursor?.close() 64 | } 65 | 66 | uiThread { 67 | audioRecyclerView.adapter?.notifyDataSetChanged() 68 | } 69 | } 70 | } 71 | 72 | fun onSelectAudio(audioModel: AudioModel){ 73 | val intent = Intent() 74 | intent.putExtra("path",audioModel.path) 75 | setResult(Activity.RESULT_OK,intent) 76 | finish() 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/java/com/masoudss/adapter/AudioAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.masoudss.adapter 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import androidx.recyclerview.widget.RecyclerView 7 | import com.masoudss.R 8 | import com.masoudss.activity.SelectAudioActivity 9 | import com.masoudss.model.AudioModel 10 | import kotlinx.android.synthetic.main.item_audio.view.* 11 | 12 | class AudioAdapter(private val activity: SelectAudioActivity,private val audioList : ArrayList) : 13 | RecyclerView.Adapter() { 14 | 15 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AudioViewHolder { 16 | return AudioViewHolder(LayoutInflater.from(activity).inflate(R.layout.item_audio,parent,false)) 17 | } 18 | 19 | override fun getItemCount(): Int { 20 | return audioList.size 21 | } 22 | 23 | override fun onBindViewHolder(holder: AudioViewHolder, position: Int) { 24 | holder.itemView.title.text = "${audioList[position].title}\n${audioList[position].artist}".trim() 25 | } 26 | 27 | inner class AudioViewHolder(view: View) : RecyclerView.ViewHolder(view) { 28 | 29 | init { 30 | view.setOnClickListener { 31 | activity.onSelectAudio(audioList[adapterPosition]) 32 | } 33 | } 34 | } 35 | 36 | 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/masoudss/model/AudioModel.kt: -------------------------------------------------------------------------------- 1 | package com.masoudss.model 2 | 3 | class AudioModel { 4 | 5 | var title = "" 6 | 7 | var artist = "" 8 | 9 | var path = "" 10 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_audio.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_github.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_import.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 18 | 19 | 20 | 30 | 31 | 41 | 42 | 43 | 44 | 45 | 46 | 50 | 51 | 55 | 56 | 73 | 74 | 80 | 81 | 87 | 88 | 96 | 97 | 98 | 104 | 105 | 112 | 113 | 119 | 120 | 127 | 128 | 135 | 136 | 144 | 145 | 152 | 153 | 162 | 163 | 168 | 169 | 174 | 175 | 180 | 181 | 186 | 187 | 193 | 194 | 201 | 202 | 208 | 209 | 210 | 211 | 212 | 213 | 218 | 219 | 224 | 225 | 230 | 231 | 237 | 238 | 244 | 245 | 252 | 253 | 254 | 255 | 256 | 257 | 263 | 264 | 269 | 270 | 275 | 276 | 282 | 283 | 290 | 291 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_select_audio.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_audio.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hankinghu/AudioVisulizer/e2fedd91ef26824475ec1be228d9f348960164e2/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hankinghu/AudioVisulizer/e2fedd91ef26824475ec1be228d9f348960164e2/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hankinghu/AudioVisulizer/e2fedd91ef26824475ec1be228d9f348960164e2/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hankinghu/AudioVisulizer/e2fedd91ef26824475ec1be228d9f348960164e2/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hankinghu/AudioVisulizer/e2fedd91ef26824475ec1be228d9f348960164e2/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #108BF3 4 | #108BF3 5 | #108BF3 6 | 7 | #108BF3 8 | #FE444C 9 | #FDC01F 10 | #0BCF73 11 | #f93376 12 | #ffffff 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 16dp 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | HankingMusic 3 | Permission Denied :( 4 | Please wait... 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 14 |