接入Tradplus(源代码)

This commit is contained in:
yuqian 2025-12-29 10:27:29 +08:00
commit bb0cd7fcf5
153 changed files with 8137 additions and 0 deletions

15
.gitignore vendored Normal file
View File

@ -0,0 +1,15 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties

3
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

6
.idea/AndroidProjectSystem.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AndroidProjectSystem">
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
</component>
</project>

6
.idea/compiler.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="21" />
</component>
</project>

10
.idea/deploymentTargetSelector.xml generated Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
</selectionStates>
</component>
</project>

19
.idea/gradle.xml generated Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

10
.idea/migrations.xml generated Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>

9
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,9 @@
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

17
.idea/runConfigurations.xml generated Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set>
</option>
</component>
</project>

4
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings" defaultProject="true" />
</project>

6
KeyStore.properties Normal file
View File

@ -0,0 +1,6 @@
app_name=SoundGags
package_name=com.soundapp.soundgags
keystoreFile=soundgags.jks
key_alias=soundgagskey0
key_store_password=soundgags
key_password=soundgags

1
app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

71
app/build.gradle.kts Normal file
View File

@ -0,0 +1,71 @@
import java.text.SimpleDateFormat
import java.util.Date
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
id("kotlin-kapt")
id("kotlin-parcelize")
}
android {
namespace = "com.soundapp.soundgags"
compileSdk {
version = release(36)
}
val timeStamp=SimpleDateFormat("MM_dd_HH_mm").format(Date())
defaultConfig {
applicationId = "com.soundapp.soundgags"
minSdk = 26
targetSdk = 36
versionCode = 3
versionName = "3.0"
setProperty(
"archivesBaseName",
"Sound Gags_V" + versionName + "_${versionCode}$timeStamp"
)
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
buildFeatures {
viewBinding = true
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
implementation("com.github.bumptech.glide:glide:4.16.0")
// Room components
implementation("androidx.room:room-runtime:2.7.2")
kapt("androidx.room:room-compiler:2.7.2")
implementation("androidx.room:room-ktx:2.7.2")
// 其他相关依赖
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.2")
implementation("androidx.activity:activity-ktx:1.10.1")
implementation("com.google.android.flexbox:flexbox:3.0.0")
}

21
app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,37 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.soundapp.soundgags",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 3,
"versionName": "3.0",
"outputFile": "Sound Gags_V3.0_312_18_10_06-release.apk"
}
],
"elementType": "File",
"baselineProfiles": [
{
"minApi": 28,
"maxApi": 30,
"baselineProfiles": [
"baselineProfiles/1/Sound Gags_V3.0_312_18_10_06-release.dm"
]
},
{
"minApi": 31,
"maxApi": 2147483647,
"baselineProfiles": [
"baselineProfiles/0/Sound Gags_V3.0_312_18_10_06-release.dm"
]
}
],
"minSdkVersionForDexing": 26
}

BIN
app/soundgags Normal file

Binary file not shown.

View File

@ -0,0 +1,24 @@
package com.soundapp.soundgags
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.soundapp.soundgags", appContext.packageName)
}
}

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:name=".ReloadApplication"
android:theme="@style/Theme.SoundGags">
<activity
android:name=".ui.NowPlayingActivity"
android:exported="false" />
<activity
android:name=".ui.OpenSetActivity"
android:exported="false" />
<activity
android:name=".ui.CompleteListActivity"
android:exported="false" />
<activity
android:name=".ui.LocalMusicActivity"
android:exported="false" />
<activity
android:name=".ui.VoiceRecorderActivity"
android:exported="false" />
<activity
android:name=".ui.JokeViewerActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,53 @@
package com.soundapp.soundgags
import android.app.Application
import android.graphics.Typeface
import android.util.Log
import com.soundapp.soundgags.datame.AppDataBase
import com.soundapp.soundgags.datame.Familiar
import com.soundapp.soundgags.setting.SetUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.IOException
import java.util.Collections;
class ReloadApplication : Application() {
companion object {
lateinit var app: ReloadApplication
lateinit var typeface: Typeface
var familiars: List<Familiar>? = null
}
override fun onCreate() {
super.onCreate()
app = this
CoroutineScope(Dispatchers.IO).launch {
initializeDatabase()
}
}
private fun initializeDatabase() {
val database = AppDataBase.getDatabase(this)
val infoDao = database.infoDao()
try {
typeface = Typeface.createFromAsset(assets, "lato_regular.ttf")
val open = assets.open("prank.json")
val string = SetUtil.getString(open)
if (string.isNotEmpty()) {
familiars = SetUtil.getFamiliar(string)
Collections.shuffle(familiars)
familiars?.forEach { category ->
Log.d("Database", "处理分类: ${category.name}")
// 使用Room的suspend函数或确保在IO线程
infoDao.insertAll(category.infolist)
}
Log.d("Database", "数据库初始化完成")
}
} catch (e: IOException) {
Log.e("Database", "初始化失败", e)
}
}
}

View File

@ -0,0 +1,23 @@
package com.soundapp.soundgags.datame
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(entities = [Reflection::class], version = 1, exportSchema = false)
abstract class AppDataBase:RoomDatabase() {
abstract fun infoDao():ReflectionDao
companion object{
@Volatile
private var INSTANCE:AppDataBase?=null
fun getDatabase(context:Context):AppDataBase{
return INSTANCE?: synchronized(this){
INSTANCE?:createDatabase(context).also { INSTANCE=it }
}
}
private fun createDatabase(context: Context):AppDataBase{
return Room.databaseBuilder(context.applicationContext,AppDataBase::class.java,"sound_database").fallbackToDestructiveMigration().build()
}
}
}

View File

@ -0,0 +1,185 @@
package com.soundapp.soundgags.datame
import android.app.Application
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class MainViewModel(application: Application) : AndroidViewModel(application){
private val repository: ReflectionRepository by lazy {
val dao = AppDataBase.getDatabase(application).infoDao()
ReflectionRepository(dao)
}
// 使用LiveData暴露状态
private val _likeItems = MutableLiveData<List<Reflection>>()
var likeItems: LiveData<List<Reflection>> = _likeItems
private val _customItems = MutableLiveData<List<Reflection>>()
val customItems: LiveData<List<Reflection>> = _customItems
private val _randomItems = MutableLiveData<List<Reflection>>()
val randomItems: LiveData<List<Reflection>> = _randomItems
private val _nonCustomCount = MutableLiveData<Int>()
val nonCustomCount: LiveData<Int> = _nonCustomCount
// 加载状态
private val _isLoading = MutableLiveData<Boolean>()
val isLoading: LiveData<Boolean> = _isLoading
// 错误处理
private val _errorMessage = MutableLiveData<String>()
val errorMessage: LiveData<String> = _errorMessage
init {
loadInitialData()
}
// 初始化加载数据
private fun loadInitialData() {
loadLikeItems()
loadCustomItems()
// loadRandomItems()
}
fun debugDatabase() {
viewModelScope.launch(Dispatchers.IO) {
try {
val reflection = repository.getAll()
Log.d("DB_DEBUG", "总记录数: ${reflection.size}")
reflection.take(5).forEach { infoitem ->
Log.d("DB_DEBUG", "Paper[${infoitem.id}]: " +
"cat=${infoitem.name}, " +
"imId=${infoitem.audioUrl}, ")
}
} catch (e: Exception) {
Log.e("DB_DEBUG", "查询失败", e)
}
}
}
// 加载随机项目
fun loadRandomItems() {
_isLoading.value = true
viewModelScope.launch(Dispatchers.IO) {
try {
val items = repository.getMoreEfficient()
_randomItems.postValue(items)
} catch (e: Exception) {
_errorMessage.postValue("Failed to load random items: ${e.message}")
} finally {
_isLoading.postValue(false)
}
}
}
// 显式加载点赞项(可用于手动刷新)
fun loadLikeItems() {
_isLoading.postValue(true)
viewModelScope.launch(Dispatchers.IO) {
try {
val likes = repository.getAllLike()
_likeItems.postValue(likes)
Log.d("DB_DEBUG", "点赞记录数: ${likes.size}")
} catch (e: Exception) {
_errorMessage.postValue("Failed to load like items: ${e.message}")
} finally {
_isLoading.postValue(false)
}
}
}
// 显式加载上传项(可用于手动刷新)
fun loadCustomItems() {
_isLoading.postValue(true)
viewModelScope.launch(Dispatchers.IO) {
try {
val customs = repository.getAllCustomization()
_customItems.postValue(customs)
} catch (e: Exception) {
_errorMessage.postValue("Failed to load customs items: ${e.message}")
} finally {
_isLoading.postValue(false)
}
}
}
// 更新点赞状态
fun updateLikeStatus(name: String, isLike: Boolean) {
viewModelScope.launch(Dispatchers.IO) {
try {
repository.updateLikeStatus(name, isLike)
// 根据需求刷新相应的列表
if (isLike) {
loadLikeItems() // 如果是点赞,刷新点赞列表
} else {
loadLikeItems() // 如果是取消点赞,也刷新点赞列表
// 或者如果当前在某个特定页面,也可以刷新该页面的列表
}
} catch (e: Exception) {
_errorMessage.postValue("Failed to update like status: ${e.message}")
}
}
}
// 检查是否点赞
fun checkLikeStatus(name: String, callback: (Boolean) -> Unit) {
viewModelScope.launch(Dispatchers.IO) {
try {
val isLiked = repository.checkLikeExists(name)
callback(isLiked)
} catch (e: Exception) {
_errorMessage.postValue("Failed to check like status: ${e.message}")
callback(false)
}
}
}
// 检查是否已存在
fun checkSaveStatus(name: String, callback: (Boolean) -> Unit) {
viewModelScope.launch(Dispatchers.IO) {
try {
Log.d("DB_DEBUG", "检查是否已存在: $name")
val isSaved = repository.checkNameExists(name)
Log.d("DB_DEBUG", "是否已存在: $isSaved")
callback(isSaved)
} catch (e: Exception) {
_errorMessage.postValue("Failed to check save status: ${e.message}")
callback(false)
}
}
}
// 插入自定义
fun insertItem(reflection: Reflection) {
viewModelScope.launch(Dispatchers.IO) {
try {
val itemToInsert = reflection.copy(isCustomization = true)
repository.insert(itemToInsert)
loadCustomItems()
// 依赖数据库观察者自动更新UI
} catch (e: Exception) {
_errorMessage.postValue("Failed to insert item: ${e.message}")
}
}
}
// 删除自定义
fun deleteItem(reflection: Reflection) {
viewModelScope.launch(Dispatchers.IO) {
try {
repository.delete(reflection)
loadCustomItems()
loadLikeItems()
// 依赖数据库观察者自动更新UI
} catch (e: Exception) {
_errorMessage.postValue("Failed to delete item: ${e.message}")
}
}
}
// 清除错误消息
fun clearErrorMessage() {
_errorMessage.value = ""
}
}

