接入Tradplus(源代码)
15
.gitignore
vendored
Normal 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
@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
6
.idea/AndroidProjectSystem.xml
generated
Normal 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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
71
app/build.gradle.kts
Normal 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
@ -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
|
||||||
BIN
app/release/Sound Gags_V3.0_312_18_10_06-release.apk
Normal file
BIN
app/release/Sound Gags_V3.0_312_18_10_12-release.aab
Normal file
37
app/release/output-metadata.json
Normal 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
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
49
app/src/main/AndroidManifest.xml
Normal 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>
|
||||||
BIN
app/src/main/assets/lato_regular.ttf
Normal file
3705
app/src/main/assets/prank.json
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
185
app/src/main/java/com/soundapp/soundgags/datame/MainViewModel.kt
Normal 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 = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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)
|
||||||
|
}
|
||||||
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
28
app/src/main/java/com/soundapp/soundgags/datame/allEn.kt
Normal 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
|
||||||
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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){
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
108
app/src/main/java/com/soundapp/soundgags/setting/SetUtil.kt
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
package com.soundapp.soundgags.setting
|
||||||
|
|
||||||
|
interface TimerThisListener {
|
||||||
|
fun onClickTimerListener(time: Long)
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -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() }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
218
app/src/main/java/com/soundapp/soundgags/ui/SoundFragment.kt
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
6
app/src/main/res/drawable/_20_coner_radius.xml
Normal 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>
|
||||||
5
app/src/main/res/drawable/blue_little_ic.xml
Normal 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>
|
||||||
BIN
app/src/main/res/drawable/choose_fragh.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
app/src/main/res/drawable/choose_fragh_none.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
5
app/src/main/res/drawable/choose_fragh_sel.xml
Normal 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>
|
||||||
BIN
app/src/main/res/drawable/choose_fragl.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
app/src/main/res/drawable/choose_fragl_none.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
5
app/src/main/res/drawable/choose_fragl_sel.xml
Normal 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>
|
||||||
BIN
app/src/main/res/drawable/choose_fragr_none.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
app/src/main/res/drawable/choose_fragr_sel.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
5
app/src/main/res/drawable/clock_off_on.xml
Normal 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>
|
||||||
BIN
app/src/main/res/drawable/clock_off_status.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
app/src/main/res/drawable/clock_so_sel.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
9
app/src/main/res/drawable/come_back.xml
Normal 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>
|
||||||
8
app/src/main/res/drawable/cover_on_img.xml
Normal 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>
|
||||||
5
app/src/main/res/drawable/cycle_selector.xml
Normal 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>
|
||||||
BIN
app/src/main/res/drawable/del_red_icon.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
app/src/main/res/drawable/go_to_fly.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
5
app/src/main/res/drawable/gradient_sel_back.xml
Normal 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>
|
||||||
74
app/src/main/res/drawable/ic_launcher_background.xml
Normal 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>
|
||||||
30
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal 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>
|
||||||
6
app/src/main/res/drawable/item_show_style.xml
Normal 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>
|
||||||
6
app/src/main/res/drawable/item_solid.xml
Normal 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>
|
||||||
21
app/src/main/res/drawable/joke_view_bg.xml
Normal 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>
|
||||||
BIN
app/src/main/res/drawable/love_love_ic.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
app/src/main/res/drawable/love_sign_none.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
app/src/main/res/drawable/love_sign_sel.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
5
app/src/main/res/drawable/main_bottom_activity.xml
Normal 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>
|
||||||
6
app/src/main/res/drawable/me_add_love_selector.xml
Normal 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>
|
||||||
8
app/src/main/res/drawable/media_no_playing.xml
Normal 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>
|
||||||
21
app/src/main/res/drawable/pause_big_show.xml
Normal 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>
|
||||||
7
app/src/main/res/drawable/pause_or_play.xml
Normal 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>
|
||||||
24
app/src/main/res/drawable/pic_only_diy.xml
Normal 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>
|
||||||
BIN
app/src/main/res/drawable/play_ing.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
app/src/main/res/drawable/play_red_big.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
5
app/src/main/res/drawable/pop_dark_blue_sel_bg.xml
Normal 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>
|
||||||
5
app/src/main/res/drawable/pop_sel_selector.xml
Normal 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>
|
||||||
5
app/src/main/res/drawable/pop_white_none_bg.xml
Normal 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>
|
||||||
5
app/src/main/res/drawable/reco_save_button.xml
Normal 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>
|
||||||
BIN
app/src/main/res/drawable/record_logo2.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
5
app/src/main/res/drawable/record_rec_selector.xml
Normal 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>
|
||||||
5
app/src/main/res/drawable/record_tran_alittile.xml
Normal 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>
|
||||||
5
app/src/main/res/drawable/record_ty_sel.xml
Normal 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>
|
||||||
BIN
app/src/main/res/drawable/sound_audio_no_play.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
app/src/main/res/drawable/sound_audio_play.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
app/src/main/res/drawable/sound_logo1.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
app/src/main/res/drawable/sound_loud_jia.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
app/src/main/res/drawable/sound_loud_jian.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
6
app/src/main/res/drawable/text_wite_top_cor_bg.xml
Normal 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>
|
||||||
4
app/src/main/res/drawable/trans_bg.xml
Normal 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>
|
||||||
BIN
app/src/main/res/drawable/trans_play_btn.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |