strings.xml
<resources>
<string name="app_name">SimpleMusicPlayer</string>
<string name="btn_play">재생</string>
<string name="btn_pause">일시정지</string>
<string name="btn_stop">재생중지</string>
</resources>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/btn_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/btn_play"
app:layout_constraintBottom_toTopOf="@+id/button2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="120dp"
android:text="@string/btn_pause"
app:layout_constraintBottom_toTopOf="@+id/button3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_play" />
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="120dp"
android:text="@string/btn_stop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button2" />
</androidx.constraintlayout.widget.ConstraintLayout>
MusicPlayerService.kt
package com.example.simplemusicplayer
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Intent
import android.media.MediaPlayer
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.widget.Toast
class MusicPlayerService : Service() {
var mMediaPlayer: MediaPlayer? = null // 미디어 플레이어 객체를 null로 초기화
var mBinder: MusicPlayerBinder = MusicPlayerBinder()
inner class MusicPlayerBinder : Binder() {
fun getService(): MusicPlayerService {
return this@MusicPlayerService
}
}
override fun onCreate() {
super.onCreate()
startForegroundService()
}
override fun onBind(intent: Intent?): IBinder? {
TODO("Not yet implemented")
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return START_STICKY
}
fun startForegroundService() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
val mChannel = NotificationChannel(
"CHANNEL_ID",
"CHANNEL_NAME",
NotificationManager.IMPORTANCE_DEFAULT
)
notificationManager.createNotificationChannel(mChannel)
}
val notification: Notification = Notification.Builder(this, "CHANNEL_ID")
.setSmallIcon(R.drawable.baseline_play_arrow_24)
.setContentTitle("뮤직 플레이어 앱")
.setContentText("앱이 실행 중입니다.")
.build()
startForeground(1, notification)
}
override fun onDestroy() {
super.onDestroy()
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
stopForeground(true);
}
}
fun isPlaying() : Boolean {
return (mMediaPlayer != null && mMediaPlayer?.isPlaying ?: false)
}
fun play() {
if (mMediaPlayer == null) {
mMediaPlayer = MediaPlayer.create(this, R.raw.chocolate)
mMediaPlayer?.setVolume(1.0f, 1.0f);
mMediaPlayer?.isLooping = true
mMediaPlayer?.start()
} else {
if(mMediaPlayer!!.isPlaying) {
Toast.makeText(this, "이미 음악이 실행 중입니다.",
Toast.LENGTH_SHORT).show()
} else {
mMediaPlayer?.start()
}
}
}
fun pause() {
mMediaPlayer?.let {
if (it.isPlaying) {
it.pause()
}
}
}
fun stop() {
mMediaPlayer?.let {
if (it.isPlaying) {
it.stop()
it.release()
mMediaPlayer = null
}
}
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.SimpleMusicPlayer"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".MusicPlayerService"/>
</application>
</manifest>
MainActivity.kt
package com.example.simplemusicplayer
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Button
class MainActivity : AppCompatActivity(), View.OnClickListener {
lateinit var btn_play : Button
lateinit var btn_pause : Button
lateinit var btn_stop : Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btn_play = findViewById(R.id.btn_play)
btn_pause = findViewById(R.id.btn_pause)
btn_stop = findViewById(R.id.btn_stop)
btn_play.setOnClickListener(this)
btn_pause.setOnClickListener(this)
btn_stop.setOnClickListener(this)
}
override fun onClick(v: View?) {
when(v?.id) {
R.id.btn_play -> {
play()
}
R.id.btn_pause -> {
pause()
}
R.id.btn_stop -> {
stop()
}
}
}
override fun onResume() {
super.onResume()
}
override fun onPause() {
super.onPause()
}
private fun play() {
}
private fun pause() {
}
private fun stop() {
}
}
MainActivity.kt
package com.example.simplemusicplayer
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.IBinder
import android.view.View
import android.widget.Button
class MainActivity : AppCompatActivity(), View.OnClickListener {
lateinit var btn_play : Button
lateinit var btn_pause : Button
lateinit var btn_stop : Button
var mService: MusicPlayerService? = null
val mServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
mService = (service as MusicPlayerService.MusicPlayerBinder).getService()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btn_play = findViewById(R.id.btn_play)
btn_pause = findViewById(R.id.btn_pause)
btn_stop = findViewById(R.id.btn_stop)
btn_play.setOnClickListener(this)
btn_pause.setOnClickListener(this)
btn_stop.setOnClickListener(this)
}
override fun onClick(v: View?) {
when(v?.id) {
R.id.btn_play -> {
play()
}
R.id.btn_pause -> {
pause()
}
R.id.btn_stop -> {
stop()
}
}
}
override fun onResume() {
super.onResume()
if(mService == null) {
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
startForegroundService(Intent(this, MusicPlayerService::class.java))
} else {
startService(Intent(applicationContext, MusicPlayerService::class.java))
}
val intent = Intent(this, MusicPlayerService::class.java)
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)
}
}
override fun onPause() {
super.onPause()
if (mService != null) {
if (!mService!!.isPlaying()) {
mService!!.stopSelf()
}
unbindService(mServiceConnection)
mService = null
}
}
private fun play() {
mService?.play()
}
private fun pause() {
mService?.pause()
}
private fun stop() {
mService?.stop()
}
}
- 바코드 스캐닝 API: 바코드를 스캔하고 해석하는 API를 제공합니다. 우리가 이번 앱에서 사용할 API입니다.
- 얼굴 인식 API: 얼굴을 인식하거나 얼굴의 요소들을 인식합니다.
- 텍스트 인식 API: 이미지로부터 텍스트를 인식하거나 추출합니다.
- 포즈 인식 API: 사람 몸의 자세를 실시간으로 인식합니다.
- 언어 감지 API: 주어진 텍스트가 쓰여진 언어가 무엇인지를 알려줍니다.
build.gradle
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'com.example.qrcodereader'
compileSdk 34
defaultConfig {
applicationId "com.example.qrcodereader"
minSdk 26
targetSdk 34
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled 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
}
}
dependencies {
def camerax_version = "1.1.0-alpha04"
implementation "androidx.camera:camera-camera2:$camerax_version"
implementation "androidx.camera:camera-lifecycle:$camerax_version"
implementation "androidx.camera:camera-view:1.0.0-alpha24"
implementation "com.google.mlkit:barcode-scanning:16.1.1"
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.11.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
activity_main.xml
package com.example.qrcodereader
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
activity_result.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ResultActivity">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="168dp"
android:text="결과값 :"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="48dp"
android:text="결과값이 들어갈 자리"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_title" />
<Button
android:id="@+id/btn_go_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="68dp"
android:text="돌아가기"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.kt
package com.example.qrcodereader
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.camera.core.CameraSelector
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import com.example.qrcodereader.databinding.ActivityMainBinding
import com.google.common.util.concurrent.ListenableFuture
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
private lateinit var cameraProviderFuture : ListenableFuture<ProcessCameraProvider>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
startCamera()
}
fun startCamera() {
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener(Runnable {
val cameraProvider = cameraProviderFuture.get()
val preview = getPreview()
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
cameraProvider.bindToLifecycle(this, cameraSelector,preview)
}, ContextCompat.getMainExecutor(this))
}
fun getPreview(): Preview {
val preview : Preview = Preview.Builder().build()
preview.setSurfaceProvider(binding.barcodePreview.getSurfaceProvider())
return preview
}
}
AndroidMAnifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.CAMERA" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.QRCodeReader"
tools:targetApi="31">
<activity
android:name=".ResultActivity"
android:exported="false" />
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
MainActivity.kt
package com.example.qrcodereader
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.camera.core.CameraSelector
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import com.example.qrcodereader.databinding.ActivityMainBinding
import com.google.common.util.concurrent.ListenableFuture
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
private lateinit var cameraProviderFuture : ListenableFuture<ProcessCameraProvider>
private val PERMISSIONS_REQUEST_CODE = 1
private val PERMISSIONS_REQUIRED = arrayOf(Manifest.permission.CAMERA)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
if (!hasPermissions(this)) {
requestPermissions(PERMISSIONS_REQUIRED, PERMISSIONS_REQUEST_CODE)
} else {
startCamera()
}
}
fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all {
ContextCompat.checkSelfPermission(context, it) ==
PackageManager.PERMISSION_GRANTED
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSIONS_REQUEST_CODE) {
if(PackageManager.PERMISSION_GRANTED == grantResults.firstOrNull()) {
Toast.makeText(this@MainActivity, "권한 요청이 승인되었습니다.",
Toast.LENGTH_LONG).show()
finish()
}
}
}
fun startCamera() {
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener(Runnable {
val cameraProvider = cameraProviderFuture.get()
val preview = getPreview()
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
cameraProvider.bindToLifecycle(this, cameraSelector,preview)
}, ContextCompat.getMainExecutor(this))
}
fun getPreview(): Preview {
val preview : Preview = Preview.Builder().build()
preview.setSurfaceProvider(binding.barcodePreview.getSurfaceProvider())
return preview
}
}
OnDetectListener.kt
package com.example.qrcodereader
interface OnDetectListener {
fun onDetect(msg : String)
}
MainActivity.kt
package com.example.qrcodereader
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import com.example.qrcodereader.databinding.ActivityMainBinding
import com.google.common.util.concurrent.ListenableFuture
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
private lateinit var cameraProviderFuture : ListenableFuture<ProcessCameraProvider>
private val PERMISSIONS_REQUEST_CODE = 1
private val PERMISSIONS_REQUIRED = arrayOf(Manifest.permission.CAMERA)
fun getImageAnalysis() : ImageAnalysis {
val cameraExecutor: ExecutorService = Executors.newSingleThreadExecutor()
val imageAnalysis = ImageAnalysis.Builder().build()
imageAnalysis.setAnalyzer(cameraExecutor, QRCodeAnalyzer(object : OnDetectListener {
override fun onDetect(msg: String) {
Toast.makeText(this@MainActivity, "${msg}", Toast.LENGTH_SHORT).show()
}
}))
return imageAnalysis
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
if (!hasPermissions(this)) {
requestPermissions(PERMISSIONS_REQUIRED, PERMISSIONS_REQUEST_CODE)
} else {
startCamera()
}
}
fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all {
ContextCompat.checkSelfPermission(context, it) ==
PackageManager.PERMISSION_GRANTED
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSIONS_REQUEST_CODE) {
if(PackageManager.PERMISSION_GRANTED == grantResults.firstOrNull()) {
Toast.makeText(this@MainActivity, "권한 요청이 승인되었습니다.",
Toast.LENGTH_LONG).show()
finish()
}
}
}
fun startCamera() {
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener(Runnable {
val cameraProvider = cameraProviderFuture.get()
val preview = getPreview()
val imageAnalysis = getImageAnalysis()
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
cameraProvider.bindToLifecycle(this, cameraSelector,preview,imageAnalysis)
}, ContextCompat.getMainExecutor(this))
}
fun getPreview(): Preview {
val preview : Preview = Preview.Builder().build()
preview.setSurfaceProvider(binding.barcodePreview.getSurfaceProvider())
return preview
}
}
package com.example.qrcodereader
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import com.example.qrcodereader.databinding.ActivityMainBinding
import com.google.common.util.concurrent.ListenableFuture
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
private lateinit var cameraProviderFuture : ListenableFuture<ProcessCameraProvider>
private val PERMISSIONS_REQUEST_CODE = 1
private val PERMISSIONS_REQUIRED = arrayOf(Manifest.permission.CAMERA)
private var isDetected = false
override fun onResume() {
super.onResume()
isDetected=false
}
fun getImageAnalysis() : ImageAnalysis {
val cameraExecutor: ExecutorService = Executors.newSingleThreadExecutor()
val imageAnalysis = ImageAnalysis.Builder().build()
imageAnalysis.setAnalyzer(cameraExecutor, QRCodeAnalyzer(object : OnDetectListener {
override fun onDetect(msg: String) {
if(!isDetected) {
isDetected = true
val intent = Intent(this@MainActivity,
ResultActivity::class.java)
intent.putExtra("msg", msg)
startActivity(intent)
}
Toast.makeText(this@MainActivity, "${msg}", Toast.LENGTH_SHORT).show()
}
}))
return imageAnalysis
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
if (!hasPermissions(this)) {
requestPermissions(PERMISSIONS_REQUIRED, PERMISSIONS_REQUEST_CODE)
} else {
startCamera()
}
}
fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all {
ContextCompat.checkSelfPermission(context, it) ==
PackageManager.PERMISSION_GRANTED
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSIONS_REQUEST_CODE) {
if(PackageManager.PERMISSION_GRANTED == grantResults.firstOrNull()) {
Toast.makeText(this@MainActivity, "권한 요청이 승인되었습니다.",
Toast.LENGTH_LONG).show()
finish()
}
}
}
fun startCamera() {
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener(Runnable {
val cameraProvider = cameraProviderFuture.get()
val preview = getPreview()
val imageAnalysis = getImageAnalysis()
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
cameraProvider.bindToLifecycle(this, cameraSelector,preview,imageAnalysis)
}, ContextCompat.getMainExecutor(this))
}
fun getPreview(): Preview {
val preview : Preview = Preview.Builder().build()
preview.setSurfaceProvider(binding.barcodePreview.getSurfaceProvider())
return preview
}
}