View File

@ -0,0 +1,52 @@
package com.soundapp.soundgags.datame
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
@Dao
interface ReflectionDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(reflection: Reflection): Long
@Delete
suspend fun delete(reflection: Reflection): Int
@Query("SELECT * FROM reflection")
suspend fun selectAll(): List<Reflection>
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insertAll(infos: List<Reflection>): List<Long>
//使用索引优化查询
@Query("SELECT * FROM reflection WHERE isLike = 1 ORDER BY id ASC")
fun getAllLike(): List<Reflection>
@Query("SELECT * FROM reflection WHERE isCustomization = 1 ORDER BY id ASC")
fun getAllCustomization(): List<Reflection>
// 高效的随机查询使用SQLite的随机函数
@Query("""
SELECT * FROM reflection
WHERE isCustomization = 0
ORDER BY RANDOM()
LIMIT 11
""")
suspend fun getMoreEfficient(): List<Reflection>
// 使用EXISTS进行存在性检查更高效
@Query("SELECT EXISTS(SELECT 1 FROM reflection WHERE name = :name AND isLike = 1)")
suspend fun checkLikeExists(name: String): Boolean
@Query("SELECT EXISTS(SELECT 1 FROM reflection WHERE name = :name)")
suspend fun checkNameExists(name: String): Boolean
@Query("UPDATE reflection SET isLike = :isLike WHERE name = :name")
suspend fun updateLikeStatus(name: String, isLike: Boolean): Int
@Query("UPDATE reflection SET isLike = 0 WHERE name = :name")
suspend fun removeLike(name: String)
}

View File

@ -0,0 +1,47 @@
package com.soundapp.soundgags.datame
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class ReflectionRepository(private val infoDao: ReflectionDao){
// 获取所有点赞项
fun getAllLike():List<Reflection> = infoDao.getAllLike()
suspend fun getAll(): List<Reflection> = infoDao.selectAll()
// 获取所有自定义项
fun getAllCustomization(): List<Reflection> = infoDao.getAllCustomization()
// 获取高效随机数据
suspend fun getMoreEfficient(): List<Reflection> = withContext(Dispatchers.IO) {
infoDao.getMoreEfficient()
}
// 检查点赞存在性
suspend fun checkLikeExists(name: String): Boolean = withContext(Dispatchers.IO) {
infoDao.checkLikeExists(name)
}
// 检查名称存在性
suspend fun checkNameExists(name: String): Boolean = withContext(Dispatchers.IO) {
infoDao.checkNameExists(name)
}
// 添加点赞
suspend fun updateLikeStatus(name: String, isLike: Boolean) = withContext(Dispatchers.IO) {
infoDao.updateLikeStatus(name, isLike)
}
// 移除点赞
suspend fun removeLike(name: String) = withContext(Dispatchers.IO) {
infoDao.removeLike(name)
}
// 插入单个项目
suspend fun insert(reflection: Reflection): Long = withContext(Dispatchers.IO) {
infoDao.insert(reflection)
}
// 删除项目
suspend fun delete(reflection: Reflection): Int = withContext(Dispatchers.IO) {
infoDao.delete(reflection)
}
}

View File

@ -0,0 +1,28 @@
package com.soundapp.soundgags.datame
import android.os.Parcelable
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import kotlinx.parcelize.Parcelize
@Parcelize
@Entity(tableName = "familiar")
data class Familiar(
@PrimaryKey(autoGenerate = true) var id: Int = 0,
@ColumnInfo(name = "name") var name:String="",
@ColumnInfo(name = "convert") var convert:String="",
@ColumnInfo(name="infolist") var infolist:List<Reflection> = arrayListOf()
): Parcelable
@Parcelize
@Entity(tableName = "reflection",indices = [Index(value = ["name"], unique = true)])
data class Reflection(
@PrimaryKey(autoGenerate = true) var id: Int = 0,
@ColumnInfo(name = "name") var name: String = "",
@ColumnInfo(name = "convert") var convert: String = "",
@ColumnInfo(name = "audioUrl") var audioUrl: String = "",
@ColumnInfo(name = "isLike") var isLike: Boolean = false,
@ColumnInfo(name = "isCustomization") var isCustomization: Boolean = false,
@ColumnInfo(name = "colorIndex") var colorIndex: Int = 0
):Parcelable

View File

