diff --git a/naru/W01/.gitignore b/naru/AndroidFloClone/.gitignore similarity index 100% rename from naru/W01/.gitignore rename to naru/AndroidFloClone/.gitignore diff --git a/naru/W01/.idea/.gitignore b/naru/AndroidFloClone/.idea/.gitignore similarity index 100% rename from naru/W01/.idea/.gitignore rename to naru/AndroidFloClone/.idea/.gitignore diff --git a/naru/W01/.idea/compiler.xml b/naru/AndroidFloClone/.idea/compiler.xml similarity index 100% rename from naru/W01/.idea/compiler.xml rename to naru/AndroidFloClone/.idea/compiler.xml diff --git a/naru/AndroidFloClone/.idea/deploymentTargetSelector.xml b/naru/AndroidFloClone/.idea/deploymentTargetSelector.xml new file mode 100644 index 00000000..af9e22b5 --- /dev/null +++ b/naru/AndroidFloClone/.idea/deploymentTargetSelector.xml @@ -0,0 +1,18 @@ + + + + + + + + + \ No newline at end of file diff --git a/naru/W01/.idea/gradle.xml b/naru/AndroidFloClone/.idea/gradle.xml similarity index 100% rename from naru/W01/.idea/gradle.xml rename to naru/AndroidFloClone/.idea/gradle.xml diff --git a/naru/W01/.idea/kotlinc.xml b/naru/AndroidFloClone/.idea/kotlinc.xml similarity index 100% rename from naru/W01/.idea/kotlinc.xml rename to naru/AndroidFloClone/.idea/kotlinc.xml diff --git a/naru/W01/.idea/migrations.xml b/naru/AndroidFloClone/.idea/migrations.xml similarity index 100% rename from naru/W01/.idea/migrations.xml rename to naru/AndroidFloClone/.idea/migrations.xml diff --git a/naru/AndroidFloClone/.idea/misc.xml b/naru/AndroidFloClone/.idea/misc.xml new file mode 100644 index 00000000..8978d23d --- /dev/null +++ b/naru/AndroidFloClone/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/naru/W01/.idea/vcs.xml b/naru/AndroidFloClone/.idea/vcs.xml similarity index 100% rename from naru/W01/.idea/vcs.xml rename to naru/AndroidFloClone/.idea/vcs.xml diff --git a/naru/W01/app/.gitignore b/naru/AndroidFloClone/app/.gitignore similarity index 100% rename from naru/W01/app/.gitignore rename to naru/AndroidFloClone/app/.gitignore diff --git a/naru/AndroidFloClone/app/build.gradle.kts b/naru/AndroidFloClone/app/build.gradle.kts new file mode 100644 index 00000000..09a03db9 --- /dev/null +++ b/naru/AndroidFloClone/app/build.gradle.kts @@ -0,0 +1,70 @@ +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + id("kotlin-kapt") +} + +android { + namespace = "com.example.androidfloclone" + compileSdk = 34 + + defaultConfig { + applicationId = "com.example.androidfloclone" + minSdk = 24 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } + + buildFeatures { + viewBinding = true + dataBinding = true + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.material) + implementation(libs.androidx.activity) + implementation(libs.androidx.constraintlayout) + implementation(libs.circleindicator) + implementation(libs.gson) + implementation(libs.room.runtime) + implementation(libs.room.ktx) + kapt(libs.room.compiler) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + + // Retrofit + implementation("com.squareup.retrofit2:retrofit:2.9.0") + implementation("com.squareup.retrofit2:converter-gson:2.9.0") + + // OkHTTP + implementation("com.squareup.okhttp3:okhttp:4.10.0") + implementation("com.squareup.okhttp3:logging-interceptor:4.10.0") + + // Glide + implementation("com.github.bumptech.glide:glide:4.15.1") + kapt("com.github.bumptech.glide:compiler:4.15.1") +} diff --git a/naru/W01/app/proguard-rules.pro b/naru/AndroidFloClone/app/proguard-rules.pro similarity index 100% rename from naru/W01/app/proguard-rules.pro rename to naru/AndroidFloClone/app/proguard-rules.pro diff --git a/naru/AndroidFloClone/app/src/androidTest/java/com/example/androidfloclone/ExampleInstrumentedTest.kt b/naru/AndroidFloClone/app/src/androidTest/java/com/example/androidfloclone/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..8b290b0a --- /dev/null +++ b/naru/AndroidFloClone/app/src/androidTest/java/com/example/androidfloclone/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.example.androidfloclone + +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.example.androidfloclone", appContext.packageName) + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/AndroidManifest.xml b/naru/AndroidFloClone/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..d70b562b --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/AndroidManifest.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/Album.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/Album.kt new file mode 100644 index 00000000..f34b739a --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/Album.kt @@ -0,0 +1,12 @@ +package com.example.androidfloclone + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "AlbumTable") +data class Album ( + @PrimaryKey(autoGenerate = false) var id: Int = 0, + var title: String? = "", + var singer: String? = "", + var coverImg: Int? = null +) \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AlbumDao.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AlbumDao.kt new file mode 100644 index 00000000..17935293 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AlbumDao.kt @@ -0,0 +1,37 @@ +package com.example.androidfloclone + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update + +@Dao +interface AlbumDao { + @Insert + fun insert(album: Album) + + @Update + fun update(album: Album) + + @Delete + fun delete(album: Album) + + @Query("SELECT * FROM AlbumTable") + fun getAlbums(): List + + @Query("SELECT * FROM AlbumTable WHERE id = :id") + fun getAlbum(id: Int): Album + + @Insert + fun likeAlbum(like: Like) + + @Query("SELECT id FROM LikeTable WHERE userId = :userId AND albumId = :albumId") + fun isLikedAlbum(userId: Int, albumId: Int) : Int? + + @Query("DELETE FROM LikeTable WHERE userId = :userId AND albumId = :albumId") + fun disLikedAlbum(userId: Int, albumId: Int) + + @Query("SELECT AT.* FROM LikeTable as LT LEFT JOIN AlbumTable as AT ON LT.albumId = AT.id WHERE LT.userId = :userId") + fun getLikedAlbums(userId: Int) : List +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AlbumFragment.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AlbumFragment.kt new file mode 100644 index 00000000..ed196535 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AlbumFragment.kt @@ -0,0 +1,105 @@ +package com.example.androidfloclone + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import com.example.androidfloclone.databinding.FragmentAlbumBinding +import com.google.android.material.tabs.TabLayoutMediator +import com.google.gson.Gson + +class AlbumFragment : Fragment() { + lateinit var binding: FragmentAlbumBinding + private var gson: Gson = Gson() + private val information = arrayListOf("수록곡", "상세정보", "영상") + + private var isLiked : Boolean = false + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentAlbumBinding.inflate(inflater,container,false) + + val albumJson = arguments?.getString("album") + val album = gson.fromJson(albumJson, Album::class.java) + isLiked = isLikedAlbum(album.id) + setInit(album) + setOnClickListeners(album) + + binding.albumBackIv.setOnClickListener { + (context as MainActivity).supportFragmentManager.beginTransaction() + .replace(R.id.main_frm,HomeFragment()) + .commitAllowingStateLoss() + } + + // 앨범 어댑터 초기화 + val albumAdapter = AlbumVPAdapter(this) + binding.albumContentVp.adapter = albumAdapter + + TabLayoutMediator(binding.albumContentTb, binding.albumContentVp) { + tap, position -> + tap.text = information[position] + }.attach() + + return binding.root + } + + private fun setInit(album: Album) { + binding.albumAlbumIv.setImageResource(album.coverImg!!) + binding.albumMusicTitleTv.text = album.title.toString() + binding.albumSingerNameTv.text = album.singer.toString() + + if (isLiked) { + binding.albumLikeIv.setImageResource(R.drawable.ic_my_like_on) + } + else { + binding.albumLikeIv.setImageResource(R.drawable.ic_my_like_off) + } + } + + private fun getJwt(): Int { + val spf = activity?.getSharedPreferences("auth", AppCompatActivity.MODE_PRIVATE) + return spf!!.getInt("jwt", 0) + } + + private fun likeAlbum(userId: Int, albumId: Int) { + val songDB = SongDatabase.getInstance(requireContext())!! + val like = Like(userId, albumId) + + songDB.albumDao().likeAlbum(like) + } + + private fun isLikedAlbum(albumId: Int): Boolean { + val songDB = SongDatabase.getInstance(requireContext())!! + val userId = getJwt() + + val likeId : Int? = songDB.albumDao().isLikedAlbum(userId, albumId) + + return likeId != null + } + + private fun disLikedAlbum(albumId: Int) { + val songDB = SongDatabase.getInstance(requireContext())!! + val userId = getJwt() + + songDB.albumDao().disLikedAlbum(userId, albumId) + } + + private fun setOnClickListeners(album: Album) { + val userId = getJwt() + binding.albumLikeIv.setOnClickListener { + if (isLiked) { + binding.albumLikeIv.setImageResource(R.drawable.ic_my_like_off) + disLikedAlbum(album.id) + } + else { + binding.albumLikeIv.setImageResource(R.drawable.ic_my_like_on) + likeAlbum(userId, album.id) + } + } + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AlbumRVAdapter.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AlbumRVAdapter.kt new file mode 100644 index 00000000..f5f8edb3 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AlbumRVAdapter.kt @@ -0,0 +1,68 @@ +package com.example.androidfloclone + +import android.util.Log +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.example.androidfloclone.databinding.ActivitySplashBinding +import com.example.androidfloclone.databinding.ItemAlbumBinding + +class AlbumRVAdapter(private val albumList: ArrayList): RecyclerView.Adapter(){ + + interface MyItemClickListener { + fun onItemClick(album: Album) + fun onPlayImgClick(album: Album) + fun onRemoveAlbum(position: Int) + } + + private lateinit var myItemClickListener: MyItemClickListener + fun setMyItemCLickListener(itemClickListener: MyItemClickListener) { + myItemClickListener = itemClickListener + } + + fun addItem(album: Album) { + albumList.add(album) + notifyDataSetChanged() + } + + fun removeItem(position: Int) { + albumList.removeAt(position) + notifyDataSetChanged() + } + + override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): AlbumRVAdapter.ViewHolder { + val binding: ItemAlbumBinding = ItemAlbumBinding.inflate(LayoutInflater.from(viewGroup.context), viewGroup, false) + + return ViewHolder(binding) + } + + override fun onBindViewHolder(holder: AlbumRVAdapter.ViewHolder, position: Int) { + holder.bind(albumList[position]) + + holder.itemView.setOnClickListener { + Log.d("AlbumRVAdapter", "PlayButton: ${albumList[position].title}") + myItemClickListener.onItemClick(albumList[position]) + } + holder.binding.itemAlbumPlayImgIv.setOnClickListener{ + Log.d("AlbumRVAdapter", "AlbumImg: ${albumList[position].title}") + myItemClickListener.onPlayImgClick(albumList[position]) + } + holder.binding.itemAlbumTitleTv.setOnClickListener { + myItemClickListener.onRemoveAlbum(position) + } + + + } + + override fun getItemCount(): Int = albumList.size + + inner class ViewHolder(val binding: ItemAlbumBinding): RecyclerView.ViewHolder(binding.root) { + + fun bind(album: Album) { + binding.itemAlbumTitleTv.text = album.title + binding.itemAlbumSingerTv.text = album.singer + binding.itemAlbumCoverImgIv.setImageResource(album.coverImg!!) + } + } + +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AlbumVPAdapter.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AlbumVPAdapter.kt new file mode 100644 index 00000000..74ef3352 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AlbumVPAdapter.kt @@ -0,0 +1,25 @@ +package com.example.androidfloclone + +import android.os.Bundle +import androidx.fragment.app.Fragment +import androidx.viewpager2.adapter.FragmentStateAdapter + +class AlbumVPAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) { + // 프래그먼트의 개수를 반환 + override fun getItemCount(): Int = 3 + + // 주어진 위치에 해당하는 프래그먼트를 생성하여 반환 + override fun createFragment(position: Int): Fragment { + return when(position) { + 0 -> SongFragment() + /* private val singer: String? + .apply { + arguments = Bundle().apply { + putString("singer", singer) + } + }*/ + 1 -> DetailFragment() + else -> VideoFragment() + } + } +} diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AuthResponse.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AuthResponse.kt new file mode 100644 index 00000000..94deb9d0 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AuthResponse.kt @@ -0,0 +1,15 @@ +package com.example.androidfloclone + +import com.google.gson.annotations.SerializedName + +data class AuthResponse ( + @SerializedName(value = "isSuccess") val isSuccess:Boolean, + @SerializedName(value = "code") val code:String, + @SerializedName(value = "message") val message:String, + @SerializedName(value = "result") val result: Result? +) + +data class Result( + @SerializedName(value = "userIdx") var memberId : Int, + @SerializedName(value = "jwt") var accessToken : String +) \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AuthRetrofitInterface.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AuthRetrofitInterface.kt new file mode 100644 index 00000000..75c71404 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AuthRetrofitInterface.kt @@ -0,0 +1,13 @@ +package com.example.androidfloclone + +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.POST + +interface AuthRetrofitInterface { + @POST("/join") + fun signUp(@Body user: User): Call + + @POST("/login") + fun login(@Body user: User): Call +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AuthService.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AuthService.kt new file mode 100644 index 00000000..ef31521f --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/AuthService.kt @@ -0,0 +1,59 @@ +package com.example.androidfloclone + +import android.util.Log +import android.view.View +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + +class AuthService { + private lateinit var signUpView: SignUpView + private lateinit var loginView: LoginView + + fun setSignUpView(signUpView: SignUpView) { + this.signUpView = signUpView + } + + fun setLoginView(loginView: LoginView) { + this.loginView = loginView + } + + fun signUp(user: User) { + val authService = getRetrofit().create(AuthRetrofitInterface::class.java) + + authService.signUp(user).enqueue(object: Callback { + override fun onResponse(call: Call, response: Response) { + Log.d("SIGNUP/SUCCESS", response.toString()) + val resp: AuthResponse = response.body()!! + when(resp.code) { + "200" -> signUpView.onSignUpSuccess() + else -> signUpView.onSignUpFailure() + } + } + override fun onFailure(call: Call, t: Throwable) { + Log.d("SIGNUP/FAILURE", t.message.toString()) + } + }) + Log.d("SIGNUP", "HELLO") + } + + fun login(user: User) { + val authService = getRetrofit().create(AuthRetrofitInterface::class.java) + + authService.login(user).enqueue(object: Callback { + override fun onResponse(call: Call, response: Response) { + Log.d("LOGIN/SUCCESS", response.toString()) + val resp: AuthResponse = response.body()!! + when(resp.code) { + "200" -> loginView.onLoginSuccess(true) + else -> loginView.onLoginFailure() + } + } + + override fun onFailure(call: Call, t: Throwable) { + Log.d("LOGIN/FAILURE", t.message.toString()) + } + + }) + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/BannerFragment.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/BannerFragment.kt new file mode 100644 index 00000000..7949e81b --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/BannerFragment.kt @@ -0,0 +1,23 @@ +package com.example.androidfloclone + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.example.androidfloclone.databinding.FragmentBannerBinding + +class BannerFragment(val imgRes : Int) : Fragment() { + lateinit var binding : FragmentBannerBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentBannerBinding.inflate(inflater, container, false) + + binding.bannerImageIv.setImageResource(imgRes) + return binding.root + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/BannerVPAdapter.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/BannerVPAdapter.kt new file mode 100644 index 00000000..9c311b0e --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/BannerVPAdapter.kt @@ -0,0 +1,21 @@ +package com.example.androidfloclone + +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.viewpager2.adapter.FragmentStateAdapter + +class BannerVPAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) { + // 프래그먼트를 저장하는 리스트 + private val fragmentList: ArrayList = ArrayList() + + override fun getItemCount(): Int = fragmentList.size + + // 해당 위치의 프래그먼트를 반환 + override fun createFragment(position: Int): Fragment = fragmentList[position] + + // 프래그먼트를 리스트에 추가하고 새 아이템이 삽입됨을 알림 + fun addFragment(fragment: Fragment) { + fragmentList.add(fragment) + notifyItemInserted(fragmentList.size - 1) + } +} diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/BottomSheetFragment.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/BottomSheetFragment.kt new file mode 100644 index 00000000..46f79874 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/BottomSheetFragment.kt @@ -0,0 +1,39 @@ +package com.example.androidfloclone + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import com.example.androidfloclone.databinding.FragmentBottomSheetBinding +import com.google.android.material.bottomsheet.BottomSheetDialogFragment + + +class BottomSheetFragment : BottomSheetDialogFragment() { + lateinit var binding: FragmentBottomSheetBinding + lateinit var songDB: SongDatabase + private lateinit var savedSongRVAdapter: SavedSongRVAdapter + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentBottomSheetBinding.inflate(inflater, container,false) + songDB = SongDatabase.getInstance(requireContext())!! + savedSongRVAdapter = SavedSongRVAdapter() + + // 바텀 시트에서 삭제 버튼 클릭 시 처리 + binding.bottomSheetDeleteBtn.setOnClickListener { + songDB.songDao().unlikeAllSongs() + + savedSongRVAdapter.removeAllSongs() + + dismiss() // 바텀 시트 닫기 + Toast.makeText(requireContext(), "삭제", Toast.LENGTH_SHORT).show() + } + + return binding.root + } + +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/CustomToast.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/CustomToast.kt new file mode 100644 index 00000000..77df4494 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/CustomToast.kt @@ -0,0 +1,22 @@ +package com.example.androidfloclone + +import android.content.Context +import android.view.LayoutInflater +import android.widget.Toast +import com.example.androidfloclone.databinding.ToastBinding + +object CustomToast { + + fun showToast(context: Context, message: String) { + val inflater = LayoutInflater.from(context) + val binding = ToastBinding.inflate(inflater) + + binding.toastTv.text = message + + Toast(context).apply { + duration = Toast.LENGTH_SHORT + view = binding.root + show() + } + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/DetailFragment.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/DetailFragment.kt new file mode 100644 index 00000000..8a4662b8 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/DetailFragment.kt @@ -0,0 +1,21 @@ +package com.example.androidfloclone + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.example.androidfloclone.databinding.FragmentDetailBinding + +class DetailFragment : Fragment() { + lateinit var binding: FragmentDetailBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentDetailBinding.inflate(inflater, container, false) + return binding.root + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/HomeFragment.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/HomeFragment.kt new file mode 100644 index 00000000..ec33be19 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/HomeFragment.kt @@ -0,0 +1,140 @@ +package com.example.androidfloclone + +import android.os.Bundle +import android.os.DeadObjectException +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.viewpager2.widget.ViewPager2 +import com.example.androidfloclone.databinding.FragmentHomeBinding +import com.google.gson.Gson +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch + +class HomeFragment : Fragment() { + + private lateinit var binding: FragmentHomeBinding + private var job: Job? = null // 자동 슬라이드 Job을 nullable로 변경 + private var albumDatas = ArrayList() // 앨범 데이터를 저장할 ArrayList + private lateinit var songDB: SongDatabase + private val gson = Gson() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentHomeBinding.inflate(inflater, container, false) + + songDB = SongDatabase.getInstance(requireContext())!! + albumDatas.addAll(songDB.albumDao().getAlbums()) + + val albumRVAdapter = AlbumRVAdapter(albumDatas) + binding.homeTodayMusicAlbumRv.adapter = albumRVAdapter + binding.homeTodayMusicAlbumRv.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) + + // 앨범 아이템 클릭 리스너 설정 + albumRVAdapter.setMyItemCLickListener(object: AlbumRVAdapter.MyItemClickListener { + // 앨범 아이템 클릭 시 AlbunFragment로 데이터 전달 + override fun onItemClick(album: Album) { + changeAlbumFragment(album) + } + // 앨범 재생 아이콘 클릭 시, Ids를 액티비티로 전달 + override fun onPlayImgClick(album: Album) { + val mainActivity = requireActivity() as MainActivity + val albumSongs = ArrayList() + val songIds = ArrayList() + + albumSongs.addAll(songDB.songDao().getAlbumSongs(album.id)) + + for (i in 0 until albumSongs.size) { + songIds.add(albumSongs[i].id) + } + + val songIdsJson = gson.toJson(songIds) + mainActivity.albumSongsReceived(songIdsJson) + + /*// SharedPreferences에 저장하여 SongActivity로 전달 + val sharedPreferences = requireActivity().getSharedPreferences("SongPreferences", Context.MODE_PRIVATE) + val editor = sharedPreferences.edit() + editor.putString("songIds", songIdsJson) + editor.apply()*/ + } + // 앨범 아이템 삭제 + override fun onRemoveAlbum(position: Int) { + albumRVAdapter.removeItem(position) + } + }) + + // 배너 ViewPager 어댑터 설정 + val bannerAdapter = BannerVPAdapter(this) + bannerAdapter.addFragment(BannerFragment(R.drawable.img_home_viewpager_exp3)) + bannerAdapter.addFragment(BannerFragment(R.drawable.img_home_viewpager_exp4)) + bannerAdapter.addFragment(BannerFragment(R.drawable.img_home_viewpager_exp3)) + bannerAdapter.addFragment(BannerFragment(R.drawable.img_home_viewpager_exp4)) + binding.homeBannerVp.adapter = bannerAdapter + binding.homeBannerVp.orientation = ViewPager2.ORIENTATION_HORIZONTAL + + // 패널 ViewPager 어댑터 설정 + val panelAdapter = PanelVPAdapter(this) + panelAdapter.addFragment(PanelFragment(R.drawable.img_first_album_default)) + panelAdapter.addFragment(PanelFragment(R.drawable.img_first_album_default)) + panelAdapter.addFragment(PanelFragment(R.drawable.img_first_album_default)) + binding.homePannelBackgroundVp.adapter = panelAdapter + binding.homePannelBackgroundVp.orientation = ViewPager2.ORIENTATION_HORIZONTAL + + // 패널 뷰페이저와 인디케이터 연결 + binding.homePannelIndicator.setViewPager(binding.homePannelBackgroundVp) + + return binding.root + } + + // 앨범 클릭 시 AlbumFragment로 데이터 전달하는 메소드 + private fun changeAlbumFragment(album: Album) { + (context as MainActivity).supportFragmentManager.beginTransaction() + .replace(R.id.main_frm, AlbumFragment().apply { + arguments = Bundle().apply { + val gson = Gson() + val albumJson = gson.toJson(album) + putString("album", albumJson) + } + }) + .commitAllowingStateLoss() + } + + // 자동 슬라이드 기능 + private fun startAutoScroll() { + // 기존 job이 있으면 중단 + stopAutoScroll() + + job = CoroutineScope(Dispatchers.Main).launch { + while (isActive) { + delay(5000) + val nextItem = (binding.homePannelBackgroundVp.currentItem + 1) % + (binding.homePannelBackgroundVp.adapter?.itemCount ?: 1) + binding.homePannelBackgroundVp.setCurrentItem(nextItem, true) + } + } + } + + private fun stopAutoScroll() { + job?.cancel() + job = null + } + + override fun onResume() { + super.onResume() + startAutoScroll() + } + + override fun onPause() { + super.onPause() + stopAutoScroll() + } +} diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/Like.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/Like.kt new file mode 100644 index 00000000..80303f23 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/Like.kt @@ -0,0 +1,12 @@ +package com.example.androidfloclone + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "LikeTable") +class Like ( + var userId : Int, + var albumId : Int +){ + @PrimaryKey(autoGenerate = true) var id: Int = 0 +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/LockerFragment.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/LockerFragment.kt new file mode 100644 index 00000000..26c32508 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/LockerFragment.kt @@ -0,0 +1,82 @@ +package com.example.androidfloclone + +import android.content.Intent +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment +import com.example.androidfloclone.databinding.FragmentLockerBinding +import com.google.android.material.tabs.TabLayoutMediator + +class LockerFragment : Fragment() { + + lateinit var binding: FragmentLockerBinding + + private var information = arrayListOf("저장한곡", "음악파일", "저장앨범") + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentLockerBinding.inflate(inflater, container, false) + + // ViewPager 에 어탭터 연결 + val lockerAdapter = LockerVPAdapter(this) + binding.lockerContentVp.adapter = lockerAdapter + + // 탭 레이아웃과 뷰 페이저를 연결하고 탭 제목 설정 + TabLayoutMediator(binding.lockerContentTb, binding.lockerContentVp) { + tab, position -> + tab.text = information[position] + }.attach() + + val bottomSheetFragment = BottomSheetFragment() + binding.lockerSelectAllTv.setOnClickListener { + bottomSheetFragment.show(requireFragmentManager(), "BottomSheetDialog") + } + + binding.lockerLoginTv.setOnClickListener { + startActivity(Intent(activity, LoginActivity::class.java)) + } + + return binding.root + } + + override fun onStart() { + super.onStart() + initViews() + } + + private fun getJwt(): Int{ + val spf = activity?.getSharedPreferences("auth", AppCompatActivity.MODE_PRIVATE) + return spf!!.getInt("jwt", 0) + } + + private fun initViews() { + val jwt : Int = getJwt() + if (jwt == 0) { + binding.lockerLoginTv.text = "로그인" + binding.lockerLoginTv.setOnClickListener { + startActivity(Intent(activity, LoginActivity::class.java)) + } + } + else { + binding.lockerLoginTv.text = "로그아웃" + binding.lockerLoginTv.setOnClickListener { + logout() + startActivity(Intent(activity, MainActivity::class.java)) + } + } + } + + private fun logout() { + val spf = activity?.getSharedPreferences("auth", AppCompatActivity.MODE_PRIVATE) + val editor = spf!!.edit() + editor.remove("jwt") + editor.apply() + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/LockerVPAdapter.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/LockerVPAdapter.kt new file mode 100644 index 00000000..6e8adf0a --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/LockerVPAdapter.kt @@ -0,0 +1,18 @@ +package com.example.androidfloclone + +import androidx.fragment.app.Fragment +import androidx.viewpager2.adapter.FragmentStateAdapter + +class LockerVPAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) { + // 프래그먼트의 개수를 반환 + override fun getItemCount(): Int = 3 + + // 주어진 위치에 해당하는 프래그먼트를 생성하여 반환 + override fun createFragment(position: Int): Fragment { + return when(position) { + 0 -> SavedSongFragment() + 1 -> MusicFileFragment() + else -> SavedAlbumFragment() + } + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/LoginActivity.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/LoginActivity.kt new file mode 100644 index 00000000..b0c26e57 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/LoginActivity.kt @@ -0,0 +1,91 @@ +package com.example.androidfloclone + +import android.content.Intent +import android.os.Bundle +import android.util.Log +import android.widget.Toast +import androidx.activity.enableEdgeToEdge +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import com.example.androidfloclone.databinding.ActivityLoginBinding + +class LoginActivity : AppCompatActivity(), LoginView { + lateinit var binding: ActivityLoginBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityLoginBinding.inflate(layoutInflater) + setContentView(binding.root) + + binding.loginSignUpTv.setOnClickListener { + startActivity(Intent(this, SignUpActivity::class.java)) + } + + binding.loginSignInBtn.setOnClickListener { + login() + } + } + + private fun login() { + if (binding.loginIdEt.text.toString().isEmpty() || binding.loginDirectInputEt.text.toString().isEmpty()) { + Toast.makeText(this, "이메일을 입력해 주세요.", Toast.LENGTH_SHORT).show() + return + } + + if (binding.loginPasswordEt.text.toString().isEmpty()) { + Toast.makeText(this, "비밀번호를 입력해 주세요", Toast.LENGTH_SHORT).show() + return + } + + val email : String = binding.loginIdEt.text.toString() + "@" + binding.loginDirectInputEt.text.toString() + val pwd : String = binding.loginPasswordEt.text.toString() + +/* val songDB = SongDatabase.getInstance(this)!! + val user = songDB.userDao().getUser(email, pwd) + + user?.let { + Log.d("Login_Act/Get_User", "userId: ${user.id}, $user") + saveJwt(user.id) + startMainActivity() + + return // 로그인 성공 시 함수 종료 + }*/ + + val authService = AuthService() + authService.setLoginView(this) + + authService.login(User(email, pwd, "")) + Toast.makeText(this, "회원 정보가 존재하지 않습니다.", Toast.LENGTH_SHORT).show() + } + +/* private fun saveJwt(jwt: Int) { + val spf = getSharedPreferences("auth", MODE_PRIVATE) + val editor = spf.edit() + editor .putInt("jwt", jwt) + editor.apply() + }*/ + + private fun saveJwt2(jwt: String) { + val spf = getSharedPreferences("auth", MODE_PRIVATE) + val editor = spf.edit() + editor .putString("jwt", jwt) + editor.apply() + } + + private fun startMainActivity() { + val intent = Intent(this, MainActivity::class.java) + startActivity(intent) + } + + override fun onLoginSuccess(isSuccess: Boolean) { + if (isSuccess) { + Toast.makeText(this, "로그인 하였습니다.", Toast.LENGTH_SHORT).show() + startMainActivity() + } + } + + override fun onLoginFailure() { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/LoginView.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/LoginView.kt new file mode 100644 index 00000000..bdf72e20 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/LoginView.kt @@ -0,0 +1,6 @@ +package com.example.androidfloclone + +interface LoginView { + fun onLoginSuccess(isSuccess: Boolean) + fun onLoginFailure() +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/LookFragment.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/LookFragment.kt new file mode 100644 index 00000000..a5f47c06 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/LookFragment.kt @@ -0,0 +1,23 @@ +package com.example.androidfloclone + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.example.androidfloclone.databinding.FragmentLookBinding + +class LookFragment : Fragment() { + + lateinit var binding: FragmentLookBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentLookBinding.inflate(inflater, container, false) + + return binding.root + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/MainActivity.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/MainActivity.kt new file mode 100644 index 00000000..9e205312 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/MainActivity.kt @@ -0,0 +1,323 @@ +package com.example.androidfloclone + +import android.content.Context +import android.content.Intent +import android.media.MediaPlayer +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.util.Log +import android.view.View +import android.widget.Toast +import com.example.androidfloclone.databinding.ActivityMainBinding +import com.google.gson.Gson + + +class MainActivity : AppCompatActivity() { + + lateinit var binding: ActivityMainBinding + val songs = arrayListOf() + lateinit var songDB: SongDatabase + var nowPos = 0 + + private var mediaPlayer: MediaPlayer? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + + inputDummyAlbums() + inputDummySongs() + initPlayList() + initClickListener() + + initBottomNavigation() + + Log.d("Song", songs[nowPos].title + songs[nowPos].singer) + + Log.d("MAIN/JWT_TO_SERVER", getJwt().toString()) + + } + + fun setPlayerStatus(isPlaying : Boolean) { + songs[nowPos].isPlaying = isPlaying + + if (isPlaying) { + binding.mainPauseBtn.visibility = View.VISIBLE + binding.mainMiniplayerBtn.visibility = View.GONE + mediaPlayer?.start() + } + else { + binding.mainPauseBtn.visibility = View.GONE + binding.mainMiniplayerBtn.visibility = View.VISIBLE + if (mediaPlayer?.isPlaying == true) { + mediaPlayer?.pause() + } + } + } + + private fun initBottomNavigation(){ + + supportFragmentManager.beginTransaction() + .replace(R.id.main_frm, HomeFragment()) + .commitAllowingStateLoss() + + binding.mainBnv.setOnItemSelectedListener{ item -> + when (item.itemId) { + + R.id.homeFragment -> { + supportFragmentManager.beginTransaction() + .replace(R.id.main_frm, HomeFragment()) + .commitAllowingStateLoss() + return@setOnItemSelectedListener true + } + + R.id.lookFragment -> { + supportFragmentManager.beginTransaction() + .replace(R.id.main_frm, LookFragment()) + .commitAllowingStateLoss() + return@setOnItemSelectedListener true + } + R.id.searchFragment -> { + supportFragmentManager.beginTransaction() + .replace(R.id.main_frm, SearchFragment()) + .commitAllowingStateLoss() + return@setOnItemSelectedListener true + } + R.id.lockerFragment -> { + supportFragmentManager.beginTransaction() + .replace(R.id.main_frm, LockerFragment()) + .commitAllowingStateLoss() + return@setOnItemSelectedListener true + } + } + false + } + } + + private fun getPlayingSongPosition(songId: Int): Int { + for (i in 0 until songs.size) { + if (songs[i].id == songId) { + return i + } + } + return 0 + } + + private fun initPlayList() { + songDB = SongDatabase.getInstance(this)!! + songs.addAll(songDB.songDao().getSongs()) + } + + private fun initClickListener() { + binding.mainPlayerCl.setOnClickListener { + val editor = getSharedPreferences("song", MODE_PRIVATE).edit() + editor.putInt("songId", songs[nowPos].id) + editor.apply() + + val intent = Intent(this, SongActivity::class.java) + startActivity(intent) + } + + binding.mainPreviousBtn.setOnClickListener { + moveSong(-1) + } + binding.mainNextBtn.setOnClickListener { + moveSong(+1) + } + + binding.mainPauseBtn.setOnClickListener { + setPlayerStatus(false) + } + binding.mainMiniplayerBtn.setOnClickListener { + setPlayerStatus(true) + } + } + + private fun moveSong(direct: Int) { + if (nowPos + direct < 0) { + Toast.makeText(this, "first song", Toast.LENGTH_SHORT).show() + return + } + if (nowPos + direct >= songs.size) { + Toast.makeText(this, "Last song", Toast.LENGTH_SHORT).show() + return + } + + nowPos += direct + + binding.mainProgressSb.progress = 0 + mediaPlayer?.release() + mediaPlayer = null + + setMiniPlayer(songs[nowPos]) + } + + private fun setMiniPlayer(song: Song) { + binding.mainMiniplayerTitleTv.text = song.title + binding.mainMiniplayerSingerTv.text = song.singer + val sharedPreferences = getSharedPreferences("song", MODE_PRIVATE) + val second = sharedPreferences.getInt("second", 0) + binding.mainProgressSb.progress = (second * 100000 / song.playTime) + + val music = resources.getIdentifier(song.music, "raw", this.packageName) + mediaPlayer = MediaPlayer.create(this, music) + + setPlayerStatus(song.isPlaying) + } + + fun albumSongsReceived(jsonData: String) { + val gson = Gson() + val songId = gson.fromJson(jsonData, Array::class.java) + val songIdList: ArrayList = ArrayList(songId.toList()) + + songDB = SongDatabase.getInstance(this)!! + + for(i in 0 until songIdList.size) { + songs.add(songDB.songDao().getSong(songIdList[i])) + } + + nowPos = songs.size - songIdList.size + + setMiniPlayer(songs[nowPos]) + Log.d("AlbumSongs", "Loaded songs: $songs") + + // SharedPreferences에서 기존에 저장된 수록곡 ID 리스트를 불러오기 + val sharedPreferences = getSharedPreferences("SongPreferences", Context.MODE_PRIVATE) + val editor = sharedPreferences.edit() + + val existingIdsJson = sharedPreferences.getString("songIds", "[]") + val existingIdsArray = gson.fromJson(existingIdsJson, Array::class.java) + val existingIdsList = ArrayList(existingIdsArray.toList()) + + // 새로운 앨범의 수록곡을 기존 리스트에 추가 + existingIdsList.addAll(songIdList) + + // 새로운 리스트를 JSON으로 변환하여 저장 + val updatedSongIdsJson = gson.toJson(existingIdsList) + editor.putString("songIds", updatedSongIdsJson) + + // 데이터 저장 + editor.apply() + + Log.d("mainAlbumId", songs.toString()) + } + +/* + fun updateMiniPlayer(album: Album) { + // 앨범의 첫 번째 노래를 선택 + val firstSong = album.songs?.first() + + // 미니 플레이어에 앨범 정보와 첫 번째 노래 정보 업데이트 + if (firstSong != null) { + binding.mainMiniplayerTitleTv.text = firstSong.title + binding.mainMiniplayerSingerTv.text = firstSong.singer + } + binding.mainProgressSb.progress = 0 // 프로그레스바 초기화 + } +*/ + + private fun getJwt(): String? { + val spf = this.getSharedPreferences("auth2", AppCompatActivity.MODE_PRIVATE) + + return spf!!.getString("jwt", "") + } + + override fun onStart() { + super.onStart() + + val sharedPreferences = getSharedPreferences("song", MODE_PRIVATE) + val songId = sharedPreferences.getInt("songId", 0) + + // songId에 해당하는 노래가 있으면 그 노래를 리스트에서 찾아서 해당 위치를 설정 + val songPosition = getPlayingSongPosition(songId) + + if (songPosition >= 0 && songPosition < songs.size) { + nowPos = songPosition + } else { + // songId가 없거나 잘못된 경우 기본 첫 번째 노래 설정 + nowPos = 0 + } + + // 선택된 노래로 미니 플레이어 업데이트 + setMiniPlayer(songs[nowPos]) + + Log.d("song ID", songs[nowPos].id.toString()) + } + + private fun inputDummyAlbums() { + val songDB = SongDatabase.getInstance(this)!! + val albums = songDB.albumDao().getAlbums() + + if (albums.isNotEmpty()) return + + songDB.albumDao().insert( + Album(1, "The Volunteers", "The Volunteers (더 발룬티어스)", R.drawable.img_album_exp2) + ) + songDB.albumDao().insert( + Album(2, "OO-LI", "WOODZ", R.drawable.img_album_exp4) + ) + songDB.albumDao().insert( + Album(3,"SUPER REAL ME", "아일릿 (ILLIT)", R.drawable.img_album_exp5) + ) + songDB.albumDao().insert( + Album(4, "Butter", "방탄소년단 (BTS)", R.drawable.img_album_exp) + ) + songDB.albumDao().insert( + Album(5, "Next Level", "에스파 (AESPA)", R.drawable.img_album_exp3) + ) + songDB.albumDao().insert( + Album(6, "Weekend", "태연 (Tae Yeon)", R.drawable.img_album_exp6) + ) + } + + private fun inputDummySongs() { + val songDB = SongDatabase.getInstance(this)!! + val songs = songDB.songDao().getSongs() + + if (songs.isNotEmpty()) return + + songDB.songDao().insert( + Song("S.A.D", "The Volunteers", 0, 60, false, "music_sad", R.drawable.img_album_exp2, false, 1) + ) + songDB.songDao().insert( + Song("Summer", "The Volunteers", 0, 60, false, "music_sad", R.drawable.img_album_exp2, false, 1) + ) + songDB.songDao().insert( + Song("Journey", "WOODZ", 0, 60, false, "music_journey", R.drawable.img_album_exp4, false, 2) + ) + songDB.songDao().insert( + Song("Drowning", "WOODZ", 0, 60, false, "music_journey", R.drawable.img_album_exp4, false, 2) + ) + songDB.songDao().insert( + Song("My World", "아일릿 (ILLIT)", 0, 60, false, "music_myworld", R.drawable.img_album_exp5, false, 3) + ) + songDB.songDao().insert( + Song("Busted", "WOODZ", 0, 60, false, "music_journey", R.drawable.img_album_exp4, false, 2) + ) + songDB.songDao().insert( + Song("Lucky Girl Syndrome", "아일릿 (ILLIT)", 0, 60, false, "music_myworld", R.drawable.img_album_exp5, false, 3) + ) + songDB.songDao().insert( + Song("Midnight Fiction", "아일릿 (ILLIT)", 0, 60, false, "music_myworld", R.drawable.img_album_exp5, false, 3) + ) + songDB.songDao().insert( + Song("Butter", "방탄소년단 (BTS)", 0, 60, false, "music_butter", R.drawable.img_album_exp, false, 4) + ) + songDB.songDao().insert( + Song("Magnetic", "아일릿 (ILLIT)", 0, 60, false, "music_myworld", R.drawable.img_album_exp5, false, 3) + ) + songDB.songDao().insert( + Song("Next Level", "에스파 (AESPA)", 0, 60, false, "music_nextlevel", R.drawable.img_album_exp3, false, 5) + ) + songDB.songDao().insert( + Song("Weekend", "태연 (Tae Yeon)", 0, 60, false, "music_weekend", R.drawable.img_album_exp6, false, 6) + ) + songDB.songDao().insert( + Song("Violet", "The Volunteers", 0, 60, false, "music_sad", R.drawable.img_album_exp2, false, 1) + ) + val songDBData = songDB.songDao().getSongs() + Log.d("DB data", songDBData.toString()) + } + +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/MusicFileFragment.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/MusicFileFragment.kt new file mode 100644 index 00000000..72bfc7e1 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/MusicFileFragment.kt @@ -0,0 +1,24 @@ +package com.example.androidfloclone + +import android.os.Binder +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.example.androidfloclone.databinding.FragmentMusicFileBinding + +class MusicFileFragment : Fragment() { + + lateinit var binding: FragmentMusicFileBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentMusicFileBinding.inflate(inflater, container, false) + + return binding.root + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/MusicService.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/MusicService.kt new file mode 100644 index 00000000..ef7057ee --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/MusicService.kt @@ -0,0 +1,77 @@ +package com.example.androidfloclone + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.app.Service +import android.content.Intent +import android.os.Build +import android.os.IBinder +import android.util.Log +import androidx.annotation.RequiresApi + +class MusicService : Service() { + + companion object { + private const val TAG = "MusicService" + } + + override fun onCreate() { + super.onCreate() + try { + createNotificationChannel() + } catch (e: Exception) { + Log.e(TAG, "Error creating notification channel: ${e.message}") + } + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + Log.d(TAG, "Service started") + + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + showNotification() + } + } catch (e: Exception) { + Log.e(TAG, "Error showing notification: ${e.message}") + } + + return START_STICKY + } + + override fun onBind(intent: Intent): IBinder? = null + + @RequiresApi(Build.VERSION_CODES.O) + private fun showNotification() { + // 알림을 클릭했을 때 이동할 화면을 지정한다. + val notificationIntent = Intent(this, SongActivity::class.java) + val pendingIntent = PendingIntent.getActivity( + this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE // FLAG_IMMUTABLE 추가 + ) + + val notification = Notification + .Builder(this, NotificationConstants.CHANNEL_ID) + .setContentTitle("음악 재생 중") + .setContentText("현재 음악이 재생 중입니다.") + .setSmallIcon(android.R.drawable.ic_media_play) + .setContentIntent(pendingIntent) + .build() + + startForeground(NotificationConstants.NOTIFICATION_ID, notification) + Log.d(TAG, "Foreground service started with notification") + } + + private fun createNotificationChannel() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val serviceChannel = NotificationChannel( + NotificationConstants.CHANNEL_ID, "Music Playback", + NotificationManager.IMPORTANCE_DEFAULT // IMPORTANCE_DEFAULT로 변경 + ) + + val manager = getSystemService(NotificationManager::class.java) + manager?.createNotificationChannel(serviceChannel) + Log.d(TAG, "Notification channel created") + } + } +} diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/NetworkModule.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/NetworkModule.kt new file mode 100644 index 00000000..d38aa5e3 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/NetworkModule.kt @@ -0,0 +1,14 @@ +package com.example.androidfloclone + +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +const val BASE_URL = "http://3.35.121.185" + +fun getRetrofit(): Retrofit +{ + val retrofit = Retrofit.Builder().baseUrl(BASE_URL) + .addConverterFactory(GsonConverterFactory.create()).build() + + return retrofit +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/NotificationConstants.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/NotificationConstants.kt new file mode 100644 index 00000000..e2dabf66 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/NotificationConstants.kt @@ -0,0 +1,6 @@ +package com.example.androidfloclone + +object NotificationConstants { + const val CHANNEL_ID = "MusicServiceChannel" + const val NOTIFICATION_ID = 1 +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/PanelFragment.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/PanelFragment.kt new file mode 100644 index 00000000..9aaf5d46 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/PanelFragment.kt @@ -0,0 +1,23 @@ +package com.example.androidfloclone + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.example.androidfloclone.databinding.FragmentPanelBinding + +class PanelFragment(val imgRes : Int) : Fragment() { + lateinit var binding : FragmentPanelBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentPanelBinding.inflate(inflater, container, false) + + binding.homePanelBackgroundIv.setImageResource(imgRes) + return binding.root + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/PanelVPAdapter.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/PanelVPAdapter.kt new file mode 100644 index 00000000..b9e1154c --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/PanelVPAdapter.kt @@ -0,0 +1,20 @@ +package com.example.androidfloclone + +import androidx.fragment.app.Fragment +import androidx.viewpager2.adapter.FragmentStateAdapter + +class PanelVPAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) { + // 프래그먼트를 저장하는 리스트 + private val fragmentList: ArrayList = ArrayList() + + override fun getItemCount(): Int = fragmentList.size + + // 해당 위치의 프래그먼트를 반환 + override fun createFragment(position: Int): Fragment = fragmentList[position] + + // 프래그먼트를 리스트에 추가하고 새 아이템이 삽입됨을 알림 + fun addFragment(fragment: Fragment) { + fragmentList.add(fragment) + notifyItemInserted(fragmentList.size - 1) + } +} diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SavedAlbumFragment.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SavedAlbumFragment.kt new file mode 100644 index 00000000..8e0089cd --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SavedAlbumFragment.kt @@ -0,0 +1,57 @@ +package com.example.androidfloclone + +import android.os.Bundle +import android.util.Log +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.recyclerview.widget.LinearLayoutManager +import com.example.androidfloclone.databinding.FragmentSavedAlbumBinding + +class SavedAlbumFragment : Fragment() { + lateinit var binding: FragmentSavedAlbumBinding + lateinit var albumDB: SongDatabase + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentSavedAlbumBinding.inflate(inflater, container, false) + + albumDB = SongDatabase.getInstance(requireContext())!! + + return binding.root + } + + override fun onStart() { + super.onStart() + initRecyclerview() + } + + private fun initRecyclerview(){ + binding.lockerSavedSongRecyclerView.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) + + val albumRVAdapter = SavedAlbumRVAdapter() + + albumRVAdapter.setMyItemClickListener(object : SavedAlbumRVAdapter.MyItemClickListener{ + override fun onRemoveSong(songId: Int) { + albumDB.albumDao().getLikedAlbums(getJwt()) + } + }) + + binding.lockerSavedSongRecyclerView.adapter = albumRVAdapter + + albumRVAdapter.addAlbums(albumDB.albumDao().getLikedAlbums(getJwt()) as ArrayList) + } + + private fun getJwt() : Int { + val spf = activity?.getSharedPreferences("auth" , AppCompatActivity.MODE_PRIVATE) + val jwt = spf!!.getInt("jwt", 0) + Log.d("MAIN_ACT/GET_JWT", "jwt_token: $jwt") + + return jwt + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SavedAlbumRVAdapter.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SavedAlbumRVAdapter.kt new file mode 100644 index 00000000..8f4d80cb --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SavedAlbumRVAdapter.kt @@ -0,0 +1,59 @@ +package com.example.androidfloclone + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.example.androidfloclone.databinding.ItemLockerAlbumBinding + +class SavedAlbumRVAdapter (): RecyclerView.Adapter() { + private val albums = ArrayList() + + interface MyItemClickListener{ + fun onRemoveSong(songId: Int) + } + + private lateinit var mItemClickListener: MyItemClickListener + + fun setMyItemClickListener(itemClickListener: MyItemClickListener){ + mItemClickListener = itemClickListener + } + + override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): SavedAlbumRVAdapter.ViewHolder { + val binding: ItemLockerAlbumBinding = ItemLockerAlbumBinding.inflate(LayoutInflater.from(viewGroup.context), viewGroup, false) + + return ViewHolder(binding) + } + + override fun onBindViewHolder(holder: SavedAlbumRVAdapter.ViewHolder, position: Int) { + holder.bind(albums[position]) + holder.binding.itemAlbumMoreIv.setOnClickListener { + mItemClickListener.onRemoveSong(albums[position].id) + removeSong(position) + } + } + + override fun getItemCount(): Int = albums.size + + @SuppressLint("NotifyDataSetChanged") + fun addAlbums(albums: ArrayList) { + this.albums.clear() + this.albums.addAll(albums) + + notifyDataSetChanged() + } + + fun removeSong(position: Int){ + albums.removeAt(position) + notifyDataSetChanged() + } + + inner class ViewHolder(val binding: ItemLockerAlbumBinding) : RecyclerView.ViewHolder(binding.root){ + fun bind(album: Album){ + binding.itemAlbumImgIv.setImageResource(album.coverImg!!) + binding.itemAlbumTitleTv.text = album.title + binding.itemAlbumSingerTv.text = album.singer + } + } + +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SavedSongFragment.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SavedSongFragment.kt new file mode 100644 index 00000000..013e1c62 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SavedSongFragment.kt @@ -0,0 +1,45 @@ +package com.example.androidfloclone + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import com.example.androidfloclone.databinding.FragmentSavedSongBinding +import com.google.gson.Gson + +class SavedSongFragment : Fragment() { + lateinit var binding: FragmentSavedSongBinding + lateinit var songDB: SongDatabase + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentSavedSongBinding.inflate(inflater, container, false) + + songDB = SongDatabase.getInstance(requireContext())!! + + return binding.root + } + + override fun onStart() { + super.onStart() + initRecyclerview() + } + + private fun initRecyclerview() { + binding.savedSongAlbumRv.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) + val songRVAdapter = SavedSongRVAdapter() + songRVAdapter.setMyItemClickListener(object : SavedSongRVAdapter.MyItemClickListener { + override fun onRemoveSong(songId: Int) { + songDB.songDao().updateIsLikeMyId(false, songId) + } + + }) + binding.savedSongAlbumRv.adapter = songRVAdapter + songRVAdapter.addSongs(songDB.songDao().getLikedSong(true) as ArrayList) + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SavedSongRVAdapter.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SavedSongRVAdapter.kt new file mode 100644 index 00000000..3a6b9f3c --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SavedSongRVAdapter.kt @@ -0,0 +1,84 @@ +package com.example.androidfloclone + +import android.annotation.SuppressLint +import android.util.SparseBooleanArray +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.util.getOrDefault +import androidx.core.util.set +import androidx.recyclerview.widget.RecyclerView +import com.example.androidfloclone.databinding.ItemSavedSongBinding + +class SavedSongRVAdapter(): RecyclerView.Adapter() { + private val songs = ArrayList() + private val switchStatus = SparseBooleanArray() + + interface MyItemClickListener { + fun onRemoveSong(songId: Int) + } + + private lateinit var myItemClickListener: MyItemClickListener + + fun setMyItemClickListener(itemClickListener: MyItemClickListener) { + myItemClickListener = itemClickListener + } + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): SavedSongRVAdapter.ViewHolder { + val binding: ItemSavedSongBinding = ItemSavedSongBinding.inflate(LayoutInflater.from(parent.context), parent, false) + + return ViewHolder(binding) + } + + override fun onBindViewHolder(holder: SavedSongRVAdapter.ViewHolder, position: Int) { + holder.bind(songs[position]) + holder.binding.itemSavedSongMoreIv.setOnClickListener { + myItemClickListener.onRemoveSong(songs[position].id) + removeSongs(position) + } + + val switch = holder.binding.itemSavedSongSwitch + // switch 상태 초기화: Map에서 가져오기, 기본값은 false + switch.isChecked = switchStatus.getOrDefault(position, false) + + switch.setOnClickListener { + // 스위치 상태 변경 + switchStatus[position] = switch.isChecked + // 상태가 변경된 항목 갱신 + notifyItemChanged(position) + } + } + + @SuppressLint("NotifyDataSetChanged") + fun addSongs(songs: ArrayList) { + this.songs.clear() + this.songs.addAll(songs) + + notifyDataSetChanged() + } + + @SuppressLint("NotifyDataSetChanged") + fun removeSongs(position: Int){ + songs.removeAt(position) + notifyDataSetChanged() + } + + @SuppressLint("NotifyDataSetChanged") + fun removeAllSongs() { + songs.clear() // 모든 노래 제거 + notifyDataSetChanged() // UI 업데이트 + } + + override fun getItemCount(): Int = songs.size + + inner class ViewHolder(val binding: ItemSavedSongBinding): RecyclerView.ViewHolder(binding.root) { + + fun bind(album: Song) { + binding.itemSavedSongTitleTv.text = album.title + binding.itemSavedSongSingerTv.text = album.singer + binding.itemSavedSongAlbumCoverImgIv.setImageResource(album.coverImg!!) + } + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SearchFragment.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SearchFragment.kt new file mode 100644 index 00000000..2e461fba --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SearchFragment.kt @@ -0,0 +1,23 @@ +package com.example.androidfloclone + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.example.androidfloclone.databinding.FragmentSearchBinding + +class SearchFragment : Fragment() { + + lateinit var binding: FragmentSearchBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentSearchBinding.inflate(inflater, container, false) + + return binding.root + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SignUpActivity.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SignUpActivity.kt new file mode 100644 index 00000000..5c200670 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SignUpActivity.kt @@ -0,0 +1,83 @@ +package com.example.androidfloclone + +import android.os.Bundle +import android.util.Log +import android.view.View +import android.widget.Toast +import androidx.activity.enableEdgeToEdge +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import com.example.androidfloclone.databinding.ActivityLoginBinding +import com.example.androidfloclone.databinding.ActivitySignUpBinding +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + + +class SignUpActivity : AppCompatActivity(), SignUpView { + + lateinit var binding: ActivitySignUpBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivitySignUpBinding.inflate(layoutInflater) + setContentView(binding.root) + + binding.signUpSignUpBtn.setOnClickListener { + signUp() + } + } + + private fun getUser() : User { + val email : String = binding.signUpIdEt.text.toString() + "@" + binding.signUpDirectInputEt.text.toString() + val pwd : String = binding.signUpPasswordEt.text.toString() + val name : String = binding.signUpNameEt.text.toString() + + return User(email, pwd, name) + } + +/* private fun signUp() { + if (binding.signUpIdEt.text.toString().isEmpty() || binding.signUpDirectInputEt.text.toString().isEmpty()) { + Toast.makeText(this, "이메일 형식이 잘못되었습니다.", Toast.LENGTH_SHORT).show() + return + } + if (binding.signUpPasswordEt.text.toString() != binding.signUpPasswordCheckEt.text.toString()) { + Toast.makeText(this, "비밀번호가 일치하지 않습니다.", Toast.LENGTH_SHORT).show() + return + } + val userDB = SongDatabase.getInstance(this)!! + userDB.userDao().insert(getUser()) + + val user = userDB.userDao().getUsers() + Log.d("SignUpAct", user.toString()) + }*/ + + private fun signUp() { + if (binding.signUpIdEt.text.toString().isEmpty() || binding.signUpDirectInputEt.text.toString().isEmpty()) { + Toast.makeText(this, "이메일 형식이 잘못되었습니다.", Toast.LENGTH_SHORT).show() + return + } + if (binding.signUpNameEt.text.toString().isEmpty()) { + Toast.makeText(this, "이름 형식이 잘못되었습니다.", Toast.LENGTH_SHORT).show() + return + } + if (binding.signUpPasswordEt.text.toString() != binding.signUpPasswordCheckEt.text.toString()) { + Toast.makeText(this, "비밀번호가 일치하지 않습니다.", Toast.LENGTH_SHORT).show() + return + } + + val authService = AuthService() + authService.setSignUpView(this) + + authService.signUp(getUser()) + } + + override fun onSignUpSuccess() { + finish() + } + + override fun onSignUpFailure() { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SignUpView.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SignUpView.kt new file mode 100644 index 00000000..e4c8aa24 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SignUpView.kt @@ -0,0 +1,6 @@ +package com.example.androidfloclone + +interface SignUpView { + fun onSignUpSuccess() + fun onSignUpFailure() +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/Song.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/Song.kt new file mode 100644 index 00000000..cc97939e --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/Song.kt @@ -0,0 +1,20 @@ +package com.example.androidfloclone + +import androidx.room.Entity +import androidx.room.PrimaryKey + + +@Entity(tableName = "SongTable") +data class Song( + var title: String = "", + var singer: String = "", + var second: Int = 0, + var playTime: Int = 0, + var isPlaying: Boolean = false, + var music: String = "", + var coverImg: Int? = null, + var isLike: Boolean = false, + var albumIdx : Int = 0 +){ + @PrimaryKey(autoGenerate = true) var id: Int = 0 +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SongActivity.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SongActivity.kt new file mode 100644 index 00000000..5e33c2b4 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SongActivity.kt @@ -0,0 +1,316 @@ +package com.example.androidfloclone + +import android.app.ActivityManager +import android.content.Context +import android.content.Intent +import android.media.MediaPlayer +import android.os.Bundle +import android.util.Log +import android.view.View +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import com.example.androidfloclone.databinding.ActivitySongBinding +import com.google.gson.Gson + +class SongActivity : AppCompatActivity() { + lateinit var binding: ActivitySongBinding + lateinit var timer: Timer + private var mediaPlayer: MediaPlayer? = null + + val songs = arrayListOf() + lateinit var songDB: SongDatabase + var nowPos = 0 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivitySongBinding.inflate(layoutInflater) + setContentView(binding.root) + + initPlayList() + initSong() + initClickListener() + + albumSongsReceived() + } + + private fun albumSongsReceived() { + val sharedPreferences = getSharedPreferences("SongPreferences", Context.MODE_PRIVATE) + val songIdsJson = sharedPreferences.getString("songIds", null) + + songIdsJson?.let { + val gson = Gson() + + // songIdsJson을 리스트로 변환 + val songIdArray = gson.fromJson(it, Array::class.java) + val songIdList: ArrayList = ArrayList(songIdArray.toList()) + + songDB = SongDatabase.getInstance(this)!! + + // songIdList에 있는 각 곡을 songs 리스트에 추가 + for (id in songIdList) { + songs.add(songDB.songDao().getSong(id)) + } + + Log.d("AlbumSongs", "Loaded songs: $songs") + } + } + + private fun initPlayList() { + songDB = SongDatabase.getInstance(this)!! + songs.addAll(songDB.songDao().getSongs()) + } + + private fun initClickListener() { + binding.songDownIb.setOnClickListener { + finish() + } + + binding.songMiniplayerIv.setOnClickListener { + setPlayerStatus(true) + // startStopService() + } + binding.songPauseIv.setOnClickListener { + setPlayerStatus(false) + // startStopService() + } + + // 랜덤 재생 버튼 이미지 변경 + binding.songRandomIv.setOnClickListener { + binding.songRandomIv.visibility = View.GONE + binding.songRandomActiveIv.visibility = View.VISIBLE + } + binding.songRandomActiveIv.setOnClickListener { + binding.songRandomIv.visibility = View.VISIBLE + binding.songRandomActiveIv.visibility = View.GONE + } + + // 반복 재생 이미지 변경 + binding.songRepeatIv.setOnClickListener { + binding.songRepeatIv.visibility = View.GONE + binding.songRepeatActiveIv.visibility = View.VISIBLE + binding.songRepeatOneIv.visibility = View.GONE + } + binding.songRepeatActiveIv.setOnClickListener { + binding.songRepeatIv.visibility = View.GONE + binding.songRepeatActiveIv.visibility = View.GONE + binding.songRepeatOneIv.visibility = View.VISIBLE + } + binding.songRepeatOneIv.setOnClickListener { + binding.songRepeatIv.visibility = View.VISIBLE + binding.songRepeatActiveIv.visibility = View.GONE + binding.songRepeatOneIv.visibility = View.GONE + } + + binding.songNextIv.setOnClickListener { + moveSong(+1) + } + binding.songPreviousIv.setOnClickListener { + moveSong(-1) + } + + binding.songLikeIv.setOnClickListener { + setLike(songs[nowPos].isLike) + } + } + + private fun initSong() { + val spf = getSharedPreferences("song", MODE_PRIVATE) + val songId = spf.getInt("songId", 0) + nowPos = getPlayingSongPosition(songId) + + startTimer() + setPlayer(songs[nowPos]) + } + + private fun setLike(isLike: Boolean) { + songs[nowPos].isLike = !isLike + songDB.songDao().updateIsLikeMyId(!isLike, songs[nowPos].id) + + if (!isLike) { + binding.songLikeIv.setImageResource(R.drawable.ic_my_like_on) + CustomToast.showToast(this, "좋아요 한 곡에 담겼습니다.") + } + else { + binding.songLikeIv.setImageResource(R.drawable.ic_my_like_off) + CustomToast.showToast(this, "좋아요 한 곡이 취소되었습니다.") + } + } + + private fun moveSong(direct: Int) { + if (nowPos + direct < 0) { + Toast.makeText(this, "first song", Toast.LENGTH_SHORT).show() + return + } + if (nowPos + direct >= songs.size) { + Toast.makeText(this, "Last song", Toast.LENGTH_SHORT).show() + return + } + + nowPos += direct + + timer.interrupt() + startTimer() + + mediaPlayer?.release() + mediaPlayer = null + + setPlayer(songs[nowPos]) + } + + private fun getPlayingSongPosition(songId: Int): Int { + for (i in 0 until songs.size) { + if (songs[i].id == songId) { + return i + } + } + return 0 + } + + private fun setPlayer(song: Song) { + binding.songMusicTitleTv.text = song.title + binding.songSingerNameTv.text = song.singer + binding.songStartTimeTv.text = String.format("%02d:%02d", song.second / 60, song.second % 60) + binding.songEndTimeTv.text = String.format("%02d:%02d", song.playTime / 60, song.playTime % 60) + binding.songAlbumIv.setImageResource(song.coverImg!!) + binding.songProgressSb.progress = (song.second * 1000 / song.playTime) + + val music = resources.getIdentifier(song.music, "raw", this.packageName) + mediaPlayer = MediaPlayer.create(this, music) + + if (song.isLike) { + binding.songLikeIv.setImageResource(R.drawable.ic_my_like_on) + } + else { + binding.songLikeIv.setImageResource(R.drawable.ic_my_like_off) + } + + setPlayerStatus(song.isPlaying) + } + + // 플레이 상태를 설정하는 함수 + fun setPlayerStatus(isPlaying : Boolean) { + songs[nowPos].isPlaying = isPlaying + timer.isPlaying = isPlaying + + if (isPlaying) { + binding.songMiniplayerIv.visibility = View.GONE + binding.songPauseIv.visibility = View.VISIBLE + mediaPlayer?.start() + } + else { + binding.songMiniplayerIv.visibility = View.VISIBLE + binding.songPauseIv.visibility = View.GONE + if (mediaPlayer?.isPlaying == true) { + mediaPlayer?.pause() + } + } + } + + private fun startTimer() { + timer = Timer(songs[nowPos].playTime, songs[nowPos].isPlaying) + timer.start() + } + + inner class Timer(private val playTime: Int, var isPlaying: Boolean = true): Thread() { + private var second : Int = 0 + private var mills : Float = 0f + + override fun run() { + super.run() + try { + while (true) { + if (second >= playTime) { + if (binding.songRepeatOneIv.visibility == View.VISIBLE) { + // 한곡 재생 모드일 때만 시간과 SeekBar 초기화 + second = 0 + mills = 0f + runOnUiThread { + binding.songStartTimeTv.text = String.format("%02d:%02d", second / 60, second % 60) + binding.songProgressSb.progress = 0 + } + // 노래를 처음으로 되돌림 + mediaPlayer?.seekTo(0) + mediaPlayer?.start() // 처음부터 재생 시작 + + } else { + mediaPlayer?.release() // 미디어 플레이어가 갖고 있던 리소스 해제 + mediaPlayer = null // 미디어 플레이어 해제 + break // 한곡 재생 모드가 아닐 때 타이머 종료 + } + } + + if (isPlaying) { + sleep(50) + mills += 50 + + runOnUiThread { + binding.songProgressSb.progress = ((mills / playTime) * 100).toInt() + } + + if (mills % 1000 == 0f) { + runOnUiThread { + binding.songStartTimeTv.text = String.format("%02d:%02d", second / 60, second % 60) + } + second++ + } + } + } + } catch (e: InterruptedException) { + Log.d("Song", "스레드가 중지되었습니다: ${e.message}") + } + } + } + // 사용자가 포커스를 잃었을 때 음악이 중지 + override fun onPause() { + super.onPause() + // startStopService() + setPlayerStatus(false) + + songs[nowPos].isPlaying = false + + songs[nowPos].second = ((binding.songProgressSb.progress * songs[nowPos].playTime)/100)/1000 + + val sharedPreferences = getSharedPreferences("song", MODE_PRIVATE) + val editor = sharedPreferences.edit() + editor.putInt("songId", songs[nowPos].id) + editor.putInt("second", songs[nowPos].second) + editor.apply() + } + override fun onDestroy() { + super.onDestroy() + timer.interrupt() + mediaPlayer?.release() // 미디어 플레이어가 갖고 있던 리소스 해제 + mediaPlayer = null // 미디어 플레이어 해제 + } +/* + private fun startStopService() { + if (isServiceRunning(MusicService::class.java)) { // 서비스가 실행 중인지 확인 + Toast.makeText(this, "Foreground Service Stopped", Toast.LENGTH_SHORT).show() + val stopped = stopService(Intent(this, MusicService::class.java)) + if (!stopped) { + Toast.makeText(this, "Failed to stop Foreground Service", Toast.LENGTH_SHORT).show() + } + } else { + Toast.makeText(this, "Foreground Service Started", Toast.LENGTH_SHORT).show() + val startedIntent = Intent(this, MusicService::class.java) + val started = startService(startedIntent) != null + if (!started) { + Toast.makeText(this, "Failed to start Foreground Service", Toast.LENGTH_SHORT).show() + } + } + } + + private fun isServiceRunning(inputClass : Class) : Boolean { + val manager : ActivityManager = getSystemService( + Context.ACTIVITY_SERVICE + ) as ActivityManager + + for (service : ActivityManager.RunningServiceInfo in manager.getRunningServices(Integer.MAX_VALUE)) { + if (inputClass.name.equals(service.service.className)) { + return true + } + } + return false + } +*/ +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SongDao.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SongDao.kt new file mode 100644 index 00000000..b930b0f4 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SongDao.kt @@ -0,0 +1,37 @@ +package com.example.androidfloclone + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update + +@Dao +interface SongDao { + @Insert + fun insert(song: Song) + + @Update + fun update(song: Song) + + @Delete + fun delete(song: Song) + + @Query("SELECT * FROM SongTable") + fun getSongs(): List + + @Query("SELECT * FROM SongTable WHERE id = :id") + fun getSong(id: Int): Song + + @Query("UPDATE songtable SET isLike = :isLike WHERE id = :id") + fun updateIsLikeMyId(isLike: Boolean, id: Int) + + @Query("UPDATE SongTable SET isLike = 0 WHERE isLike = 1") + fun unlikeAllSongs() + + @Query("SELECT * FROM SongTable WHERE isLike = :isLike") + fun getLikedSong(isLike: Boolean): List + + @Query("SELECT * FROM SongTable WHERE albumIdx = :albumIdx") + fun getAlbumSongs(albumIdx: Int): List +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SongDatabase.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SongDatabase.kt new file mode 100644 index 00000000..fd94a43a --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SongDatabase.kt @@ -0,0 +1,31 @@ +package com.example.androidfloclone + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase + +@Database(entities = [Song::class, Album::class, User::class, Like::class], version = 1) +abstract class SongDatabase: RoomDatabase() { + abstract fun albumDao(): AlbumDao + abstract fun songDao(): SongDao + abstract fun userDao(): UserDao + + companion object { + private var instance: SongDatabase? = null + + @Synchronized + fun getInstance(context: Context): SongDatabase? { + if (instance == null) { + synchronized(SongDatabase::class) { + instance = Room.databaseBuilder( + context.applicationContext, + SongDatabase::class.java, + "song-database" + ).allowMainThreadQueries().build() + } + } + return instance + } + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SongFragment.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SongFragment.kt new file mode 100644 index 00000000..c1e207a0 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SongFragment.kt @@ -0,0 +1,54 @@ +package com.example.androidfloclone + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.example.androidfloclone.databinding.FragmentSongBinding + +class SongFragment : Fragment() { + lateinit var binding: FragmentSongBinding + private lateinit var singer: String + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentSongBinding.inflate(inflater, container, false) + + singer = arguments?.getString("singer") ?: "Unknown Singer" + + // 가수 이름을 여러 TextView에 설정하기 + val singerTextViews = listOf( + binding.songSingerName01Tv, + binding.songSingerName02Tv, + binding.songSingerName03Tv, + binding.songSingerName04Tv, + binding.songSingerName05Tv, + binding.songSingerName06Tv, + binding.songSingerName07Tv, + binding.songSingerName08Tv, + binding.songSingerName09Tv, + binding.songSingerName10Tv + ) + + // 내 취향 버튼 이미지 변경 + binding.songMixoffTg.setOnClickListener { + binding.songMixoffTg.visibility = View.GONE + binding.songMixonTg.visibility = View.VISIBLE + } + binding.songMixonTg.setOnClickListener { + binding.songMixoffTg.visibility = View.VISIBLE + binding.songMixonTg.visibility = View.GONE + } + + // 각 TextView에 가수 이름 설정 + singerTextViews.forEach { textView -> + textView.text = singer + } + + return binding.root + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SplashActivity.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SplashActivity.kt new file mode 100644 index 00000000..86543a71 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/SplashActivity.kt @@ -0,0 +1,28 @@ +package com.example.androidfloclone + +import android.content.Intent +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import androidx.activity.enableEdgeToEdge +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import com.example.androidfloclone.databinding.ActivitySplashBinding + +class SplashActivity : AppCompatActivity() { + + lateinit var binding: ActivitySplashBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivitySplashBinding.inflate(layoutInflater) + setContentView(binding.root) + + val handler = Handler(Looper.getMainLooper()) + handler.postDelayed({ + startActivity(Intent(this, MainActivity::class.java)) + finish() // 스플래시 액티비티 종료 + }, 2000) + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/User.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/User.kt new file mode 100644 index 00000000..7cb68008 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/User.kt @@ -0,0 +1,14 @@ +package com.example.androidfloclone + +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.google.gson.annotations.SerializedName + +@Entity(tableName = "UserTable") +class User ( + @SerializedName(value = "email") var email: String, + @SerializedName(value = "password") var password : String, + @SerializedName(value = "name") var name : String +){ + @PrimaryKey(autoGenerate = true) var id : Int = 0 +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/UserDao.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/UserDao.kt new file mode 100644 index 00000000..da795316 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/UserDao.kt @@ -0,0 +1,18 @@ +package com.example.androidfloclone + +import android.provider.ContactsContract.CommonDataKinds.Email +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query + +@Dao +interface UserDao { + @Insert + fun insert(user: User) + + @Query("SELECT * FROM UserTable") + fun getUsers() : List + + @Query("SELECT *FROM UserTable WHERE email = :email AND password = :password") + fun getUser(email: String, password: String) : User? +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/VideoFragment.kt b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/VideoFragment.kt new file mode 100644 index 00000000..315df13c --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/java/com/example/androidfloclone/VideoFragment.kt @@ -0,0 +1,21 @@ +package com.example.androidfloclone + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.example.androidfloclone.databinding.FragmentVideoBinding + +class VideoFragment : Fragment() { + lateinit var binding: FragmentVideoBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentVideoBinding.inflate(inflater, container, false) + return binding.root + } +} \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/apple_44.png b/naru/AndroidFloClone/app/src/main/res/drawable/apple_44.png new file mode 100644 index 00000000..f365d20c Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/apple_44.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/blue_circle.xml b/naru/AndroidFloClone/app/src/main/res/drawable/blue_circle.xml new file mode 100644 index 00000000..f2a12db1 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/drawable/blue_circle.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btm_color_selector.xml b/naru/AndroidFloClone/app/src/main/res/drawable/btm_color_selector.xml new file mode 100644 index 00000000..3ecf8a7a --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/drawable/btm_color_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_actionbar_close.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_actionbar_close.png new file mode 100644 index 00000000..b6cc3cc0 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_actionbar_close.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_actionbar_instagram.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_actionbar_instagram.png new file mode 100644 index 00000000..90bc0277 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_actionbar_instagram.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_arrow_black.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_arrow_black.png new file mode 100644 index 00000000..cc38ca8c Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_arrow_black.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_arrow_more.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_arrow_more.png new file mode 100644 index 00000000..59e410c5 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_arrow_more.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_editbar_addplaylist.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_editbar_addplaylist.png new file mode 100644 index 00000000..a1180c24 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_editbar_addplaylist.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_editbar_delete.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_editbar_delete.png new file mode 100644 index 00000000..3417786e Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_editbar_delete.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_editbar_download.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_editbar_download.png new file mode 100644 index 00000000..f295699e Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_editbar_download.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_editbar_mylist.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_editbar_mylist.png new file mode 100644 index 00000000..fbfc6208 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_editbar_mylist.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_editbar_play.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_editbar_play.png new file mode 100644 index 00000000..c244c212 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_editbar_play.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_input_password.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_input_password.png new file mode 100644 index 00000000..8c2eb186 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_input_password.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_input_password_off.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_input_password_off.png new file mode 100644 index 00000000..8234f538 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_input_password_off.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_main_arrow_more.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_main_arrow_more.png new file mode 100644 index 00000000..59e410c5 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_main_arrow_more.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_main_mike.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_main_mike.png new file mode 100644 index 00000000..9bddec69 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_main_mike.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_main_setting.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_main_setting.png new file mode 100644 index 00000000..7a8d5d6a Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_main_setting.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_main_ticket.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_main_ticket.png new file mode 100644 index 00000000..52b6d64f Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_main_ticket.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplay_mvpause.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplay_mvpause.png new file mode 100644 index 00000000..470e0467 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplay_mvpause.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplay_mvplay.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplay_mvplay.png new file mode 100644 index 00000000..d1186774 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplay_mvplay.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplay_pause.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplay_pause.png new file mode 100644 index 00000000..470e0467 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplay_pause.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplayer_go_list.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplayer_go_list.png new file mode 100644 index 00000000..1b2d9774 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplayer_go_list.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplayer_next.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplayer_next.png new file mode 100644 index 00000000..3aedba3c Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplayer_next.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplayer_play.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplayer_play.png new file mode 100644 index 00000000..f6190725 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplayer_play.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplayer_previous.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplayer_previous.png new file mode 100644 index 00000000..d0bf1f66 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_miniplayer_previous.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_nugu.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_nugu.png new file mode 100644 index 00000000..9bddec69 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_nugu.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_panel_play_large.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_panel_play_large.png new file mode 100644 index 00000000..4ac71038 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_panel_play_large.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_eq_off.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_eq_off.png new file mode 100644 index 00000000..f23d9c6c Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_eq_off.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_go_list.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_go_list.png new file mode 100644 index 00000000..1b2d9774 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_go_list.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_more.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_more.png new file mode 100644 index 00000000..a8ad9e62 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_more.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_play.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_play.png new file mode 100644 index 00000000..f6c3201c Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_play.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_related.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_related.png new file mode 100644 index 00000000..9026fe5e Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_related.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_setting.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_setting.png new file mode 100644 index 00000000..0df8f69a Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_setting.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_unlike_off.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_unlike_off.png new file mode 100644 index 00000000..b5395047 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_unlike_off.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_unlike_on.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_unlike_on.png new file mode 100644 index 00000000..45a43cae Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_player_unlike_on.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_playlist_select_off.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_playlist_select_off.png new file mode 100644 index 00000000..62ef45ca Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_playlist_select_off.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_playlist_select_on.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_playlist_select_on.png new file mode 100644 index 00000000..2d3b6afe Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_playlist_select_on.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_setting_phone.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_setting_phone.png new file mode 100644 index 00000000..d6de4c69 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_setting_phone.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_textbox_close.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_textbox_close.png new file mode 100644 index 00000000..10f1f636 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_textbox_close.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_titlebar_close.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_titlebar_close.png new file mode 100644 index 00000000..6615deff Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_titlebar_close.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_toggle_off.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_toggle_off.png new file mode 100644 index 00000000..983360df Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_toggle_off.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/btn_toggle_on.png b/naru/AndroidFloClone/app/src/main/res/drawable/btn_toggle_on.png new file mode 100644 index 00000000..fb609f46 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/btn_toggle_on.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/button_background_black_color.xml b/naru/AndroidFloClone/app/src/main/res/drawable/button_background_black_color.xml new file mode 100644 index 00000000..cad3794f --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/drawable/button_background_black_color.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/button_background_flo_color.xml b/naru/AndroidFloClone/app/src/main/res/drawable/button_background_flo_color.xml new file mode 100644 index 00000000..d5e92f35 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/drawable/button_background_flo_color.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/button_background_gray_color.xml b/naru/AndroidFloClone/app/src/main/res/drawable/button_background_gray_color.xml new file mode 100644 index 00000000..dbcaae2b --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/drawable/button_background_gray_color.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/button_background_white_color.xml b/naru/AndroidFloClone/app/src/main/res/drawable/button_background_white_color.xml new file mode 100644 index 00000000..32e95830 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/drawable/button_background_white_color.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/discovery_banner_aos.jpg b/naru/AndroidFloClone/app/src/main/res/drawable/discovery_banner_aos.jpg new file mode 100644 index 00000000..c9055156 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/discovery_banner_aos.jpg differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/fragment_look_chart_background.xml b/naru/AndroidFloClone/app/src/main/res/drawable/fragment_look_chart_background.xml new file mode 100644 index 00000000..64c040f5 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/drawable/fragment_look_chart_background.xml @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/fragment_look_chip_off_background.xml b/naru/AndroidFloClone/app/src/main/res/drawable/fragment_look_chip_off_background.xml new file mode 100644 index 00000000..43005ce6 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/drawable/fragment_look_chip_off_background.xml @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/fragment_look_chip_on_background.xml b/naru/AndroidFloClone/app/src/main/res/drawable/fragment_look_chip_on_background.xml new file mode 100644 index 00000000..ce894ed7 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/drawable/fragment_look_chip_on_background.xml @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/gray_circle.xml b/naru/AndroidFloClone/app/src/main/res/drawable/gray_circle.xml new file mode 100644 index 00000000..5b9a17d6 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/drawable/gray_circle.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_home_no_select.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_home_no_select.png new file mode 100644 index 00000000..69a8ab6c Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_home_no_select.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_home_select.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_home_select.png new file mode 100644 index 00000000..c0ff48e7 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_home_select.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_locker_no_select.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_locker_no_select.png new file mode 100644 index 00000000..a67dec39 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_locker_no_select.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_locker_select.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_locker_select.png new file mode 100644 index 00000000..042489f1 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_locker_select.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_look_no_select.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_look_no_select.png new file mode 100644 index 00000000..6c2f4f0c Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_look_no_select.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_look_select.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_look_select.png new file mode 100644 index 00000000..3d169e48 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_look_select.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_my_no_select.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_my_no_select.png new file mode 100644 index 00000000..a67dec39 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_my_no_select.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_my_select.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_my_select.png new file mode 100644 index 00000000..042489f1 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_my_select.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_search_no_select.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_search_no_select.png new file mode 100644 index 00000000..a77b8c57 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_search_no_select.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_search_select.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_search_select.png new file mode 100644 index 00000000..d5c8a722 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_bottom_search_select.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_browse_arrow_right.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_browse_arrow_right.png new file mode 100644 index 00000000..71b588bf Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_browse_arrow_right.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_flo_logo.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_flo_logo.png new file mode 100644 index 00000000..643224de Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_flo_logo.png differ diff --git a/naru/W01/app/src/main/res/drawable/ic_launcher_background.xml b/naru/AndroidFloClone/app/src/main/res/drawable/ic_launcher_background.xml similarity index 100% rename from naru/W01/app/src/main/res/drawable/ic_launcher_background.xml rename to naru/AndroidFloClone/app/src/main/res/drawable/ic_launcher_background.xml diff --git a/naru/W01/app/src/main/res/drawable/ic_launcher_foreground.xml b/naru/AndroidFloClone/app/src/main/res/drawable/ic_launcher_foreground.xml similarity index 100% rename from naru/W01/app/src/main/res/drawable/ic_launcher_foreground.xml rename to naru/AndroidFloClone/app/src/main/res/drawable/ic_launcher_foreground.xml diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_facebook.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_facebook.png new file mode 100644 index 00000000..83e97321 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_facebook.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_facebook_btn.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_facebook_btn.png new file mode 100644 index 00000000..83e97321 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_facebook_btn.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_instagram.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_instagram.png new file mode 100644 index 00000000..398ce614 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_instagram.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_instagram_btn.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_instagram_btn.png new file mode 100644 index 00000000..398ce614 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_instagram_btn.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_twitter.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_twitter.png new file mode 100644 index 00000000..6ddc68ee Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_twitter.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_twitter_btn.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_twitter_btn.png new file mode 100644 index 00000000..6ddc68ee Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_twitter_btn.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_youtube.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_youtube.png new file mode 100644 index 00000000..0c4ec932 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_youtube.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_youtube_btn.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_youtube_btn.png new file mode 100644 index 00000000..0c4ec932 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_main_youtube_btn.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_my_like_off.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_my_like_off.png new file mode 100644 index 00000000..c06e139d Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_my_like_off.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ic_my_like_on.png b/naru/AndroidFloClone/app/src/main/res/drawable/ic_my_like_on.png new file mode 100644 index 00000000..22577c0b Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ic_my_like_on.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/ico_20_logo_tid_white.png b/naru/AndroidFloClone/app/src/main/res/drawable/ico_20_logo_tid_white.png new file mode 100644 index 00000000..c6f4d4f9 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/ico_20_logo_tid_white.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/icon_browse_arrow_right.png b/naru/AndroidFloClone/app/src/main/res/drawable/icon_browse_arrow_right.png new file mode 100644 index 00000000..71b588bf Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/icon_browse_arrow_right.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/img_album_exp.png b/naru/AndroidFloClone/app/src/main/res/drawable/img_album_exp.png new file mode 100644 index 00000000..6e3f38af Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/img_album_exp.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/img_album_exp2.jpg b/naru/AndroidFloClone/app/src/main/res/drawable/img_album_exp2.jpg new file mode 100644 index 00000000..839fdc0d Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/img_album_exp2.jpg differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/img_album_exp3.jpg b/naru/AndroidFloClone/app/src/main/res/drawable/img_album_exp3.jpg new file mode 100644 index 00000000..66416001 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/img_album_exp3.jpg differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/img_album_exp4.png b/naru/AndroidFloClone/app/src/main/res/drawable/img_album_exp4.png new file mode 100644 index 00000000..c5db3fd6 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/img_album_exp4.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/img_album_exp5.png b/naru/AndroidFloClone/app/src/main/res/drawable/img_album_exp5.png new file mode 100644 index 00000000..8a2d411e Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/img_album_exp5.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/img_album_exp6.jpg b/naru/AndroidFloClone/app/src/main/res/drawable/img_album_exp6.jpg new file mode 100644 index 00000000..48202f2d Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/img_album_exp6.jpg differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/img_album_lp.png b/naru/AndroidFloClone/app/src/main/res/drawable/img_album_lp.png new file mode 100644 index 00000000..29fb1b4d Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/img_album_lp.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/img_default_4_x_1.png b/naru/AndroidFloClone/app/src/main/res/drawable/img_default_4_x_1.png new file mode 100644 index 00000000..926d34f5 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/img_default_4_x_1.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/img_detail_video.jpg b/naru/AndroidFloClone/app/src/main/res/drawable/img_detail_video.jpg new file mode 100644 index 00000000..35f8dee5 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/img_detail_video.jpg differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/img_first_album_default.png b/naru/AndroidFloClone/app/src/main/res/drawable/img_first_album_default.png new file mode 100644 index 00000000..926d34f5 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/img_first_album_default.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/img_home_viewpager_exp.png b/naru/AndroidFloClone/app/src/main/res/drawable/img_home_viewpager_exp.png new file mode 100644 index 00000000..da780323 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/img_home_viewpager_exp.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/img_home_viewpager_exp2.png b/naru/AndroidFloClone/app/src/main/res/drawable/img_home_viewpager_exp2.png new file mode 100644 index 00000000..50fa4be8 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/img_home_viewpager_exp2.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/img_home_viewpager_exp3.jpg b/naru/AndroidFloClone/app/src/main/res/drawable/img_home_viewpager_exp3.jpg new file mode 100644 index 00000000..9b588e31 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/img_home_viewpager_exp3.jpg differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/img_home_viewpager_exp4.jpg b/naru/AndroidFloClone/app/src/main/res/drawable/img_home_viewpager_exp4.jpg new file mode 100644 index 00000000..e7d038d5 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/img_home_viewpager_exp4.jpg differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/img_jenre_exp_1.png b/naru/AndroidFloClone/app/src/main/res/drawable/img_jenre_exp_1.png new file mode 100644 index 00000000..0d43e8e4 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/img_jenre_exp_1.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/img_jenre_exp_2.png b/naru/AndroidFloClone/app/src/main/res/drawable/img_jenre_exp_2.png new file mode 100644 index 00000000..f03efb22 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/img_jenre_exp_2.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/img_jenre_exp_3.png b/naru/AndroidFloClone/app/src/main/res/drawable/img_jenre_exp_3.png new file mode 100644 index 00000000..51de6849 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/img_jenre_exp_3.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/img_potcast_exp.png b/naru/AndroidFloClone/app/src/main/res/drawable/img_potcast_exp.png new file mode 100644 index 00000000..50a46e03 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/img_potcast_exp.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/img_video_exp.png b/naru/AndroidFloClone/app/src/main/res/drawable/img_video_exp.png new file mode 100644 index 00000000..7f6b05f2 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/img_video_exp.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/kakako_44.png b/naru/AndroidFloClone/app/src/main/res/drawable/kakako_44.png new file mode 100644 index 00000000..243298e0 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/kakako_44.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/main_btm_color_selector.xml b/naru/AndroidFloClone/app/src/main/res/drawable/main_btm_color_selector.xml new file mode 100644 index 00000000..3ecf8a7a --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/drawable/main_btm_color_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/main_btm_home_selector.xml b/naru/AndroidFloClone/app/src/main/res/drawable/main_btm_home_selector.xml new file mode 100644 index 00000000..b018478e --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/drawable/main_btm_home_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/main_btm_look_selector.xml b/naru/AndroidFloClone/app/src/main/res/drawable/main_btm_look_selector.xml new file mode 100644 index 00000000..89ced75d --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/drawable/main_btm_look_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/main_btm_my_selector.xml b/naru/AndroidFloClone/app/src/main/res/drawable/main_btm_my_selector.xml new file mode 100644 index 00000000..d6739bf2 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/drawable/main_btm_my_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/main_btm_search_selector.xml b/naru/AndroidFloClone/app/src/main/res/drawable/main_btm_search_selector.xml new file mode 100644 index 00000000..e6e823fd --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/drawable/main_btm_search_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/naver_44.png b/naru/AndroidFloClone/app/src/main/res/drawable/naver_44.png new file mode 100644 index 00000000..d9844877 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/naver_44.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_down.png b/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_down.png new file mode 100644 index 00000000..03a04c57 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_down.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_pause_32.png b/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_pause_32.png new file mode 100644 index 00000000..9388aa33 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_pause_32.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_play_32.png b/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_play_32.png new file mode 100644 index 00000000..b781e4c8 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_play_32.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_random_active.png b/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_random_active.png new file mode 100644 index 00000000..89781437 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_random_active.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_random_inactive.png b/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_random_inactive.png new file mode 100644 index 00000000..fe4f880b Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_random_inactive.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_repeat_active.png b/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_repeat_active.png new file mode 100644 index 00000000..d424e377 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_repeat_active.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_repeat_inactive.png b/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_repeat_inactive.png new file mode 100644 index 00000000..1e4044dc Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_repeat_inactive.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_repeat_one.png b/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_repeat_one.png new file mode 100644 index 00000000..62344a48 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_repeat_one.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_skip_next_32.png b/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_skip_next_32.png new file mode 100644 index 00000000..fc02f28f Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_skip_next_32.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_skip_previous_32.png b/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_skip_previous_32.png new file mode 100644 index 00000000..03ec854c Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/nugu_btn_skip_previous_32.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/splash.xml b/naru/AndroidFloClone/app/src/main/res/drawable/splash.xml new file mode 100644 index 00000000..ace8a7d8 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/drawable/splash.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/textview_background_radius.xml b/naru/AndroidFloClone/app/src/main/res/drawable/textview_background_radius.xml new file mode 100644 index 00000000..53beead7 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/drawable/textview_background_radius.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/textview_background_select_color_radius.xml b/naru/AndroidFloClone/app/src/main/res/drawable/textview_background_select_color_radius.xml new file mode 100644 index 00000000..ea8dc88c --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/drawable/textview_background_select_color_radius.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/toast_background.xml b/naru/AndroidFloClone/app/src/main/res/drawable/toast_background.xml new file mode 100644 index 00000000..de008fd4 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/drawable/toast_background.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/drawable/widget_black_play.png b/naru/AndroidFloClone/app/src/main/res/drawable/widget_black_play.png new file mode 100644 index 00000000..0ec27005 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/drawable/widget_black_play.png differ diff --git a/naru/AndroidFloClone/app/src/main/res/layout/activity_login.xml b/naru/AndroidFloClone/app/src/main/res/layout/activity_login.xml new file mode 100644 index 00000000..7a3b16fc --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,284 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/layout/activity_main.xml b/naru/AndroidFloClone/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..91a3a426 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/layout/activity_sign_up.xml b/naru/AndroidFloClone/app/src/main/res/layout/activity_sign_up.xml new file mode 100644 index 00000000..e5e99f2a --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/activity_sign_up.xml @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/layout/activity_song.xml b/naru/AndroidFloClone/app/src/main/res/layout/activity_song.xml new file mode 100644 index 00000000..d539f82f --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/activity_song.xml @@ -0,0 +1,341 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/layout/activity_splash.xml b/naru/AndroidFloClone/app/src/main/res/layout/activity_splash.xml new file mode 100644 index 00000000..f1a67a8a --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/activity_splash.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/layout/fragment_album.xml b/naru/AndroidFloClone/app/src/main/res/layout/fragment_album.xml new file mode 100644 index 00000000..429d0278 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/fragment_album.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/layout/fragment_banner.xml b/naru/AndroidFloClone/app/src/main/res/layout/fragment_banner.xml new file mode 100644 index 00000000..80eaeb65 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/fragment_banner.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/layout/fragment_bottom_sheet.xml b/naru/AndroidFloClone/app/src/main/res/layout/fragment_bottom_sheet.xml new file mode 100644 index 00000000..72ca4f44 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/fragment_bottom_sheet.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/naru/AndroidFloClone/app/src/main/res/layout/fragment_detail.xml b/naru/AndroidFloClone/app/src/main/res/layout/fragment_detail.xml new file mode 100644 index 00000000..f25713ee --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/fragment_detail.xml @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/naru/AndroidFloClone/app/src/main/res/layout/fragment_home.xml b/naru/AndroidFloClone/app/src/main/res/layout/fragment_home.xml new file mode 100644 index 00000000..f0f6ea5a --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/fragment_home.xml @@ -0,0 +1,798 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/layout/fragment_locker.xml b/naru/AndroidFloClone/app/src/main/res/layout/fragment_locker.xml new file mode 100644 index 00000000..689853c1 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/fragment_locker.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/layout/fragment_look.xml b/naru/AndroidFloClone/app/src/main/res/layout/fragment_look.xml new file mode 100644 index 00000000..7f9e6854 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/fragment_look.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/layout/fragment_music_file.xml b/naru/AndroidFloClone/app/src/main/res/layout/fragment_music_file.xml new file mode 100644 index 00000000..fd16bd99 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/fragment_music_file.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/layout/fragment_panel.xml b/naru/AndroidFloClone/app/src/main/res/layout/fragment_panel.xml new file mode 100644 index 00000000..8bb07c7d --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/fragment_panel.xml @@ -0,0 +1,18 @@ + + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/layout/fragment_saved_album.xml b/naru/AndroidFloClone/app/src/main/res/layout/fragment_saved_album.xml new file mode 100644 index 00000000..2f7e2d0e --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/fragment_saved_album.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/layout/fragment_saved_song.xml b/naru/AndroidFloClone/app/src/main/res/layout/fragment_saved_song.xml new file mode 100644 index 00000000..ded070e9 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/fragment_saved_song.xml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/layout/fragment_search.xml b/naru/AndroidFloClone/app/src/main/res/layout/fragment_search.xml new file mode 100644 index 00000000..f1a5153d --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/fragment_search.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/layout/fragment_song.xml b/naru/AndroidFloClone/app/src/main/res/layout/fragment_song.xml new file mode 100644 index 00000000..e851295c --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/fragment_song.xml @@ -0,0 +1,810 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/layout/fragment_video.xml b/naru/AndroidFloClone/app/src/main/res/layout/fragment_video.xml new file mode 100644 index 00000000..d18af59f --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/fragment_video.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/layout/item_album.xml b/naru/AndroidFloClone/app/src/main/res/layout/item_album.xml new file mode 100644 index 00000000..fa55fce3 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/item_album.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/layout/item_locker_album.xml b/naru/AndroidFloClone/app/src/main/res/layout/item_locker_album.xml new file mode 100644 index 00000000..368679e4 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/item_locker_album.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/layout/item_saved_song.xml b/naru/AndroidFloClone/app/src/main/res/layout/item_saved_song.xml new file mode 100644 index 00000000..0af7a426 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/item_saved_song.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/layout/toast.xml b/naru/AndroidFloClone/app/src/main/res/layout/toast.xml new file mode 100644 index 00000000..f076cb1c --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/layout/toast.xml @@ -0,0 +1,24 @@ + + + + + + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/menu/bottom_nav_menu.xml b/naru/AndroidFloClone/app/src/main/res/menu/bottom_nav_menu.xml new file mode 100644 index 00000000..9a55b1a3 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/menu/bottom_nav_menu.xml @@ -0,0 +1,28 @@ + + + + + + + + \ No newline at end of file diff --git a/naru/W01/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/naru/AndroidFloClone/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml similarity index 100% rename from naru/W01/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml rename to naru/AndroidFloClone/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml diff --git a/naru/W01/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/naru/AndroidFloClone/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml similarity index 100% rename from naru/W01/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to naru/AndroidFloClone/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml diff --git a/naru/W01/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/naru/AndroidFloClone/app/src/main/res/mipmap-hdpi/ic_launcher.webp similarity index 100% rename from naru/W01/app/src/main/res/mipmap-hdpi/ic_launcher.webp rename to naru/AndroidFloClone/app/src/main/res/mipmap-hdpi/ic_launcher.webp diff --git a/naru/W01/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/naru/AndroidFloClone/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp similarity index 100% rename from naru/W01/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp rename to naru/AndroidFloClone/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp diff --git a/naru/W01/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/naru/AndroidFloClone/app/src/main/res/mipmap-mdpi/ic_launcher.webp similarity index 100% rename from naru/W01/app/src/main/res/mipmap-mdpi/ic_launcher.webp rename to naru/AndroidFloClone/app/src/main/res/mipmap-mdpi/ic_launcher.webp diff --git a/naru/W01/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/naru/AndroidFloClone/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp similarity index 100% rename from naru/W01/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp rename to naru/AndroidFloClone/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp diff --git a/naru/W01/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/naru/AndroidFloClone/app/src/main/res/mipmap-xhdpi/ic_launcher.webp similarity index 100% rename from naru/W01/app/src/main/res/mipmap-xhdpi/ic_launcher.webp rename to naru/AndroidFloClone/app/src/main/res/mipmap-xhdpi/ic_launcher.webp diff --git a/naru/W01/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/naru/AndroidFloClone/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp similarity index 100% rename from naru/W01/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp rename to naru/AndroidFloClone/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp diff --git a/naru/W01/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/naru/AndroidFloClone/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp similarity index 100% rename from naru/W01/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp rename to naru/AndroidFloClone/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp diff --git a/naru/W01/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/naru/AndroidFloClone/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp similarity index 100% rename from naru/W01/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp rename to naru/AndroidFloClone/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp diff --git a/naru/W01/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/naru/AndroidFloClone/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp similarity index 100% rename from naru/W01/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp rename to naru/AndroidFloClone/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp diff --git a/naru/W01/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/naru/AndroidFloClone/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp similarity index 100% rename from naru/W01/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp rename to naru/AndroidFloClone/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp diff --git a/naru/AndroidFloClone/app/src/main/res/raw/music_butter.mp3 b/naru/AndroidFloClone/app/src/main/res/raw/music_butter.mp3 new file mode 100644 index 00000000..82c470ff Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/raw/music_butter.mp3 differ diff --git a/naru/AndroidFloClone/app/src/main/res/raw/music_journey.mp3 b/naru/AndroidFloClone/app/src/main/res/raw/music_journey.mp3 new file mode 100644 index 00000000..c4c21465 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/raw/music_journey.mp3 differ diff --git a/naru/AndroidFloClone/app/src/main/res/raw/music_myworld.mp3 b/naru/AndroidFloClone/app/src/main/res/raw/music_myworld.mp3 new file mode 100644 index 00000000..b755e057 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/raw/music_myworld.mp3 differ diff --git a/naru/AndroidFloClone/app/src/main/res/raw/music_nextlevel.mp3 b/naru/AndroidFloClone/app/src/main/res/raw/music_nextlevel.mp3 new file mode 100644 index 00000000..6c8e45d8 Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/raw/music_nextlevel.mp3 differ diff --git a/naru/AndroidFloClone/app/src/main/res/raw/music_sad.mp3 b/naru/AndroidFloClone/app/src/main/res/raw/music_sad.mp3 new file mode 100644 index 00000000..5cdbf2cd Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/raw/music_sad.mp3 differ diff --git a/naru/AndroidFloClone/app/src/main/res/raw/music_weekend.mp3 b/naru/AndroidFloClone/app/src/main/res/raw/music_weekend.mp3 new file mode 100644 index 00000000..4602138b Binary files /dev/null and b/naru/AndroidFloClone/app/src/main/res/raw/music_weekend.mp3 differ diff --git a/naru/AndroidFloClone/app/src/main/res/values-night/themes.xml b/naru/AndroidFloClone/app/src/main/res/values-night/themes.xml new file mode 100644 index 00000000..d4b8546e --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/values-night/themes.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/values/colors.xml b/naru/AndroidFloClone/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..1b06cff1 --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/values/colors.xml @@ -0,0 +1,27 @@ + + + #3f3fff + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + + #9cbee2 + #062342 + #424242 + #6bb2ff + + #00ff0000 + #ff3f3fff + #a8a8a8 + #3f3fff + #a8a8a8 + + #3f3fff + + #F11818 + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/values/strings.xml b/naru/AndroidFloClone/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..a3c154ec --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/values/strings.xml @@ -0,0 +1,5 @@ + + AndroidFloClone + + Hello blank fragment + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/main/res/values/themes.xml b/naru/AndroidFloClone/app/src/main/res/values/themes.xml new file mode 100644 index 00000000..aac9b2af --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/values/themes.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/naru/W01/app/src/main/res/xml/backup_rules.xml b/naru/AndroidFloClone/app/src/main/res/xml/backup_rules.xml similarity index 100% rename from naru/W01/app/src/main/res/xml/backup_rules.xml rename to naru/AndroidFloClone/app/src/main/res/xml/backup_rules.xml diff --git a/naru/W01/app/src/main/res/xml/data_extraction_rules.xml b/naru/AndroidFloClone/app/src/main/res/xml/data_extraction_rules.xml similarity index 100% rename from naru/W01/app/src/main/res/xml/data_extraction_rules.xml rename to naru/AndroidFloClone/app/src/main/res/xml/data_extraction_rules.xml diff --git a/naru/AndroidFloClone/app/src/main/res/xml/network_security_config.xml b/naru/AndroidFloClone/app/src/main/res/xml/network_security_config.xml new file mode 100644 index 00000000..182ebb4b --- /dev/null +++ b/naru/AndroidFloClone/app/src/main/res/xml/network_security_config.xml @@ -0,0 +1,6 @@ + + + + 3.35.121.185 + + \ No newline at end of file diff --git a/naru/AndroidFloClone/app/src/test/java/com/example/androidfloclone/ExampleUnitTest.kt b/naru/AndroidFloClone/app/src/test/java/com/example/androidfloclone/ExampleUnitTest.kt new file mode 100644 index 00000000..1750ee4d --- /dev/null +++ b/naru/AndroidFloClone/app/src/test/java/com/example/androidfloclone/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.example.androidfloclone + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/naru/W01/build.gradle.kts b/naru/AndroidFloClone/build.gradle.kts similarity index 100% rename from naru/W01/build.gradle.kts rename to naru/AndroidFloClone/build.gradle.kts diff --git a/naru/W01/gradle.properties b/naru/AndroidFloClone/gradle.properties similarity index 100% rename from naru/W01/gradle.properties rename to naru/AndroidFloClone/gradle.properties diff --git a/naru/AndroidFloClone/gradle/libs.versions.toml b/naru/AndroidFloClone/gradle/libs.versions.toml new file mode 100644 index 00000000..763872b2 --- /dev/null +++ b/naru/AndroidFloClone/gradle/libs.versions.toml @@ -0,0 +1,34 @@ +[versions] +agp = "8.6.0" +gson = "2.8.9" +circleindicator = "2.1.6" +kotlin = "1.9.0" +coreKtx = "1.13.1" +junit = "4.13.2" +junitVersion = "1.2.1" +espressoCore = "3.6.1" +appcompat = "1.7.0" +material = "1.12.0" +activity = "1.9.2" +constraintlayout = "2.1.4" +room = "2.5.2" + +[libraries] +androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } +gson = { module = "com.google.code.gson:gson", version.ref = "gson" } +circleindicator = { module = "me.relex:circleindicator", version.ref = "circleindicator" } +junit = { group = "junit", name = "junit", version.ref = "junit" } +androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } +androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" } +room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" } +room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } + diff --git a/naru/W01/gradle/wrapper/gradle-wrapper.jar b/naru/AndroidFloClone/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from naru/W01/gradle/wrapper/gradle-wrapper.jar rename to naru/AndroidFloClone/gradle/wrapper/gradle-wrapper.jar diff --git a/naru/W01/gradle/wrapper/gradle-wrapper.properties b/naru/AndroidFloClone/gradle/wrapper/gradle-wrapper.properties similarity index 86% rename from naru/W01/gradle/wrapper/gradle-wrapper.properties rename to naru/AndroidFloClone/gradle/wrapper/gradle-wrapper.properties index 0c894356..bf6cb7bd 100644 --- a/naru/W01/gradle/wrapper/gradle-wrapper.properties +++ b/naru/AndroidFloClone/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -#Fri Sep 27 15:29:06 KST 2024 +#Sat Oct 12 09:11:00 KST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip diff --git a/naru/W01/gradlew b/naru/AndroidFloClone/gradlew similarity index 100% rename from naru/W01/gradlew rename to naru/AndroidFloClone/gradlew diff --git a/naru/W01/gradlew.bat b/naru/AndroidFloClone/gradlew.bat similarity index 100% rename from naru/W01/gradlew.bat rename to naru/AndroidFloClone/gradlew.bat diff --git a/naru/AndroidFloClone/settings.gradle.kts b/naru/AndroidFloClone/settings.gradle.kts new file mode 100644 index 00000000..a4768701 --- /dev/null +++ b/naru/AndroidFloClone/settings.gradle.kts @@ -0,0 +1,24 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "AndroidFloClone" +include(":app") + \ No newline at end of file diff --git a/naru/ForegroundService/.gitignore b/naru/ForegroundService/.gitignore new file mode 100644 index 00000000..aa724b77 --- /dev/null +++ b/naru/ForegroundService/.gitignore @@ -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 diff --git a/naru/ForegroundService/.idea/.gitignore b/naru/ForegroundService/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/naru/ForegroundService/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/naru/ForegroundService/.idea/compiler.xml b/naru/ForegroundService/.idea/compiler.xml new file mode 100644 index 00000000..b589d56e --- /dev/null +++ b/naru/ForegroundService/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/naru/ForegroundService/.idea/deploymentTargetSelector.xml b/naru/ForegroundService/.idea/deploymentTargetSelector.xml new file mode 100644 index 00000000..b268ef36 --- /dev/null +++ b/naru/ForegroundService/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/naru/ForegroundService/.idea/gradle.xml b/naru/ForegroundService/.idea/gradle.xml new file mode 100644 index 00000000..0897082f --- /dev/null +++ b/naru/ForegroundService/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/naru/ForegroundService/.idea/kotlinc.xml b/naru/ForegroundService/.idea/kotlinc.xml new file mode 100644 index 00000000..fdf8d994 --- /dev/null +++ b/naru/ForegroundService/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/naru/ForegroundService/.idea/migrations.xml b/naru/ForegroundService/.idea/migrations.xml new file mode 100644 index 00000000..f8051a6f --- /dev/null +++ b/naru/ForegroundService/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/naru/ForegroundService/.idea/misc.xml b/naru/ForegroundService/.idea/misc.xml new file mode 100644 index 00000000..8978d23d --- /dev/null +++ b/naru/ForegroundService/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/naru/ForegroundService/.idea/vcs.xml b/naru/ForegroundService/.idea/vcs.xml new file mode 100644 index 00000000..b2bdec2d --- /dev/null +++ b/naru/ForegroundService/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/naru/ForegroundService/app/.gitignore b/naru/ForegroundService/app/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/naru/ForegroundService/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/naru/ForegroundService/app/build.gradle.kts b/naru/ForegroundService/app/build.gradle.kts new file mode 100644 index 00000000..098a7e95 --- /dev/null +++ b/naru/ForegroundService/app/build.gradle.kts @@ -0,0 +1,48 @@ +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) +} + +android { + namespace = "com.example.foregroundservice" + compileSdk = 35 + + defaultConfig { + applicationId = "com.example.foregroundservice" + minSdk = 24 + targetSdk = 35 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +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) +} \ No newline at end of file diff --git a/naru/ForegroundService/app/proguard-rules.pro b/naru/ForegroundService/app/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/naru/ForegroundService/app/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/naru/ForegroundService/app/src/androidTest/java/com/example/foregroundservice/ExampleInstrumentedTest.kt b/naru/ForegroundService/app/src/androidTest/java/com/example/foregroundservice/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..51c1a6a0 --- /dev/null +++ b/naru/ForegroundService/app/src/androidTest/java/com/example/foregroundservice/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.example.foregroundservice + +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.example.foregroundservice", appContext.packageName) + } +} \ No newline at end of file diff --git a/naru/ForegroundService/app/src/main/AndroidManifest.xml b/naru/ForegroundService/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..69c227de --- /dev/null +++ b/naru/ForegroundService/app/src/main/AndroidManifest.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/naru/ForegroundService/app/src/main/java/com/example/foregroundservice/Foreground.kt b/naru/ForegroundService/app/src/main/java/com/example/foregroundservice/Foreground.kt new file mode 100644 index 00000000..f1ba18b6 --- /dev/null +++ b/naru/ForegroundService/app/src/main/java/com/example/foregroundservice/Foreground.kt @@ -0,0 +1,105 @@ +package com.example.foregroundservice + +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.app.Service +import android.content.Intent +import android.os.Binder +import android.os.Build +import android.os.Handler +import android.os.IBinder +import android.os.Looper +import android.util.Log +import androidx.core.app.NotificationCompat +import kotlin.concurrent.thread + +class Foreground : Service() { + + val CHANNEL_ID = "1" + val NOTI_ID = 2 + private val handler = Handler(Looper.getMainLooper()) + private var progress = 0 + private var isRunning = false // 서비스가 실행 중인지 여부를 나타내는 변수 + + fun createNotificationChannel() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val serviceChannel = NotificationChannel(CHANNEL_ID, "FOREGROUND", NotificationManager.IMPORTANCE_HIGH) + val manager = getSystemService(NotificationManager::class.java) + manager.createNotificationChannel(serviceChannel) + } + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + createNotificationChannel() + + // MainActivity로 이동하는 Intent 생성 + val activityIntent = Intent(this, MainActivity::class.java) + activityIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + val pendingIntent = PendingIntent.getActivity( + this, + 0, + activityIntent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + + if (!isRunning) { + isRunning = true + runBackground() + } + + return super.onStartCommand(intent, flags, startId) + } + + fun runBackground() { + // 주기적인 작업을 Handler로 처리 + handler.post(object : Runnable { + override fun run() { + if (progress <= 100 && isRunning) { // isRunning이 true일 때만 계속 진행 + // 알림의 시크바 업데이트 + val notification = NotificationCompat.Builder(this@Foreground, CHANNEL_ID) + .setContentTitle("Foreground") + .setContentText("Progressing... $progress%") + .setSmallIcon(R.mipmap.ic_launcher_round) + .setContentIntent(getPendingIntent()) + .setProgress(100, progress, false) // 시크바 진행 + .setAutoCancel(true) + .build() + + // 알림 업데이트 + val notificationManager = getSystemService(NotificationManager::class.java) + notificationManager.notify(NOTI_ID, notification) + + Log.d("서비스", "COUNT => $progress") + progress++ + + // 1초마다 작업을 반복 (주기적으로) + handler.postDelayed(this, 1000) + } + } + }) + } + + private fun getPendingIntent(): PendingIntent { + // MainActivity로 이동하는 PendingIntent + val activityIntent = Intent(this, MainActivity::class.java) + activityIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + return PendingIntent.getActivity( + this, + 0, + activityIntent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + } + + override fun onBind(intent: Intent): IBinder { + return Binder() + } + + // 서비스 종료를 위한 메서드 + override fun onDestroy() { + super.onDestroy() + isRunning = false // 서비스가 종료되면 주기적인 작업을 멈추기 위해 false로 설정 + handler.removeCallbacksAndMessages(null) // 모든 handler 작업 취소 + } +} diff --git a/naru/ForegroundService/app/src/main/java/com/example/foregroundservice/MainActivity.kt b/naru/ForegroundService/app/src/main/java/com/example/foregroundservice/MainActivity.kt new file mode 100644 index 00000000..edc55047 --- /dev/null +++ b/naru/ForegroundService/app/src/main/java/com/example/foregroundservice/MainActivity.kt @@ -0,0 +1,29 @@ +package com.example.foregroundservice + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.View +import androidx.activity.enableEdgeToEdge +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat + +class MainActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContentView(R.layout.activity_main) + } + + fun serviceStart (view: View) { + val intent = Intent(this, Foreground::class.java) + ContextCompat.startForegroundService(this, intent) + } + + fun serviceStop (view: View) { + val intent = Intent(this, Foreground::class.java) + stopService(intent) + } +} \ No newline at end of file diff --git a/naru/ForegroundService/app/src/main/res/drawable/ic_launcher_background.xml b/naru/ForegroundService/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..07d5da9c --- /dev/null +++ b/naru/ForegroundService/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/naru/ForegroundService/app/src/main/res/drawable/ic_launcher_foreground.xml b/naru/ForegroundService/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 00000000..2b068d11 --- /dev/null +++ b/naru/ForegroundService/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/naru/ForegroundService/app/src/main/res/layout/activity_main.xml b/naru/ForegroundService/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..b1866d66 --- /dev/null +++ b/naru/ForegroundService/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,34 @@ + + + + + +