@ -0,0 +1,85 @@
package com.soundapp.soundgags.setting
import android.app.Activity
import android.graphics.Color
import android.util.Log
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.widget.PopupWindow
import com.soundapp.soundgags.databinding.FragClockChooseBinding
class ClockChooseFragment(context: Activity, private val mListener: TimerThisListener) : View.OnClickListener {
private val binding: FragClockChooseBinding = FragClockChooseBinding.inflate(context.layoutInflater)
private val popupWindow: PopupWindow = PopupWindow(
binding.root,
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
init {
popupWindow.isOutsideTouchable = true
popupWindow.isFocusable = true
binding.tvOff.isSelected = true
binding.tvOff.setOnClickListener(this)
binding.tv15s.setOnClickListener(this)
binding.tv30s.setOnClickListener(this)
binding.tv1m.setOnClickListener(this)
binding.tv5m.setOnClickListener(this)
}
fun showTop(view: View) {
val location = IntArray(2)
view.getLocationOnScreen(location)
val x = location[0]
val y = location[1]
val popupHeight = popupWindow.height
val height = view.height
val width = view.width
Log.d("-------------", "--------x=$x---y=$y----popupHeight=$popupHeight")
popupWindow.showAsDropDown(view, -50, 0, Gravity.NO_GRAVITY)
}
private fun setSelected(tv: SetTxtView) {
val childCount = binding.layoutTime.childCount
for (i in 0 until childCount) {
val child = binding.layoutTime.getChildAt(i)
if (child is SetTxtView) {
if (child == tv) {
child.isSelected = true
child.setTextColor(Color.parseColor("#202859"))
} else {
child.isSelected = false
child.setTextColor(Color.parseColor("#202859"))
}
println("TextView: ${child.text}")
}
}
}
fun hide() {
popupWindow.dismiss()
}
fun setOffSelected() {
setSelected(binding.tvOff)
}
override fun onClick(v: View) {
if (v is SetTxtView) {
val string = v.text.toString()
when (string) {
"off" -> mListener.onClickTimerListener(-1)
"15 sec" -> mListener.onClickTimerListener(15 * 1000L)
"30 sec" -> mListener.onClickTimerListener(30 * 1000L)
"1 min" -> mListener.onClickTimerListener(60 * 1000L)
"5 min" -> mListener.onClickTimerListener(5 * 60 * 1000L)
}
setSelected(v)
}
}
}

View File

@ -0,0 +1,81 @@
package com.soundapp.soundgags.setting
import android.content.Context
import android.content.Intent
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.soundapp.soundgags.R
import com.soundapp.soundgags.datame.Reflection
import com.soundapp.soundgags.ui.NowPlayingActivity
import com.bumptech.glide.Glide
import com.soundapp.soundgags.databinding.ItemFavSolidBinding
class FavSolidAdapter: RecyclerView.Adapter<FavSolidAdapter.SelfViewHolder>() {
private lateinit var context:Context
private var infoList :List<Reflection> = arrayListOf()
private var type:Int=0
private lateinit var listener:LikeUpClickListener
class SelfViewHolder(val binding: ItemFavSolidBinding): RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SelfViewHolder {
context=parent.context
val binding=ItemFavSolidBinding.inflate(LayoutInflater.from(context),parent,false)
return SelfViewHolder(binding)
}
override fun getItemCount(): Int {
return infoList.size
}
override fun onBindViewHolder(holder: SelfViewHolder, position: Int) {
if (position == itemCount - 1) {
holder.binding.vBottom.visibility = View.VISIBLE
} else {
holder.binding.vBottom.visibility = View.GONE
}
val myInfo = infoList[position]
val covert: String = myInfo.convert
Log.d("covert",covert+"typre$type")
holder.binding.imgLike.isSelected = myInfo.isLike
if(type==0){
Glide.with(context).load(covert).placeholder(R.drawable.pic_only_diy).error(R.drawable.pic_only_diy).into(holder.binding.ivImage)
holder.binding.imgLike.setImageDrawable(context.getDrawable(R.drawable.me_add_love_selector))
holder.binding.imgDel.visibility=View.GONE
holder.binding.tint.visibility=View.VISIBLE
if(myInfo.isCustomization){
holder.binding.tint.text="resource:upload"
}else{
holder.binding.tint.text="resource:original"
}
}else{
Glide.with(context).load(R.drawable.pic_only_diy).placeholder(R.drawable.pic_only_diy).error(R.drawable.pic_only_diy).into(holder.binding.ivImage)
holder.binding.imgDel.setImageDrawable(context.getDrawable(R.drawable.del_red_icon))
holder.binding.tint.visibility=View.GONE
holder.binding.imgDel.visibility=View.VISIBLE
}
holder.binding.tvTitle.text = myInfo.name
holder.binding.imgLike.setOnClickListener {
val isSelected= holder.binding.imgLike.isSelected
holder.binding.imgLike.isSelected=!isSelected
this.listener.onMoreClick(myInfo,isSelected)
}
holder.binding.imgDel.setOnClickListener {
this.listener.onCustomizationClick(myInfo)
}
holder.binding.item.setOnClickListener {
val intent = Intent(context, NowPlayingActivity::class.java)
intent.putExtra(NowPlayingActivity.KEY_LIST_DATA, myInfo)
context.startActivity(intent)
}
}
fun setList(list: List<Reflection>,type:Int){
this.type=type
this.infoList=list
}
fun setListener(listener: LikeUpClickListener){
this.listener=listener
}
}

View File

@ -0,0 +1,48 @@
package com.soundapp.soundgags.setting
import android.content.Context
import android.content.Intent
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.soundapp.soundgags.datame.Familiar
import com.soundapp.soundgags.databinding.ItemHomeTransBinding
import com.soundapp.soundgags.ui.OpenSetActivity
import com.bumptech.glide.Glide
import java.util.Random
class HomeTransAdapter: RecyclerView.Adapter<HomeTransAdapter.DoubleViewHolder>() {
private lateinit var context:Context
private var beanList :List<Familiar> = arrayListOf()
class DoubleViewHolder(val binding: ItemHomeTransBinding): RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DoubleViewHolder {
context=parent.context
val binding=ItemHomeTransBinding.inflate(LayoutInflater.from(context),parent,false)
return DoubleViewHolder(binding)
}
override fun getItemCount(): Int {
return beanList.size
}
override fun onBindViewHolder(holder: DoubleViewHolder, position: Int) {
val minHeight = 200
val maxHeight = 280
val random = Random()
val myBean = beanList[position]
val covert: String = myBean.convert
Log.d("covert",covert)
Glide.with(context).load(covert).into(holder.binding.image)
holder.binding.title.text = myBean.name
holder.binding.imgRelay.setOnClickListener {
val intent = Intent(context, OpenSetActivity::class.java)
intent.putExtra(OpenSetActivity.KEY_LIST_DATA, myBean)
context.startActivity(intent)
}
}
fun setList(list: List<Familiar>){
this.beanList=list
}
}

View File

@ -0,0 +1,10 @@
package com.soundapp.soundgags.setting
import com.soundapp.soundgags.datame.Reflection
interface LikeUpClickListener {
fun onMoreClick(reflection: Reflection,add: Boolean=false)//false:删除true:新增
fun onCustomizationClick(reflection: Reflection){
}
}

View File

@ -0,0 +1,66 @@
package com.soundapp.soundgags.setting
import android.content.Context
import android.media.MediaRecorder
import java.io.File
import java.io.IOException
class MediaPlayerManager(context: Context) {
private var mediaRecorder: MediaRecorder? = null
private val tempFileName: String
private val fileName: String? = null
private var isRecording = false
init {
val dir = context.cacheDir
val file = File(dir, "temp_audio.mp3")
tempFileName = file.absolutePath
}
fun startRecording() {
if (isRecording) return
mediaRecorder = MediaRecorder()
mediaRecorder!!.setAudioSource(MediaRecorder.AudioSource.MIC)
mediaRecorder!!.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
mediaRecorder!!.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
mediaRecorder!!.setOutputFile(tempFileName)
try {
mediaRecorder!!.prepare()
mediaRecorder!!.start()
isRecording = true
} catch (e: IOException) {
e.printStackTrace()
}
}
fun stopRecording() {
if (!isRecording || mediaRecorder == null) return
try {
// 只有在录制状态下才能调用stop
mediaRecorder?.stop()
} catch (e: IllegalStateException) {
e.printStackTrace()
} catch (e: RuntimeException) {
e.printStackTrace()
} finally {
releaseMediaRecorder()
isRecording = false
}
}
private fun releaseMediaRecorder() {
try {
mediaRecorder?.release()
} catch (e: Exception) {
e.printStackTrace()
} finally {
mediaRecorder = null
}
}
fun getTempFileName(): String {
return tempFileName
}
}

View File

@ -0,0 +1,32 @@
package com.soundapp.soundgags.setting
import android.content.Context
import android.graphics.Typeface
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView
import com.soundapp.soundgags.R
import com.soundapp.soundgags.ReloadApplication
class SetTxtView(context: Context, attrs: AttributeSet?) :
AppCompatTextView(context, attrs) {
init {
initAttrs(context, attrs)
}
private fun initAttrs(context: Context, attrs: AttributeSet?) {
this.setTypeface(ReloadApplication.typeface)
val typedArray=context.obtainStyledAttributes(attrs,R.styleable.SetTxtView)
val isBold=typedArray.getBoolean(R.styleable.SetTxtView_isBold,false)
setBold(isBold);
}
fun setBold(isBold:Boolean){
if(isBold){
this.setTypeface(ReloadApplication.typeface,Typeface.BOLD)
}else{
this.setTypeface(ReloadApplication.typeface,Typeface.NORMAL)
}
}
}

View File

@ -0,0 +1,108 @@
package com.soundapp.soundgags.setting
import android.content.Context
import android.media.AudioManager
import android.media.MediaMetadataRetriever
import android.net.Uri
import android.widget.SeekBar
import androidx.appcompat.app.AppCompatActivity
import com.soundapp.soundgags.datame.Reflection
import com.soundapp.soundgags.datame.Familiar
import org.json.JSONArray
import org.json.JSONException
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
import java.io.StringWriter
object SetUtil {
fun getString(input: InputStream): String {
try {
val charArray = CharArray(input.available())
var count: Int
val stringWriter = StringWriter()
val inputStreamReader = InputStreamReader(input)
val bufferedReader = BufferedReader(inputStreamReader)
while ((bufferedReader.read(charArray).also { count = it }) != -1) {
stringWriter.write(charArray, 0, count)
}
return stringWriter.toString()
} catch (exception: IOException) {
return ""
}
}
fun getFamiliar(str: String?): List<Familiar>? {
try {
val data: MutableList<Familiar> = ArrayList()
val jsonArray = JSONArray(str)
for (i in 0..<jsonArray.length()) {
val it = jsonArray.getJSONObject(i)
val listArray = it.getJSONArray("list")
val list: MutableList<Reflection> = ArrayList()
for (k in 0..<listArray.length()) {
val listBean = listArray.getJSONObject(k)
val reflection = Reflection()
reflection.audioUrl=(listBean.getString("mp3Url"))
reflection.convert=(listBean.getString("preUrl"))
reflection.name=(listBean.getString("title"))
list.add(reflection)
}
val newBean = Familiar()
newBean.convert=(it.getString("categoryUrl"))
newBean.infolist=(list)
newBean.name=(it.getString("categoryName"))
data.add(newBean)
}
return data
} catch (jo: JSONException) {
return null
}
}
fun setVolume(context: Context, seekBar: SeekBar) {
val audioManager = context.getSystemService(AppCompatActivity.AUDIO_SERVICE) as AudioManager
val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
seekBar.max = maxVolume
seekBar.progress = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0)
}
override fun onStartTrackingTouch(seekBar: SeekBar) {
// 可以留空或添加相应逻辑
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
// 可以留空或添加相应逻辑
}
})
}
fun getAudioDuration(context: Context, uri: Uri?): Long {
val retriever = MediaMetadataRetriever()
var duration: Long = 0
try {
val contentResolver = context.contentResolver
retriever.setDataSource(context, uri)
val time = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
duration = time!!.toLong()
} catch (e: Exception) {
e.printStackTrace()
} finally {
try {
retriever.release()
} catch (e: IOException) {
throw RuntimeException(e)
}
}
return duration
}
fun dpToPx(dp: Int, context: Context): Int {
return (dp * context.resources.displayMetrics.density).toInt()
}
}

View File

@ -0,0 +1,5 @@
package com.soundapp.soundgags.setting
interface TimerThisListener {
fun onClickTimerListener(time: Long)
}

View File

@ -0,0 +1,24 @@
package com.soundapp.soundgags.setting
import android.graphics.Color
import android.view.View
import android.view.Window
object TopBarUtils {
// 设置状态栏透明
fun setStatusBar(window: Window) {
window.statusBarColor = Color.TRANSPARENT
window.decorView.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
}
// 设置状态栏黑色字体
fun setLightStatusBar(window: Window, isLight: Boolean) {
var systemUiVisibility = window.decorView.systemUiVisibility
systemUiVisibility = if (isLight) {
systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
} else {
systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
}
window.decorView.systemUiVisibility = systemUiVisibility
}
}

View File

@ -0,0 +1,91 @@
package com.soundapp.soundgags.setting
import android.content.Context
import android.content.Intent
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.soundapp.soundgags.datame.Reflection
import com.soundapp.soundgags.ui.NowPlayingActivity
import com.bumptech.glide.Glide
import com.soundapp.soundgags.databinding.ItemShowStyle1Binding
import com.soundapp.soundgags.databinding.ItemShowStyle2Binding
class TypeAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private lateinit var context:Context
private var type:Int=0
private var infoList :List<Reflection> = arrayListOf()
private lateinit var listener: LikeUpClickListener
companion object {
private const val VIEW_TYPE_1 = 1
private const val VIEW_TYPE_2 = 2
}
// 第一个 ViewHolder
class ViewHolder1(val binding: ItemShowStyle1Binding): RecyclerView.ViewHolder(binding.root)
// 第二个 ViewHolder
class ViewHolder2(val binding: ItemShowStyle2Binding): RecyclerView.ViewHolder(binding.root)
override fun getItemViewType(position: Int): Int {
return if (type != 0) VIEW_TYPE_2 else VIEW_TYPE_1
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
context = parent.context
return when (viewType) {
VIEW_TYPE_1 -> {
val binding = ItemShowStyle1Binding.inflate(LayoutInflater.from(context), parent, false)
ViewHolder1(binding)
}
VIEW_TYPE_2 -> {
val binding = ItemShowStyle2Binding.inflate(LayoutInflater.from(context), parent, false)
ViewHolder2(binding)
}
else -> throw IllegalArgumentException("Invalid view type")
}
}
override fun getItemCount(): Int {
return infoList.size
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val reflection = infoList[position]
val covert: String = reflection.convert
Log.d("covert",covert)
when (holder) {
is ViewHolder1 -> {
// 使用 ItemShowDetail1Binding 的布局
Glide.with(context).load(covert).into(holder.binding.image)
holder.binding.title.text = reflection.name
holder.binding.imgCard.setOnClickListener {
val intent = Intent(context, NowPlayingActivity::class.java)
intent.putExtra(NowPlayingActivity.KEY_LIST_DATA, reflection)
context.startActivity(intent)
}
}
is ViewHolder2 -> {
// 使用 ItemShowDetail2Binding 的布局
Glide.with(context).load(covert).into(holder.binding.image)
// holder.binding.title.text = reflection.name
holder.binding.imgCard.setOnClickListener {
listener.onMoreClick(reflection)
}
}
}
}
fun setList(list: List<Reflection>, setType: Int = 0) {
this.infoList = list
this.type = setType
notifyDataSetChanged()
}
fun setListener(listener: LikeUpClickListener) {
this.listener = listener
}
}

View File

@ -0,0 +1,119 @@
package com.soundapp.soundgags.ui
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.soundapp.soundgags.databinding.ActivityCompleteListBinding
import com.soundapp.soundgags.datame.MainViewModel
import com.soundapp.soundgags.setting.TopBarUtils
class CompleteListActivity : AppCompatActivity() {
private lateinit var binding: ActivityCompleteListBinding
// private val mainViewModel:MainViewModel by viewModels()
private var isFragmentTransitionRunning=false
private var lastClickTime=0L
private val CLICK_THROTTLE=300L
private lateinit var viewModel: MainViewModel
private lateinit var showFragment: ExploreStyleFragment
private lateinit var collFragment : SavedItemsFragment
private lateinit var recordFragment: SoundFragment
private var currentFragment: Fragment?=null
private sealed class Tab {
data object Show:Tab()
data object Collect:Tab()
data object Record:Tab()
}
private var currentTab:Tab=Tab.Record
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
TopBarUtils.setStatusBar(this.window)
TopBarUtils.setLightStatusBar(this.window, true)
binding= ActivityCompleteListBinding.inflate(layoutInflater)
viewModel=ViewModelProvider(this)[MainViewModel::class.java]
showFragment=ExploreStyleFragment()
collFragment=SavedItemsFragment(viewModel)
recordFragment=SoundFragment(viewModel)
setContentView(binding.root)
initViews()
}
private fun initViews() {
switchFragment(Tab.Show)
binding.llCategory.setOnClickListener { handleTabClick(Tab.Show) }
binding.llRecord.setOnClickListener { handleTabClick(Tab.Record) }
binding.llCollect.setOnClickListener { handleTabClick(Tab.Collect) }
}
private fun switchFragment(tab: Tab) {
if (isFragmentTransitionRunning || currentTab == tab) return
val targetFragment = when (tab) {
is Tab.Show -> showFragment
is Tab.Collect -> collFragment
is Tab.Record -> recordFragment
}
Log.d("MainViewActivity", "switchFragment: $tab")
if (binding.fragmentIn.id == View.NO_ID) return
Log.d("MainViewActivity", "switchFragment: $tab")
updateTabUI(tab)
performFragmentTransaction(targetFragment, tab)
}
private fun updateTabUI(tab: Tab) {
// val defaultColor = getColor(R.color.white)
// val selectColor = getColor(R.color.text_blue)
binding.imgCategory.isSelected = tab is Tab.Show
binding.llCategory.isSelected = tab is Tab.Show
binding.ltHome.visibility= if (tab is Tab.Show) View.VISIBLE else View.GONE
binding.imgCollect.isSelected = tab is Tab.Collect
binding.llCollect.isSelected = tab is Tab.Collect
binding.ltFavorite.visibility= if (tab is Tab.Collect) View.VISIBLE else View.GONE
binding.imgRecord.isSelected = tab is Tab.Record
binding.llRecord.isSelected = tab is Tab.Record
binding.ltRecord.visibility= if (tab is Tab.Record) View.VISIBLE else View.GONE
}
private fun performFragmentTransaction(targetFragment: Fragment, tab: Tab) {
isFragmentTransitionRunning = true
supportFragmentManager.beginTransaction().apply {
setCustomAnimations(
android.R.anim.fade_in,
android.R.anim.fade_out
)
currentFragment?.takeIf { it.isAdded }?.let {
hide(it)
}
if (targetFragment.isAdded) {
show(targetFragment)
} else {
add(binding.fragmentIn.id, targetFragment, targetFragment::class.java.simpleName)
}
setReorderingAllowed(true)
// addToBackStack(null)
commit()
}
currentFragment = targetFragment
currentTab = tab
isFragmentTransitionRunning = false
}
private fun handleTabClick(tab: Tab) {
val currentTime = System.currentTimeMillis()
if (currentTime - lastClickTime > CLICK_THROTTLE) {
switchFragment(tab)
lastClickTime = currentTime
}
}
}

View File

@ -0,0 +1,45 @@
package com.soundapp.soundgags.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.GridLayoutManager
import com.soundapp.soundgags.databinding.FragmentExploreStyleBinding
import com.soundapp.soundgags.ReloadApplication
import com.soundapp.soundgags.setting.HomeTransAdapter
import com.soundapp.soundgags.datame.MainViewModel
class ExploreStyleFragment : Fragment() {
private lateinit var binding: FragmentExploreStyleBinding
private lateinit var viewModel: MainViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentExploreStyleBinding.inflate(inflater, container, false)
viewModel = ViewModelProvider(requireActivity())[MainViewModel::class.java]
initView()
return binding.root
}
private fun initView() {
val adapter = HomeTransAdapter()
binding.recyclerView.adapter=adapter
val flexboxLayoutManager = GridLayoutManager(this.requireContext(), 2)
binding.recyclerView.layoutManager=flexboxLayoutManager
// viewModel.randomItems.observe(viewLifecycleOwner) { randomItems ->
// // 更新UI
// adapter.setList(randomItems)
// }
// viewModel.loadRandomItems()
viewModel.debugDatabase()
ReloadApplication.familiars?.let { adapter.setList(it) }
}
}

View File

@ -0,0 +1,50 @@
package com.soundapp.soundgags.ui
import android.content.Intent
import android.os.Bundle
import android.os.CountDownTimer
import android.os.Handler
import androidx.appcompat.app.AppCompatActivity
import com.soundapp.soundgags.databinding.ActivityJokeViewBinding
import com.soundapp.soundgags.setting.TopBarUtils
class JokeViewerActivity : AppCompatActivity() {
private lateinit var binding : ActivityJokeViewBinding
private var handler: Handler = Handler()
private var countDownTimer: CountDownTimer? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
TopBarUtils.setStatusBar(this.window)
TopBarUtils.setLightStatusBar(this.window, true)
binding = ActivityJokeViewBinding.inflate(layoutInflater)
setContentView(binding.root)
handler.postDelayed({
val intent= Intent(
this@JokeViewerActivity,
CompleteListActivity::class.java
)
startActivity(intent)
finish()
}, 4000)
countDownTimer = object : CountDownTimer(4000, 100) {
override fun onTick(millisUntilFinished: Long) {
val progress = ((4000 - millisUntilFinished) / 4000f * 100).toInt()
binding.loadingPb.progress = progress
}
override fun onFinish() {
binding.loadingPb.progress = 100
}
}.start()
}
override fun onDestroy() {
super.onDestroy()
handler.removeCallbacksAndMessages(null)
if (countDownTimer != null) {
countDownTimer!!.cancel()
}
}
}

View File

@ -0,0 +1,84 @@
package com.soundapp.soundgags.ui
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.SystemClock
import androidx.appcompat.app.AppCompatActivity
import com.soundapp.soundgags.R
import com.soundapp.soundgags.databinding.ActivityLocalMusicBinding
import com.soundapp.soundgags.setting.MediaPlayerManager
import com.soundapp.soundgags.setting.TopBarUtils
class LocalMusicActivity : AppCompatActivity() {
private var binding: ActivityLocalMusicBinding? = null
private var startTime: Long = 0
private var elapsedTime: Long = 0
private val handler = Handler()
private var runnable: Runnable? = null
private var isCreate = false
private var record: MediaPlayerManager? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
TopBarUtils.setStatusBar(this.window)
TopBarUtils.setLightStatusBar(this.window, true)
binding = ActivityLocalMusicBinding.inflate(layoutInflater)
setContentView(binding!!.root)
record = MediaPlayerManager(this)
binding!!.audioTime.setText(R.string.time_init)
initClick()
}
private fun initClick() {
binding!!.back.setOnClickListener { finish() }
binding!!.imMic.setOnClickListener {
binding!!.imMic.isSelected = !binding!!.imMic.isSelected
binding!!.audioTint.text=getString(R.string.click_to_finish)
initReflection()
}
}
private fun initReflection() {
if (!isCreate) {
record?.startRecording()
isCreate = true
runnable = object : Runnable {
override fun run() {
val currentTime = SystemClock.elapsedRealtime()
elapsedTime = currentTime - startTime
val milliseconds = (elapsedTime % 1000).toInt() / 10
val seconds = (elapsedTime / 1000).toInt() % 60
val minutes = (elapsedTime / (1000 * 60)).toInt() % 60
binding!!.audioTime.text =
String.format("%02d : %02d : %02d", minutes, seconds, milliseconds)
handler.postDelayed(this, 10)
}
}
startTime = SystemClock.elapsedRealtime()
handler.post(runnable as Runnable)
} else {
record?.stopRecording()
handler.removeCallbacks(runnable!!)
val intent = Intent(
this@LocalMusicActivity,
VoiceRecorderActivity::class.java
)
intent.putExtra(VoiceRecorderActivity.KEY_Time, elapsedTime)
intent.putExtra(VoiceRecorderActivity.KEY_Path, record?.getTempFileName())
intent.putExtra(VoiceRecorderActivity.KEY_import, 1)
startActivity(intent)
finish()
}
}
override fun onDestroy() {
super.onDestroy()
if(runnable!=null){
handler.removeCallbacks(runnable!!)
}
}
}

View File

@ -0,0 +1,250 @@
package com.soundapp.soundgags.ui
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.media.AudioManager
import android.media.MediaPlayer
import android.os.Bundle
import android.os.CountDownTimer
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.soundapp.soundgags.setting.SetUtil
import com.soundapp.soundgags.R
import com.soundapp.soundgags.setting.TypeAdapter
import com.soundapp.soundgags.datame.Reflection
import com.soundapp.soundgags.datame.MainViewModel
import com.soundapp.soundgags.setting.LikeUpClickListener
import com.soundapp.soundgags.setting.TimerThisListener
import com.bumptech.glide.Glide
import com.soundapp.soundgags.databinding.ActivityNoPlayingBinding
import com.soundapp.soundgags.setting.ClockChooseFragment
import com.soundapp.soundgags.setting.TopBarUtils
import java.io.IOException
class NowPlayingActivity : AppCompatActivity(), LikeUpClickListener,
TimerThisListener {
private lateinit var binding: ActivityNoPlayingBinding
private lateinit var data: Reflection
private lateinit var viewModel: MainViewModel
private var mediaPlayer: MediaPlayer? = null
private var countDownTimer: CountDownTimer? = null
private lateinit var popView: ClockChooseFragment
private lateinit var volumeReceiver: BroadcastReceiver
companion object {
const val KEY_LIST_DATA = "list_data"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
TopBarUtils.setStatusBar(this.window)
TopBarUtils.setLightStatusBar(this.window, true)
binding = ActivityNoPlayingBinding.inflate(layoutInflater)
data = intent.getParcelableExtra(KEY_LIST_DATA)!!
setContentView(binding.root)
viewModel = ViewModelProvider(this)[MainViewModel::class.java]
popView = ClockChooseFragment(this, this)
initView()
initAudio()
initClick()
setVolumeReceiver()
}
private fun setVolumeReceiver() {
volumeReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == "android.media.VOLUME_CHANGED_ACTION") {
val audioManager =
context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
if (binding.seekbar.progress != currentVolume) {
binding.seekbar.progress = currentVolume
}
}
}
}
val filter = IntentFilter("android.media.VOLUME_CHANGED_ACTION")
registerReceiver(volumeReceiver, filter)
}
private fun initClick() {
//播放按钮
binding.layoutPlay.setOnClickListener {
val playing = binding.playing.isVisible
if (playing) {
if (mediaPlayer?.isPlaying == true) {
mediaPlayer?.pause()
showLoading(1)
}
} else {
if (!mediaPlayer?.isPlaying!!) {
mediaPlayer?.start()
showLoading(2)
}
}
}
//弹出倒计时
binding.imTimer.setOnClickListener {
popView.showTop(binding.imTimer)
}
//like
binding.imLike.setOnClickListener {
val isSelected = binding.imLike.isSelected
if(isSelected){
viewModel.updateLikeStatus(data.name,false)
}else{
viewModel.updateLikeStatus(data.name,true)
}
binding.imLike.isSelected = !isSelected
}
}
private fun initAudio() {
showLoading(0)
mediaPlayer = MediaPlayer()
try {
mediaPlayer?.reset()
mediaPlayer?.setDataSource(data.audioUrl)
mediaPlayer?.prepareAsync()
mediaPlayer?.setOnPreparedListener { showLoading(1) }
mediaPlayer?.setOnCompletionListener { showLoading(1) }
} catch (ioException: IOException) {
showLoading(0)
Toast.makeText(this, "----------prepare fail", Toast.LENGTH_SHORT).show()
}
}
private fun initView() {
//设置音量
SetUtil.setVolume(this, binding.seekbar)
binding.title.text = data.name
binding.back.setOnClickListener { finish() }
binding.imLoop.setOnClickListener {
binding.imLoop.setSelected(!binding.imLoop.isSelected)
mediaPlayer!!.isLooping = binding.imLoop.isSelected
}
val convert = data.convert
if (convert.isEmpty()) {
Glide.with(this).load(R.drawable.pic_only_diy)
.into(binding.imgRecord)
} else {
Glide.with(this).load(data.convert).error(R.drawable.pic_only_diy)
.placeholder(R.drawable.pic_only_diy)
.into(binding.imgRecord)
}
val adapter = TypeAdapter()
adapter.setListener(this)
binding.recyclerView.adapter = adapter
binding.recyclerView.layoutManager =
LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
//more
viewModel.randomItems.observe(this@NowPlayingActivity) { randomItems ->
// 更新UI
Log.d("TAG", "initView: $randomItems")
adapter.setList(randomItems, 1)
adapter.notifyDataSetChanged()
}
viewModel.loadRandomItems()
// like
viewModel.checkLikeStatus(data.name, callback = { isLike ->
run {
binding.imLike.isSelected = isLike
}
})
//
}
private fun showLoading(type: Int) {
when (type) {
0 -> {//加载中
binding.playLoading.visibility = View.VISIBLE
binding.toPlay.visibility = View.GONE
binding.playing.visibility = View.GONE
}
1 -> {//可播放
binding.playLoading.visibility = View.GONE
binding.toPlay.visibility = View.VISIBLE
binding.playing.visibility = View.GONE
}
2 -> {//播放中
binding.playLoading.visibility = View.GONE
binding.toPlay.visibility = View.GONE
binding.playing.visibility = View.VISIBLE
}
}
}
private fun stopCountDown() {
countDownTimer?.cancel()
}
private fun startCountDown(time: Long) {
stopCountDown()
countDownTimer = object : CountDownTimer(time, 1000) {
override fun onTick(millisUntilFinished: Long) {
}
override fun onFinish() {
popView.setOffSelected()
binding.imTimer.isSelected = false
if (!mediaPlayer?.isPlaying!!) {
showLoading(2)
mediaPlayer?.start()
}
}
}
countDownTimer?.start()
}
override fun onDestroy() {
super.onDestroy()
if (mediaPlayer?.isPlaying == true) {
mediaPlayer?.stop()
}
mediaPlayer?.release()
stopCountDown()
if (::popView.isInitialized) {
popView.hide()
}
}
override fun onMoreClick(reflection: Reflection,isAdd: Boolean) {
val intent = Intent(this, NowPlayingActivity::class.java)
intent.putExtra(KEY_LIST_DATA, reflection)
startActivity(intent)
finish()
}
override fun onClickTimerListener(time: Long) {
runOnUiThread {
popView.hide()
if (time == -1L) {
Log.d("TAG", "onClickTimerListener: $time")
binding.imTimer.isSelected = false
stopCountDown()
} else {
binding.imTimer.isSelected = true
startCountDown(time)
}
}
}
}

View File

@ -0,0 +1,43 @@
package com.soundapp.soundgags.ui
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.GridLayoutManager
import com.soundapp.soundgags.setting.TypeAdapter
import com.soundapp.soundgags.datame.Familiar
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.soundapp.soundgags.databinding.ActivityOpenSetBinding
import com.soundapp.soundgags.setting.TopBarUtils
class OpenSetActivity : AppCompatActivity() {
private lateinit var binding: ActivityOpenSetBinding
private lateinit var data: Familiar
companion object {
const val KEY_LIST_DATA = "list_data"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityOpenSetBinding.inflate(layoutInflater)
setContentView(binding.root)
TopBarUtils.setStatusBar(this.window)
TopBarUtils.setLightStatusBar(this.window, true)
data = intent.getParcelableExtra(KEY_LIST_DATA)!!
initView()
}
private fun initView() {
Glide.with(this)
.load(data.convert)
.apply(RequestOptions.circleCropTransform())
.into(binding.image)
binding.title.text = data.name
val adapter = TypeAdapter()
binding.recyclerView.adapter = adapter
binding.recyclerView.layoutManager = GridLayoutManager(this, 3)
adapter.setList(data.infolist)
binding.back.setOnClickListener { finish() }
}
}

View File

@ -0,0 +1,63 @@
package com.soundapp.soundgags.ui
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.soundapp.soundgags.databinding.FragmentSaveItemsBinding
import com.soundapp.soundgags.setting.FavSolidAdapter
import com.soundapp.soundgags.datame.Reflection
import com.soundapp.soundgags.datame.MainViewModel
import com.soundapp.soundgags.setting.LikeUpClickListener
class SavedItemsFragment(viewModel: MainViewModel) : Fragment(),LikeUpClickListener {
private lateinit var binding: FragmentSaveItemsBinding
private lateinit var adapter: FavSolidAdapter
private val mainViewModel: MainViewModel = viewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
binding = FragmentSaveItemsBinding.inflate(inflater, container, false)
initView()
return binding.root
}
override fun onResume() {
super.onResume()
Log.d("CollectFragment", "onResume: ")
mainViewModel.loadLikeItems()
mainViewModel.loadCustomItems()
}
private fun initView() {
adapter = FavSolidAdapter()
adapter.setListener(this)
mainViewModel.likeItems.observe(viewLifecycleOwner){likeList ->
Log.d("CollectFragment", "initView: $likeList")
if(likeList.isEmpty()){
binding.emptyView.visibility=View.VISIBLE
binding.recyclerView.visibility=View.GONE
}else{
binding.emptyView.visibility=View.GONE
binding.recyclerView.visibility=View.VISIBLE
adapter.setList(likeList,0)
adapter.notifyDataSetChanged()
}
}
binding.recyclerView.adapter = adapter
binding.recyclerView.layoutManager=LinearLayoutManager(requireContext(),LinearLayoutManager.VERTICAL,false)
}
override fun onMoreClick(reflection: Reflection,isAdd: Boolean) {
mainViewModel.updateLikeStatus(reflection.name,!isAdd)
}
}

View File

@ -0,0 +1,218 @@
package com.soundapp.soundgags.ui
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts.GetContent
import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.soundapp.soundgags.R
import com.soundapp.soundgags.databinding.FragmentSoundBinding
import com.soundapp.soundgags.setting.SetUtil
import com.soundapp.soundgags.setting.FavSolidAdapter
import com.soundapp.soundgags.datame.Reflection
import com.soundapp.soundgags.datame.MainViewModel
import com.soundapp.soundgags.setting.LikeUpClickListener
class SoundFragment(viewModel: MainViewModel) : Fragment(),LikeUpClickListener{
private lateinit var binding: FragmentSoundBinding
private val mianViewModel: MainViewModel = viewModel
private lateinit var adapter: FavSolidAdapter
private var type: Int = 1
private var permission_read_audio: Array<String> = arrayOf(Manifest.permission.READ_MEDIA_AUDIO)
private var permission_record_audio: String = Manifest.permission.RECORD_AUDIO
private var permission_store: Array<String> = arrayOf(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
)
private lateinit var requestStorePermissionsLauncher: ActivityResultLauncher<Array<String>>
private lateinit var requestRecordAudioLauncher: ActivityResultLauncher<String>
private lateinit var audioPickerLauncher: ActivityResultLauncher<String>
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
binding = FragmentSoundBinding.inflate(inflater, container, false)
initView()
return binding.root
}
override fun onResume() {
super.onResume()
mianViewModel.loadCustomItems()
mianViewModel.loadLikeItems()
}
private fun initView() {
adapter = FavSolidAdapter()
adapter.setListener(this)
mianViewModel.customItems.observe(viewLifecycleOwner){customList ->
Log.d("CustomFragment", "initView: $customList")
if(customList.isEmpty()){
binding.emptyView.visibility=View.VISIBLE
binding.recyclerView.visibility=View.GONE
}else{
binding.emptyView.visibility=View.GONE
binding.recyclerView.visibility=View.VISIBLE
adapter.setList(customList,1)
Log.d("CustomFragment", "initView: ${adapter.itemCount}")
adapter.notifyDataSetChanged()
}
}
mianViewModel.likeItems.observe(viewLifecycleOwner) { likeList ->
Log.d("CustomFragment", "likeItems updated: ${likeList.size}")
mianViewModel.loadCustomItems()
}
binding.recyclerView.adapter = adapter
binding.recyclerView.layoutManager=
LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL,false)
selectBtnStyle()//初始化默认选择录制按钮
binding
binding.recordImg.setOnClickListener {
type=1
Log.d("---------", "========type=$type")
selectBtnStyle()
}
binding.diyImg.setOnClickListener {
type=0
Log.d("---------", "========type=$type")
selectBtnStyle()
}
binding.upload.setOnClickListener {
selectBtnClick(type)
}
requestRecordAudioLauncher = registerForActivityResult<String, Boolean>(
RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
intentRecordAudio()
} else {
Toast.makeText(requireContext(), "Recording permission denied", Toast.LENGTH_SHORT)
.show()
}
}
requestStorePermissionsLauncher =
registerForActivityResult<Array<String>, Map<String, Boolean>>(
RequestMultiplePermissions()
) { result ->
var isGranted = true
for ((_, value) in result) {
isGranted = value
if (!value) {
isGranted = false
}
}
if (isGranted) {
importFromLocal()
} else {
Toast.makeText(requireContext(), " permits denied", Toast.LENGTH_SHORT)
.show()
}
}
audioPickerLauncher = registerForActivityResult<String, Uri>(
GetContent()
) { uri -> // Handle the returned Uri
if (uri != null) {
Log.d("---------", "========uri=$uri")
val duration: Long = SetUtil.getAudioDuration(requireContext(), uri)
val intent = Intent(
requireContext(),
VoiceRecorderActivity::class.java
)
intent.putExtra(VoiceRecorderActivity.KEY_Path, uri.toString())
intent.putExtra(VoiceRecorderActivity.KEY_Time, duration)
startActivity(intent)
}
}
}
private fun selectBtnStyle() {
if(type == 0){
binding.recordImg.background=requireContext().getDrawable(R.drawable.record_tran_alittile)
binding.diyImg.background=requireContext().getDrawable(R.drawable.gradient_sel_back)
binding.upload.text= requireActivity().getString(R.string.start_upload)
}else{
binding.recordImg.background=requireContext().getDrawable(R.drawable.gradient_sel_back)
binding.diyImg.background=requireContext().getDrawable(R.drawable.record_tran_alittile)
binding.upload.text= requireActivity().getString(R.string.start_record)
}
}
override fun onMoreClick(reflection: Reflection,isAdd: Boolean) {
mianViewModel.updateLikeStatus(reflection.name,!isAdd)
}
override fun onCustomizationClick(reflection: Reflection) {
mianViewModel.deleteItem(reflection)
}
fun selectBtnClick(type: Int) {
if(type == 0){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(
requireContext(),
permission_read_audio[0]
)
!= PackageManager.PERMISSION_GRANTED
) {
requestStorePermissionsLauncher.launch(permission_read_audio)
// ActivityCompat.requestPermissions(requireActivity(), ConstValues.permission_read_audio,
// ConstValues.REQUEST_READ_STORAGE_PERMISSION);
} else {
importFromLocal()
}
} else {
if ((ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
!= PackageManager.PERMISSION_GRANTED) ||
(ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.READ_EXTERNAL_STORAGE
)
!= PackageManager.PERMISSION_GRANTED)
) {
requestStorePermissionsLauncher.launch(permission_store)
// ActivityCompat.requestPermissions(requireActivity(), ConstValues.permission_store, ConstValues.REQUEST_READ_STORAGE_PERMISSION);
} else {
importFromLocal()
}
}
}
else{
checkAndRequestAudioPermission()
}
}
private fun importFromLocal() {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.setType("audio/*")
audioPickerLauncher.launch("audio/*")
}
private fun checkAndRequestAudioPermission() {
if (ContextCompat.checkSelfPermission(requireContext(), permission_record_audio)
!= PackageManager.PERMISSION_GRANTED
) {
requestRecordAudioLauncher.launch(permission_record_audio)
} else {
intentRecordAudio()
}
}
private fun intentRecordAudio() {
val intent = Intent(requireContext(), LocalMusicActivity::class.java)
startActivity(intent)
}
}

View File

@ -0,0 +1,247 @@
package com.soundapp.soundgags.ui
import android.annotation.SuppressLint
import android.content.Context
import android.media.AudioAttributes
import android.media.MediaPlayer
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.os.SystemClock
import android.text.TextUtils
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import com.soundapp.soundgags.R
import com.soundapp.soundgags.datame.Reflection
import com.soundapp.soundgags.datame.MainViewModel
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import androidx.core.net.toUri
import com.soundapp.soundgags.databinding.ActivityVoiceRecorderBinding
import com.soundapp.soundgags.setting.TopBarUtils
class VoiceRecorderActivity : AppCompatActivity() {
private lateinit var binding: ActivityVoiceRecorderBinding
private var path: String? = null
private var mediaPlayer: MediaPlayer? = null
private val handler = Handler()
private var timeUpdater: Runnable? = null
private var startTime: Long = 0
private var elapsedTime: Long = 0
private lateinit var viewModel: MainViewModel
private var audioDuration: Long = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
TopBarUtils.setStatusBar(this.window)
TopBarUtils.setLightStatusBar(this.window, true)
binding = ActivityVoiceRecorderBinding.inflate(layoutInflater)
setContentView(binding.root)
viewModel = ViewModelProvider(this)[MainViewModel::class.java]
binding.audioTime.setText(R.string.time_init)
initData()
initEvent()
}
@Throws(IOException::class)
private fun togglePrank() {
if (binding.btnPlay.isSelected) {
initMediaPlayer()
} else {
stopPlay()
}
}
private fun stopPlay() {
if (timeUpdater != null) {
handler.removeCallbacks(timeUpdater!!)
}
if (mediaPlayer != null) {
mediaPlayer!!.release()
mediaPlayer = null
}
}
private fun initMediaPlayer() {
if (mediaPlayer == null) {
mediaPlayer = MediaPlayer()
mediaPlayer!!.setAudioAttributes(
AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setUsage(AudioAttributes.USAGE_MEDIA)
.build()
)
try {
mediaPlayer!!.setDataSource(path)
} catch (_: Exception) {
}
mediaPlayer!!.setOnPreparedListener { mp: MediaPlayer? ->
mediaPlayer!!.start()
beginTimer()
}
mediaPlayer!!.setOnCompletionListener { mp: MediaPlayer? ->
binding.btnPlay.isSelected = false
handler.removeCallbacks(timeUpdater!!)
}
mediaPlayer!!.prepareAsync()
} else {
mediaPlayer!!.start()
beginTimer()
}
}
private fun beginTimer() {
if (timeUpdater == null) {
timeUpdater = object : Runnable {
@SuppressLint("DefaultLocale")
override fun run() {
if (mediaPlayer != null && mediaPlayer!!.isPlaying) {
val currentTime = SystemClock.elapsedRealtime()
elapsedTime = currentTime - startTime
val milliseconds = (elapsedTime % 1000).toInt() / 10
val seconds = (elapsedTime / 1000).toInt() % 60
val minutes = (elapsedTime / (1000 * 60)).toInt() % 60
binding.audioTime.text =
String.format(
"%02d : %02d : %02d",
minutes,
seconds,
milliseconds
)
handler.postDelayed(this, 10)
}
}
}
}
startTime = SystemClock.elapsedRealtime()
handler.post(timeUpdater!!)
}
private fun initEvent() {
binding.back.setOnClickListener { finish() }
binding.audioTimeLayout.setOnClickListener {
binding.btnPlay.isSelected = !binding.btnPlay.isSelected
try {
togglePrank()
} catch (e: IOException) {
Log.e("SaveActivity", "Error toggling prank", e)
}
}
binding.save.setOnClickListener {
val name = binding.saveEdit.text.toString().trim()
viewModel.checkSaveStatus(name, callback = { isSave ->
runOnUiThread {
// 1. 先检查Activity是否即将销毁避免在已销毁的Activity上操作
if (isFinishing || isDestroyed) {
return@runOnUiThread
}
if (TextUtils.isEmpty(name)) {
Toast.makeText(
this@VoiceRecorderActivity,
getString(R.string.save_tip), // 空名称提示
Toast.LENGTH_SHORT
).show()
return@runOnUiThread
}
if (isSave) {
binding.saveEdit.setText("")
Toast.makeText(
this@VoiceRecorderActivity, // 使用显式的 this@ActivityName
getString(R.string.name_hint), // 名称已存在提示
Toast.LENGTH_SHORT
).show()
} else {
saveData(name)
Toast.makeText(
this@VoiceRecorderActivity,
getString(R.string.save_tips), // 保存成功提示
Toast.LENGTH_SHORT
).show()
finish()
}
}
})
}
}
private fun saveData(name: String) {
val cacheDir = cacheDir
val sourceFile = File(path)
val destinationFile = File(cacheDir, "$name.mp3")
if (sourceFile.renameTo(destinationFile)) {
val mydata = Reflection()
mydata.isLike = false
mydata.name = name
mydata.isCustomization = true
mydata.audioUrl = destinationFile.absolutePath
viewModel.insertItem(mydata)
} else {
Log.e("SaveActivity", "Failed to rename file")
}
}
private fun readFileFromUri(context: Context, uri: Uri): String? {
val contentResolver = context.contentResolver
try {
contentResolver.openInputStream(uri).use { inputStream ->
FileOutputStream(
File(
cacheDir, "temp_audio_file.mp3"
)
).use { outputStream ->
if (inputStream == null) {
Log.e("SaveActivity", "Input stream is null")
return null
}
val buffer = ByteArray(1024)
var bytesRead: Int
while ((inputStream.read(buffer).also { bytesRead = it }) != -1) {
outputStream.write(buffer, 0, bytesRead)
}
return File(cacheDir, "temp_audio_file.mp3").absolutePath
}
}
} catch (e: IOException) {
Log.e("SaveActivity", "Error reading file from URI", e)
return null
}
}
private fun initData() {
val intent = intent
audioDuration = intent.getLongExtra(KEY_Time, 0)
val importFlag = intent.getIntExtra(KEY_import, 0)
if (importFlag == 1) {
path = intent.getStringExtra(KEY_Path)
Log.e("zzj", "save path is$path")
} else {
val uriPath = intent.getStringExtra(KEY_Path)
val uri = uriPath!!.toUri()
path = readFileFromUri(this, uri)
}
}
override fun onDestroy() {
super.onDestroy()
stopPlay()
}
companion object {
var KEY_Time: String = "KEY_time"
var KEY_Path: String = "KEY_path"
var KEY_import: String = "flag"
}
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:topLeftRadius="20dp" android:topRightRadius="20dp"/>
<solid android:color="#d1f0eff5" />
</shape>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="#d5d8f3"/>
<corners android:radius="10dp"/>
</shape>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/choose_fragh" android:state_selected="true"/>
<item android:drawable="@drawable/choose_fragh_none"/>
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/choose_fragl" android:state_selected="true"/>
<item android:drawable="@drawable/choose_fragl_none"/>
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/clock_so_sel" android:state_selected="true"/>
<item android:drawable="@drawable/clock_off_status"/>
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M395.2,513.6l323.1,-312.4c19.1,-18.4 19.1,-48.3 0,-66.7 -19.1,-18.4 -49.9,-18.4 -69,0L291.8,480.3c-19.1,18.4 -19.1,48.3 0,66.7l357.6,345.7c9.5,9.2 22,13.8 34.5,13.8 12.5,0 25,-4.6 34.5,-13.8 19.1,-18.4 19.1,-48.2 0,-66.7L395.2,513.6z"
android:fillColor="@color/white"/>
</vector>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<gradient
android:startColor="@android:color/transparent"
android:centerColor="@android:color/transparent"
android:endColor="#44000000" android:angle="270"/>
<corners android:radius="25dp"/>
</shape>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/sound_audio_no_play" android:state_selected="false" />
<item android:drawable="@drawable/sound_audio_play" android:state_selected="true" />
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="25dp"/>
<gradient android:startColor="#3E7CB1" android:endColor="#87CEEB" android:type="linear" android:angle="0"/>
</shape>

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>

View File

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="8dp" />
<solid android:color="@color/white"/>
</shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="25dp"/>
<stroke android:color="@color/dart_blue_text" android:width="1dp"/>
<solid android:color="@color/white"/>
</shape>

View File

@ -0,0 +1,21 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="5dp" />
<solid android:color="#d1f0eff5" /> <!-- 背景颜色 -->
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<corners android:radius="25dp" />
<gradient
android:startColor="@color/navi_text_color"
android:centerColor="@color/navi_text_color"
android:endColor="@color/navi_text_color"
android:angle="0" />
</shape>
</clip>
</item>
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:topLeftRadius="10dp" android:topRightRadius="10dp"/>
<solid android:color="#f0eff5"/>
</shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/love_sign_sel" android:state_selected="false" />
<item android:drawable="@drawable/love_sign_none" android:state_selected="true" />
</selector>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/navi_text_color" />
<size
android:width="17dp"
android:height="17dp" />
</shape>

View File

@ -0,0 +1,21 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="45dp"
android:height="45dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M513.1,504.6m-450.8,0a450.8,450.8 0,1 0,901.6 0,450.8 450.8,0 1,0 -901.6,0Z"
android:fillColor="@color/white"/>
<path
android:pathData="M513.1,182c-178.2,0 -322.7,144.4 -322.7,322.7 0,178.2 144.4,322.7 322.7,322.7 178.2,0 322.7,-144.4 322.7,-322.7 0,-178.2 -144.4,-322.7 -322.7,-322.7zM419.1,397.9c0,-51.9 42,-94 94,-94 51.9,0 94,42 94,94v96.3c0,51.9 -42,94 -94,94 -51.9,0 -94,-42 -94,-94v-96.3zM684.5,492.5c0,86.5 -63.3,158.4 -145.7,171.2v46.7c0,14.1 -11.5,25.6 -25.6,25.6s-25.6,-11.5 -25.6,-25.6v-46.5C404.5,651.7 340.4,579.5 340.4,492.5v-12.8c0,-14.1 11.5,-25.6 25.6,-25.6s25.6,11.5 25.6,25.6v12.8c0,67.3 54.2,122 120.8,122 66.7,0 120.8,-54.7 120.8,-122v-12.8c0,-14.1 11.5,-25.6 25.6,-25.6s25.6,11.5 25.6,25.6v12.8z"
android:fillColor="@color/dart_blue_text"/>
<path
android:pathData="M799.6,356.4c-53.7,-103.6 -161.8,-174.4 -286.6,-174.4 -178.2,0 -322.7,144.4 -322.7,322.7 0,117.1 62.4,219.6 155.8,276.2 1.1,0 2.3,0.1 3.4,0.1 240.1,0 436.4,-187.8 450,-424.4zM419.1,397.9c0,-51.9 42,-94 94,-94 51.9,0 94,42 94,94v96.3c0,51.9 -42,94 -94,94 -51.9,0 -94,-42 -94,-94v-96.3zM538.8,663.7v46.7c0,14.1 -11.5,25.6 -25.6,25.6s-25.6,-11.5 -25.6,-25.6v-46.5C404.5,651.7 340.4,579.5 340.4,492.5v-12.8c0,-14.1 11.5,-25.6 25.6,-25.6s25.6,11.5 25.6,25.6v12.8c0,67.3 54.2,122 120.8,122 66.7,0 120.8,-54.7 120.8,-122v-12.8c0,-14.1 11.5,-25.6 25.6,-25.6s25.6,11.5 25.6,25.6v12.8c0.1,86.5 -63.2,158.4 -145.6,171.2z"
android:fillColor="@color/dart_blue_text"/>
<path
android:pathData="M190.4,504.6c0,40.9 7.7,79.9 21.5,115.9 4.8,0.2 9.5,0.3 14.3,0.3 51.9,0 101.8,-8.8 148.2,-24.9a172.9,172.9 0,0 1,-34.1 -103.3v-12.8c0,-14.1 11.5,-25.6 25.6,-25.6s25.6,11.5 25.6,25.6v12.8c0,31.9 12.2,61 32.2,82.8 8.4,-4.1 16.6,-8.4 24.7,-13.1 -18.1,-17.1 -29.4,-41.3 -29.4,-68.1v-96.3c0,-51.9 42,-94 94,-94 51.9,0 94,42 94,94v13.6a448.5,448.5 0,0 0,66.8 -186.6,321 321,0 0,0 -160.7,-42.9c-178.1,0 -322.6,144.5 -322.6,322.7z"
android:fillColor="@color/dart_blue_text"/>
<path
android:pathData="M194.5,453.8c152.2,-27.8 277.7,-132 335.3,-271.5 -5.5,-0.3 -11.1,-0.4 -16.6,-0.4 -160.9,0 -294.2,117.8 -318.6,271.9z"
android:fillColor="@color/dart_blue_text"/>
</vector>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="false" android:drawable="@drawable/pause_big_show"/>
<item android:state_selected="true" android:drawable="@drawable/play_red_big"/>
</selector>

View File

@ -0,0 +1,24 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="256dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M904,960H120c-30.9,0 -56,-25.1 -56,-56V120c0,-30.9 25.1,-56 56,-56h784c30.9,0 56,25.1 56,56v784c0,30.9 -25.1,56 -56,56z"
android:fillColor="@color/dart_blue_text"/>
<path
android:pathData="M512,512m-336,0a336,336 0,1 0,672 0,336 336,0 1,0 -672,0Z"
android:fillColor="@color/black"/>
<path
android:pathData="M512,512m-112,0a112,112 0,1 0,224 0,112 112,0 1,0 -224,0Z"
android:fillColor="@color/white"/>
<path
android:pathData="M512,512m-42,0a42,42 0,1 0,84 0,42 42,0 1,0 -84,0Z"
android:fillColor="@color/dart_blue_text"/>
<path
android:pathData="M842.7,239c-34.7,0 -63,-28.3 -63,-63s28.3,-63 63,-63 63,28.3 63,63 -28.3,63 -63,63zM842.7,155c-11.6,0 -21,9.4 -21,21s9.4,21 21,21 21,-9.4 21,-21 -9.4,-21 -21,-21z"
android:fillColor="@color/white"/>
<path
android:pathData="M715.1,597.2c-6.7,0 -13.3,-3.2 -17.3,-9.1 -6.6,-9.6 -4.1,-22.6 5.4,-29.2l52.8,-36.2 67.6,-309.2c2.5,-11.3 13.7,-18.5 25,-16 11.3,2.5 18.5,13.7 16,25L795.3,539.8a21,21 0,0 1,-8.6 12.8l-59.7,40.9a20.9,20.9 0,0 1,-11.9 3.7z"
android:fillColor="@color/white"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<stroke android:width="1dp" android:color="@color/dart_blue_text"/>
<corners android:radius="15dp"/>
</shape>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<item android:state_selected="true" android:drawable="@drawable/pop_dark_blue_sel_bg"/>
<item android:drawable="@drawable/trans_bg"/>
</selector>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="@color/white"/>
<corners android:radius="15dp"/>
</shape>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="8dp"/>
<solid android:color="@color/dart_blue_text"/>
</shape>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/choose_fragl" android:state_selected="true"/>
<item android:drawable="@drawable/choose_fragr_none"/>
</selector>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="25dp"/>
<solid android:color="#d1f0eff5" />
</shape>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/record_tran_alittile"/>
<item android:drawable="@drawable/gradient_sel_back" android:state_selected="true"/>
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:topRightRadius="10dp" android:topLeftRadius="10dp"/>
<solid android:color="#FFFFFF"/>
</shape>

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Some files were not shown because too many files have changed in this diff Show More